Questions around Tween animation (Lemur/AnimComposer/MonkeyTrap)

Hi

Considering JME3.3 animation system,

Can a tween have a sense of pausing/stopping?

let me ask this in a better way,
In MonkeyTrap example animation system, we have the idea of interpolators and tasks, tasks can be paused/stopped or we can have unknown duration for continuous tasks. (for examples mobility animations like idle/walk)

seeing how stock tasks in MonkeyTrap have similarity to stock tweens we have in JME3.3

So how are the task and tween different???

Can we think of the task in MonkeyTrap example as a tween in the new animation system? Or tween is actually just an interpolator?

If tween is just an interpolator, then should the idea of tasks be added to the new animation system in JME 3.3 thus that the AnimComposer actually become a TaskComposer which runs tasks instead of raw tweens?

MonkeyTrap is kind of an ancient take on ideas that would evolve into something better that become Lemur’s animation system.

Remy based his JME animation refactoring on Lemur’s animation system but as I recall, it’s not a 1:1 mapping.

Lemur has the idea of Tweens and Animations. A Tween is something that will perform some kind of tweening between 0 and length. It can run forward/backward between 0 and length.

An Animation is responsible for making a Tween “go” based on timely updates. (public boolean animate( double tpf ):wink: So an Animation would be the thing that loops. Pausing an animation is just not updating it anymore. Continue to update it and it will continue to go where it left off.

I don’t remember how/if these concepts were translated exactly into JME’s animation system. But I do know that animations loop (and tweens don’t or at least they shouldn’t) and if you were to stop updating then things wouldn’t move anymore.

1 Like

Say you have a complicate chained tween which somewhere in the tween execution it attaches a spatial to rootNode and will remove it sometimes later in tween execution it can be at the end of tween for example, and suddenly the current tween is switched with another tween while it is running and we do not have a way to tell it to stop so the child tweens can do their cleanups when tween stoped. But using the Task mechanism you added in MonkeyTrap we can do such kind cleanup while pausing or stopping the task.

Can’t you just tween it all the way to length?

Switching animations is more complicated than what MonkeyTrap does, I think. Which is why I abandoned a lot of those approaches.

No, it’s all controlled from AI system in server. MobAnimationState in client listens to CharacterAction’s send from server. and there is possibility of an action to be failed or interrupted in AI thus it will reflected on the client an will stop or switch currently executing animation on the client.

There are other solutions like passing the cleanup stuff to be done in a control attached to spatial to clear it up when times come instead of doing it within tween itself.

I actually like the idea of Tasks you have introduced in MonkeyTrap and was thinking it could be complementary to be added into JME’s new animation system as well. :slightly_smiling_face:

Hmm…, I guess the problem is that the AnimComposer in the new animation system directly tries to make a Tween to “go” and does not use any means of external thing like ‘Animation’ or ‘Task’.

Both ‘Animation’ (in Lemur) and ‘Task’ (in MonkeyTrap) can wrap tween/interpolator and they expose a method for canceling, which could be overridden by the user to do whatever he wants when it is stopped.

Edit:
Actually, the new animation system uses an ‘Action’ class to wrap other tweens but ‘Action’ itself is a tween and could not be thought of like an Animation or Task I guess?

How would you handle a backward tween? I mean can ‘Animation’ (in Lemur animation system) have negative speed?

Back in MonkeyTrap you had an Invert interpolator

but seems in Lemur Tweens we don’t/can’t have such idea?

And one more thing

If there is an Animation which it’s looping set to false and is currently running then it will return false.

should the check be just

running && t >= 0;

I guess a reverse() tween was never added. It would be simple enough, though.

I’m not sure why loop is in there. Maybe I originally thought that a loop would always be running.

Then again, I wonder if there is some logic in looping where it’s still running but t could be negative. Maybe that’s the case I was trying to handle but left out an or somewhere.

1 Like

One thing that was making me confused was the following scenario:

new TweenAnimation(
new Invert(Tweens.sequence(Tweens.callMethod(this, "method1"), ac.makeAction("Walk"), Tweens.callMethod(this, "method2")))
);

I was expecting it to result:

execution of Method2 -> reversed walk animation -> execution of Method1

but the actual result is:
execution of Method1 -> execution of Method2 -> reversed walk animation -> execution of Method1

The issue for sequences is that they will “catch up” to whatever time they were given. So if you give them the end time then they will perform everything up until that.

Often that’s what you want because many times sequences can only be run in one direction.

You should instead make a backwards sequence and invert just the “Walk”, I guess.

1 Like

Note: this may also be why there is no built in inverse. A sequence like “add spatial, move spatial, remove spatial” makes no sense backwards.

It’s a similar problem to canceling tweens… because for a sequence() it’s unclear what cleaning up would do.

This is why in Lemur the idea of channels, canceling, reversing, etc… is at the Effect level where you potentially make entire new animations to do something backwards.

1 Like

Ah, I see it now. Thanks so so much for your explanation. :slightly_smiling_face:

Submitted this PR Just in case you think it is Ok :slightly_smiling_face:

But I wonder if it’s meant to be:
running && (loop || t >= 0)

Do you know if t can be temporarily negative in the case of looping? I’m not in a position to look at the code at the moment.

if( loop ) {
            if( !delegate.interpolate(t) ) {       
                t = t - delegate.getLength();
                if( t > 0 ) {
                    delegate.interpolate(t);
                } 
            }
        }

Hmm, my understanding is it can not be negative once it starts (after the very first frame is signaled). It may be ==0 or > 0 while looping I guess ? :thinking:

But yes t would be negative in case the animation hasn’t been executed before. (before the very first frame is signaled) :thinking: