I'm working on a 3d game and trying to get character movement set up correctly. The interaction is simple, there's a single model on the screen, a user clicks a point on the screen, the model rotates to face the point clicked (right now the model is a little ghost with eyes, so the eyes turn to face the clicked point), and, once facing the point, the model will move there.
I had this working with some code I'd written myself, but the animations weren't smooth and the model would often overshoot/undershoot the destination and then be jerked backward or forward into place. I'd only worked with the Universal Tween engine on 2d animations before, but decided to give it a try. It solved the jerkiness problems, and rotate and transform both work fine on their own, but I'm unable to have them work together in a sequence.
If I only enable the code that rotates the model, it works fine (well, almost fine, something gets weird w/ determining clockwise or counter-clockwise rotation at certain angles, but that's another issue for later): I click a location, the model rotates to face it and retains the new rotation until the next click, then it turns to face that location. Same with translate only enabled: I click a spot, the model moves to it and retains it's new translation until I click again, then it moves to that location, etc.
When translate and rotate are both enabled, I run into issues. The model will first rotate to face the clicked point, then set back to it's initial rotation, then move to the clicked point. On subsequent clicks, the model will first go back to its initial translation, then rotate to face the clicked point (really the location of the clicked point relative to the re-initialized location), then move to the clicked point.
I've tried rearranging code, using callbacks, caching the new translation and rotation to members of the game model class and then using set()
with their values before beginning the tween, but nothing has worked.
Here is the relevant code from my 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;
...
}
}
}
And here is the code that starts the timeline and tweens (it's in 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);
Can anyone point me toward a solution?
EDIT:
It seems that the transforms being set by the completion of each tween are reset to 0 after another tween completes. These are logs of those values from the getValues
and setValues
functions in the TweenAccessor
:
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
is being called twice for each type of tween (translation and rotation) before the tweening begins. At this point, the values for translation are still what they had been set to at the end of the last translation tween. The value for rotation has been set back to 0 though (I'm guessing as a result of the next bunch of calls to setValues
for rotation).
setValues
is then called several times for rotation, until it reaches the target rotation. This is (aside from resetting rotation ) working as I'd expect and hope. After the getRotation
calls though, the translation is set back to 0,0,0, but not by a call to setValues
(there's no log of it). The translation is then animated to the target with several setValues
calls.
After this the cycle restarts on another click and getValues
is called twice again for rotation and translation. Translation has the values that were set at the last call to setValues
in the last Timeline
, but only until the rotation (which is back at 0) runs through its tween.
The 4 calls to getValues
could be something. I'm not sure what in my setup would cause Timeline
would grab the values of the translation before its tween begins, if they're supposed to be run sequentially as they're pushed in the Timeline
. I'm also not sure why getValues
is called twice for each tween, but it's not infeasible that the Tween Engine would call it twice in the course of a tween.
I'm pretty sure the translation/rotation functions from LibGDX that I'm using are fine, they 'stick' to the models and I used the same for translation as I did with my non-Tween-Engine version. Rotation is similar, but not the same function.
The changing Y values during the second translation are something to look into though.