How can I detect time zero with a reversing animation?

When running in a ‘forward’ direction (speed is positive), I can use AnimComposer.actionSequence and Tweens.callMethod to detect when I get to the ‘end’ of the animation.

It works quite well. Then I can set speed = -1 and reverse the animation.

But how can I tell when my reversing animation reaches time zero?

Thanks for any help!

3 Likes

You can also put a call method at the beginning of the sequence to detect when it reached to the beginning.

But you should note this about how Sequence works:

So as suggested above you can do it like this instead:

Tweens.sequence(invert(action), callMethod("doSomething");

and set speed = 1.

Note that Invert tween is added recently and will be available in the next release so for now you can just copy-paste it from the master branch or else you can build the engine from the source.

2 Likes

Ah nice. Thanks. Still using the latest stable build :slight_smile:

2 Likes

Tweens.invert problem?
Or could just be me not doing it right?

Using 3.5.2-stable with main branch Tweens.java

I have a mixamo animation, gettingUp (from the floor)
I recorded 3 videos:

1-free-running-jMonkeyEngine 3.5.2-stable
Just letting the animation run.
Gets up off the floor then abruptly restarts.
As expected,

2-forward-jMonkeyEngine 3.5.2-stable
Runs the animation forward once.
Tween.callMethod sets speed to zero.
Gets up off the floor.
Freezes in an upright stance.
As expected.

3-reverse-jMonkeyEngine 3.5.2-stable
Runs the animation in reverse once.
Same code as (2) but uses Tweens.invert(action).
Sinks down to the floor as expected.
But at the end, leaps up and freezes.
Not as expected!

Code snippet here:

    public void playOnce(String animationName, boolean forward) {
        Action action = animComposer.action(animationName);
        if (forward) {
            animComposer.actionSequence("PlayOnce",
                    action,
                    Tweens.callMethod(this, "playedOnce"));
        } else {
            animComposer.actionSequence("PlayOnce",
                    Tweens.invert(action),
                    Tweens.callMethod(this, "playedOnce"));
        }
        animComposer.setCurrentAction("PlayOnce");
    }
    public void playedOnce() {
        animComposer.getCurrentAction().setSpeed(0);
    }

Videos here:

https://drive.google.com/drive/folders/1aWr-yZbJRFx7oNGJgPv364Wh9sq3etmN?usp=sharing

Instead of freezing with speed = 0, just remove current action using animComposer.removeCurrentAction(). It should solve the issue.

The reason the character leaps up in the 3rd video is that when the action ends, AnimLayer resets time to 0.

and when time is 0 then the Inverse tween will move to the end of the animation.

By the way, I have recently added a flag to enable/disable looping in AnimComposer. By default it is enabled, if you want to play animation just once you can set loop to false when setting the current action:

This feature is going to be included in the next engine release as well.

2 Likes

Yes removeCurrentAction() fixed it.
Thanks!
I love when I’m doing it wrong, they’re the easiest fixes.
:slight_smile:

2 Likes

You’re welcome! :slightly_smiling_face:

I know I’m getting a little off topic here, but before I delve further into Tweenland, are there any plans to reintroduce the ‘blend time’ parameter to animComposer.setCurrentAction?
In the old system we used to support:
channel_walk.setAnim(“Walk”,0.50f);
where 0.5f was the blend time between animations.
Lovely and simple :slight_smile:

1 Like

It is already supported in the new animation system. Whenever you switch animation it will do a smooth transition to the new animation. It is controlled by transitionLength, by default, it is 0.4 seconds but you can change it as you like.

((ClipAction) animComposer.action(clipName)).setTransitionLength();
1 Like

Can’t seem to make that do anything. Do I setTransitionLength on the currently running clip, or the one about to run? Before or after I set it? Hmm, think I’ve tried every combination…

        ((ClipAction) animComposer.action(animationName)).setTransitionLength(10d);
        animComposer.setCurrentAction(animationName);

Yes, the one about to run.

Before, but this should no be important.

Do you mean when switching animation, transition to the new animation is not smooth?

I see in your example you are setting it to 10 seconds. Note that the transition length must not be greater than the animation length otherwise it will be clamped to the anim length.

1 Like

Yes you are correct, clamping does clamp!

But I’ve just tried with 2 animations that are just over 6 seconds long, one starts upright the other on the floor. Toggling between them with a blend time of 4, 5, 6, and even 7 seconds results in a nice smooth transition. But always in somewhat less than 2 seconds. Is there another ‘absolute clamp’ somewhere?

if (actionString.equals("toggleBetween2Animations") && ongoing) {
	String ANIM1 = "DwarfIdle";
	String ANIM2 = "GettingUp";
	if (newAnimationUpdater.getCurrentActionNameFromDefaultLayer().contentEquals(ANIM1)){
		((ClipAction) animComposer.action(ANIM2)).setTransitionLength(6.0d);
		animComposer.setCurrentAction(ANIM2);
	} else {
		((ClipAction) animComposer.action(ANIM1)).setTransitionLength(6.0d);
		animComposer.setCurrentAction(ANIM1);
	}
}

10:35:47 DwarfIdle, length 6.333333492279053
10:35:47 GettingUp, length 8.166666984558105

Video is here:
https://drive.google.com/drive/folders/1kBRJSmh7pKfgIeBWqsrZHrQZfm_6hvBa?usp=sharing

No, I do not think so.

Well, I wonder it is because of how JME interpolates the animation blending but I am not sure.

Let me explain with an example, let’s say we want to interpolate between a “startValue” and “endValue” in 1 second.

If the “startValue” and “endValue” are static values that never change then the interpolation result will linearly change as expected.

Now suppose we increase the “startValue” every frame like this:

update (time) {
      double result = interpolate(time, startValue, endValue);
      startValue = result;
}

now the result change won’t be linear anymore.

That is exactly the case with JME animation blending as well. Think of “startValue” as the “startPose” and the end value as the “endPose”. The “startPose” is updated every frame to point to the current pose.

I hope this explanation helps.

Edit:

It happens in this line

tr is the bone current pose and t is the bone end pose that is read from animation data.

Well thanks for the analysis Ali!
It could well be that the blend is taking 6 seconds, but the changes get exponentially small so it looks like it’s done after 2 seconds. Maybe that was found to be a better effect? Certainly cant complain about the smoothness of the result :slight_smile:

I’m away from the computer for a few days but I’ll experiment when I get back. I need to learn about JME interpolation because I’m having big problems elsewhere.
Cheers,
Tom.

1 Like