Universal Tween Engine / LibGDX:模型在序列之间丢失变换和旋转
Universal Tween Engine / LibGDX: models losing transform and rotation between tweens in sequence
我正在开发 3d 游戏并尝试正确设置角色移动。交互很简单,屏幕上只有一个模型,用户点击屏幕上的一个点,模型旋转面对点击的点(现在模型是一个有眼睛的小幽灵,所以眼睛转向点击的点点),一旦面向该点,模型就会移动到该点。
我用自己编写的一些代码进行了此操作,但动画并不流畅,模型经常 overshoot/undershoot 到达目的地,然后向后或向前猛拉到位。之前我只在 2d 动画中使用过 Universal Tween 引擎,但决定试一试。它解决了抖动问题,旋转和变换都可以单独工作,但我无法让它们按顺序一起工作。
如果我只启用旋转模型的代码,它就可以正常工作(好吧,几乎可以,有些东西会变得很奇怪 w/ 在某些角度确定顺时针或逆时针旋转,但这是以后的另一个问题):我单击一个位置,模型旋转以面对它并保持新的旋转直到下一次单击,然后它转向面对该位置。与仅启用翻译相同:我单击一个点,模型移动到它并保留它的新翻译,直到我再次单击,然后它移动到该位置,等等。
同时启用平移和旋转时,我遇到了问题。模型将首先旋转以面向单击点,然后设置回其初始旋转,然后移动到单击点。在随后的点击中,模型将首先回到其初始平移,然后旋转以面对点击点(实际上是点击点相对于重新初始化位置的位置),然后移动到点击点。
我试过重新排列代码,使用回调,将新的平移和旋转缓存到游戏模型的成员 class,然后在开始补间之前使用 set()
和它们的值,但没有任何效果成功了。
这是我的 TweenAccessor
的相关代码:
public class GameModelTweenAccessor implements TweenAccessor<DynamicModel> {
public int getValues(DynamicModel target, int tweenType, float[] returnValues) {
trans = target.model.transform.getTranslation(trans);
switch (tweenType) {
...
case POSITION_XYZ:
returnValues[0] = trans.x;
returnValues[1] = trans.y;
returnValues[2] = trans.z;
return 3;
case ROTATION:
axisVec = new Vector3();
angle = target.model.transform.getRotation(new Quaternion()).getAxisAngle(axisVec) * axisVec.nor().y;
returnValues[0] = angle;
return 1;
...
}
}
public void setValues(DynamicModel target, int tweenType, float[] newValues) {
trans = target.model.transform.getTranslation(trans);
switch (tweenType) {
...
case POSITION_XYZ:
target.model.transform.setToTranslation(newValues[0], newValues[1], newValues[2]);
break;
case ROTATION:
target.model.transform.setToRotation(Vector3.Y, newValues[0]);
break;
...
}
}
}
这是启动时间线和补间的代码(在 this file 中):
Timeline.createSequence()
.push(Tween.to(screen.ghost, GameModelTweenAccessor.ROTATION, Math.abs(angle - newRotation) / 200)
.target(newRotation)
.ease(TweenEquations.easeNone))
.push(Tween.to(screen.ghost, GameModelTweenAccessor.POSITION_XYZ, duration).
target(intersection.x, intersection.y, intersection.z)
.ease(TweenEquations.easeNone))
.start(screen.ghostManager);
任何人都可以指出我的解决方案吗?
编辑:
似乎每个补间完成后设置的转换在另一个补间完成后重置为 0。这些是 TweenAccessor
:
中 getValues
和 setValues
函数的那些值的日志
Get Rot: 0.0
Get Trans: x: 0.0 y: 0.0 z: 0.0
Get Rot: 0.0
Get Trans: x: 0.0 y: 0.0 z: 0.0
Get Rot: 0.0
Set Rot: 9.598349
Set Rot: 9.814415
Set Rot: 10.052449
...
Set Rot: 39.99417
Set Rot: 43.397423
Set Rot: 46.62333
Get Trans: x: 0.0 y: 0.0 z: 0.0
Set Trans: x: 0.0012489144 y: 0.0 z: 0.001180494
Set Trans: x: 0.024489587 y: 0.0 z: 0.023147952
Set Trans: x: 0.04921494 y: 0.0 z: 0.04651875
...
Set Trans: x: 6.4197707 y: 0.0 z: 6.06807
Set Trans: x: 6.444479 y: 0.0 z: 6.091425
Set Trans: x: 6.453598 y: 0.0 z: 6.1000443
Get Rot: 0.0
Get Trans: x: 6.453598 y: 0.0 z: 6.1000443
Get Rot: 0.0
Get Trans: x: 6.453598 y: 0.0 z: 6.1000443
Get Rot: 0.0
Set Rot: 3.4318955
Set Rot: 6.795984
Set Rot: 10.0074415
...
Set Rot: 156.79567
Set Rot: 159.99591
Set Rot: 162.38742
Get Trans: x: 0.0 y: 0.0 z: 0.0
Set Trans: x: 0.03550978 y: 3.836017E-8 z: 0.021066409
Set Trans: x: 0.15527377 y: 1.6773768E-7 z: 0.092117175
...
Set Trans: x: 6.848614 y: 7.3983565E-6 z: 4.062985
Set Trans: x: 6.961268 y: 7.5200533E-6 z: 4.129818
Set Trans: x: 7.0624847 y: 7.6293945E-6 z: 4.189865
Get Rot: 0.0
Get Trans: x: 7.0624847 y: 7.6293945E-6 z: 4.189865
Get Rot: 0.0
Get Trans: x: 7.0624847 y: 7.6293945E-6 z: 4.189865
Get Rot: 0.0
Set Rot: -3.2620814
Set Rot: -6.8205137
Set Rot: -9.834579
...
Set Rot: -76.57533
Set Rot: -79.91388
Set Rot: -80.610855
Get Trans: x: 0.0 y: 0.0 z: 0.0
Set Trans: x: 0.01940876 y: 0.0 z: 0.033669088
Set Trans: x: 0.04174851 y: 0.0 z: 0.07242267
Set Trans: x: 0.06332677 y: 0.0 z: 0.109855264
...
Set Trans: x: 2.7853239 y: 0.0 z: 4.8318033
Set Trans: x: 2.808029 y: 0.0 z: 4.8711905
Set Trans: x: 2.827034 y: 0.0 z: 4.9041595
getValues
在补间开始之前,每种类型的补间(平移和旋转)被调用两次。此时,翻译的值仍然是上次翻译补间结束时设置的值。不过,旋转的值已设置回 0(我猜测是下一堆调用 setValues
进行旋转的结果)。
然后多次调用setValues
进行轮换,直到达到目标轮换。这是(除了重置旋转之外)如我所料和希望的那样工作。尽管在 getRotation
调用之后,翻译设置回 0,0,0,但不是通过调用 setValues
(没有它的日志)。然后通过几次 setValues
调用将翻译动画化到目标。
在此之后,循环会在再次单击时重新开始,并且 getValues
会再次调用两次以进行旋转和平移。翻译具有在最后一次 Timeline
中最后一次调用 setValues
时设置的值,但仅在旋转(返回 0)通过其补间之前设置。
对 getValues
的 4 次调用可能有点问题。我不确定我的设置中的什么会导致 Timeline
会在其补间开始之前获取翻译的值,如果它们应该在被推入 Timeline
时按顺序运行的话。我也不确定为什么每个补间调用两次 getValues
,但补间引擎在补间过程中调用它两次并非不可行。
我很确定我使用的 LibGDX 中的 translation/rotation 函数很好,它们 'stick' 用于模型,我也使用相同的方法进行翻译使用我的非 Tween-Engine 版本。旋转类似,但功能不一样。
不过,第二次翻译期间 Y 值的变化值得研究。
因此该解决方案与通用补间引擎或其实现方式无关。只是我对LibGDX中的矩阵变换函数的理解很差。对 setToRotation
的调用会重置变换矩阵,然后仅应用旋转,从而将先前的平移归零。 setToTranslation
同理,造成旋转丢失
为了翻译,我用了setTranslation
代替,旋转也没有丢失。我认为没有可比的旋转功能(使用 rotate
会导致各种问题),所以我得到一个新的 Vector3
并在其中存储模型的位置,然后应用 setToRotation
,然后将翻译设置为新向量。这是来自 TweenAccessor
:
的更新代码
public void setValues(DynamicModel target, int tweenType, float[] newValues) {
trans = target.model.transform.getTranslation(trans);
switch (tweenType) {
...
case POSITION_XYZ:
target.model.transform.setTranslation(newValues[0], newValues[1], newValues[2]);
break;
case ROTATION:
// store the position
Vector3 position = target.model.transform.getTranslation(new Vector3());
// then set the rotation and reset the translation
target.model.transform.setToRotation(Vector3.Y, newValues[0]).setTranslation(position);
break;
default:
assert false;
break;
}
}
我正在开发 3d 游戏并尝试正确设置角色移动。交互很简单,屏幕上只有一个模型,用户点击屏幕上的一个点,模型旋转面对点击的点(现在模型是一个有眼睛的小幽灵,所以眼睛转向点击的点点),一旦面向该点,模型就会移动到该点。
我用自己编写的一些代码进行了此操作,但动画并不流畅,模型经常 overshoot/undershoot 到达目的地,然后向后或向前猛拉到位。之前我只在 2d 动画中使用过 Universal Tween 引擎,但决定试一试。它解决了抖动问题,旋转和变换都可以单独工作,但我无法让它们按顺序一起工作。
如果我只启用旋转模型的代码,它就可以正常工作(好吧,几乎可以,有些东西会变得很奇怪 w/ 在某些角度确定顺时针或逆时针旋转,但这是以后的另一个问题):我单击一个位置,模型旋转以面对它并保持新的旋转直到下一次单击,然后它转向面对该位置。与仅启用翻译相同:我单击一个点,模型移动到它并保留它的新翻译,直到我再次单击,然后它移动到该位置,等等。
同时启用平移和旋转时,我遇到了问题。模型将首先旋转以面向单击点,然后设置回其初始旋转,然后移动到单击点。在随后的点击中,模型将首先回到其初始平移,然后旋转以面对点击点(实际上是点击点相对于重新初始化位置的位置),然后移动到点击点。
我试过重新排列代码,使用回调,将新的平移和旋转缓存到游戏模型的成员 class,然后在开始补间之前使用 set()
和它们的值,但没有任何效果成功了。
这是我的 TweenAccessor
的相关代码:
public class GameModelTweenAccessor implements TweenAccessor<DynamicModel> {
public int getValues(DynamicModel target, int tweenType, float[] returnValues) {
trans = target.model.transform.getTranslation(trans);
switch (tweenType) {
...
case POSITION_XYZ:
returnValues[0] = trans.x;
returnValues[1] = trans.y;
returnValues[2] = trans.z;
return 3;
case ROTATION:
axisVec = new Vector3();
angle = target.model.transform.getRotation(new Quaternion()).getAxisAngle(axisVec) * axisVec.nor().y;
returnValues[0] = angle;
return 1;
...
}
}
public void setValues(DynamicModel target, int tweenType, float[] newValues) {
trans = target.model.transform.getTranslation(trans);
switch (tweenType) {
...
case POSITION_XYZ:
target.model.transform.setToTranslation(newValues[0], newValues[1], newValues[2]);
break;
case ROTATION:
target.model.transform.setToRotation(Vector3.Y, newValues[0]);
break;
...
}
}
}
这是启动时间线和补间的代码(在 this file 中):
Timeline.createSequence()
.push(Tween.to(screen.ghost, GameModelTweenAccessor.ROTATION, Math.abs(angle - newRotation) / 200)
.target(newRotation)
.ease(TweenEquations.easeNone))
.push(Tween.to(screen.ghost, GameModelTweenAccessor.POSITION_XYZ, duration).
target(intersection.x, intersection.y, intersection.z)
.ease(TweenEquations.easeNone))
.start(screen.ghostManager);
任何人都可以指出我的解决方案吗?
编辑:
似乎每个补间完成后设置的转换在另一个补间完成后重置为 0。这些是 TweenAccessor
:
getValues
和 setValues
函数的那些值的日志
Get Rot: 0.0
Get Trans: x: 0.0 y: 0.0 z: 0.0
Get Rot: 0.0
Get Trans: x: 0.0 y: 0.0 z: 0.0
Get Rot: 0.0
Set Rot: 9.598349
Set Rot: 9.814415
Set Rot: 10.052449
...
Set Rot: 39.99417
Set Rot: 43.397423
Set Rot: 46.62333
Get Trans: x: 0.0 y: 0.0 z: 0.0
Set Trans: x: 0.0012489144 y: 0.0 z: 0.001180494
Set Trans: x: 0.024489587 y: 0.0 z: 0.023147952
Set Trans: x: 0.04921494 y: 0.0 z: 0.04651875
...
Set Trans: x: 6.4197707 y: 0.0 z: 6.06807
Set Trans: x: 6.444479 y: 0.0 z: 6.091425
Set Trans: x: 6.453598 y: 0.0 z: 6.1000443
Get Rot: 0.0
Get Trans: x: 6.453598 y: 0.0 z: 6.1000443
Get Rot: 0.0
Get Trans: x: 6.453598 y: 0.0 z: 6.1000443
Get Rot: 0.0
Set Rot: 3.4318955
Set Rot: 6.795984
Set Rot: 10.0074415
...
Set Rot: 156.79567
Set Rot: 159.99591
Set Rot: 162.38742
Get Trans: x: 0.0 y: 0.0 z: 0.0
Set Trans: x: 0.03550978 y: 3.836017E-8 z: 0.021066409
Set Trans: x: 0.15527377 y: 1.6773768E-7 z: 0.092117175
...
Set Trans: x: 6.848614 y: 7.3983565E-6 z: 4.062985
Set Trans: x: 6.961268 y: 7.5200533E-6 z: 4.129818
Set Trans: x: 7.0624847 y: 7.6293945E-6 z: 4.189865
Get Rot: 0.0
Get Trans: x: 7.0624847 y: 7.6293945E-6 z: 4.189865
Get Rot: 0.0
Get Trans: x: 7.0624847 y: 7.6293945E-6 z: 4.189865
Get Rot: 0.0
Set Rot: -3.2620814
Set Rot: -6.8205137
Set Rot: -9.834579
...
Set Rot: -76.57533
Set Rot: -79.91388
Set Rot: -80.610855
Get Trans: x: 0.0 y: 0.0 z: 0.0
Set Trans: x: 0.01940876 y: 0.0 z: 0.033669088
Set Trans: x: 0.04174851 y: 0.0 z: 0.07242267
Set Trans: x: 0.06332677 y: 0.0 z: 0.109855264
...
Set Trans: x: 2.7853239 y: 0.0 z: 4.8318033
Set Trans: x: 2.808029 y: 0.0 z: 4.8711905
Set Trans: x: 2.827034 y: 0.0 z: 4.9041595
getValues
在补间开始之前,每种类型的补间(平移和旋转)被调用两次。此时,翻译的值仍然是上次翻译补间结束时设置的值。不过,旋转的值已设置回 0(我猜测是下一堆调用 setValues
进行旋转的结果)。
setValues
进行轮换,直到达到目标轮换。这是(除了重置旋转之外)如我所料和希望的那样工作。尽管在 getRotation
调用之后,翻译设置回 0,0,0,但不是通过调用 setValues
(没有它的日志)。然后通过几次 setValues
调用将翻译动画化到目标。
在此之后,循环会在再次单击时重新开始,并且 getValues
会再次调用两次以进行旋转和平移。翻译具有在最后一次 Timeline
中最后一次调用 setValues
时设置的值,但仅在旋转(返回 0)通过其补间之前设置。
对 getValues
的 4 次调用可能有点问题。我不确定我的设置中的什么会导致 Timeline
会在其补间开始之前获取翻译的值,如果它们应该在被推入 Timeline
时按顺序运行的话。我也不确定为什么每个补间调用两次 getValues
,但补间引擎在补间过程中调用它两次并非不可行。
我很确定我使用的 LibGDX 中的 translation/rotation 函数很好,它们 'stick' 用于模型,我也使用相同的方法进行翻译使用我的非 Tween-Engine 版本。旋转类似,但功能不一样。
不过,第二次翻译期间 Y 值的变化值得研究。
因此该解决方案与通用补间引擎或其实现方式无关。只是我对LibGDX中的矩阵变换函数的理解很差。对 setToRotation
的调用会重置变换矩阵,然后仅应用旋转,从而将先前的平移归零。 setToTranslation
同理,造成旋转丢失
为了翻译,我用了setTranslation
代替,旋转也没有丢失。我认为没有可比的旋转功能(使用 rotate
会导致各种问题),所以我得到一个新的 Vector3
并在其中存储模型的位置,然后应用 setToRotation
,然后将翻译设置为新向量。这是来自 TweenAccessor
:
public void setValues(DynamicModel target, int tweenType, float[] newValues) {
trans = target.model.transform.getTranslation(trans);
switch (tweenType) {
...
case POSITION_XYZ:
target.model.transform.setTranslation(newValues[0], newValues[1], newValues[2]);
break;
case ROTATION:
// store the position
Vector3 position = target.model.transform.getTranslation(new Vector3());
// then set the rotation and reset the translation
target.model.transform.setToRotation(Vector3.Y, newValues[0]).setTranslation(position);
break;
default:
assert false;
break;
}
}