0

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.

4

1 回答 1

1

因此,该解决方案与通用补间引擎或其实施方式无关。这只是我对 LibGDX 中的矩阵变换函数的理解不足。调用setToRotation重置变换矩阵,然后只应用旋转,从而将先前的平移归零。setToTranslation做同样的事情,这导致了旋转的损失。

对于翻译,我setTranslation改用了,旋转不丢失。我认为没有可比的旋转功能(使用rotate会导致各种问题),所以我得到了一个新Vector3的,其中存储了模型的位置,然后 apply 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;
    }
}
于 2015-01-06T00:09:53.680 回答