Tweens.smoothStep not always smooth (Oto.mesh.xml)

Hi, I’m currently looking into the new animation system (hoping to update the tutorial, which is still based on the old) I came across the problem of getting a smooth transition from the “Walk” action into the “stand” action in Oto.mesh.xml. I tried to solve this in the old animation system (blend time parameter of setAnim, but i might misinterpret it as “blend” means something different for Tweens) but ran into a similar problem.

MVE:

public class WalkStand extends SimpleApplication {
    public static void main(String[] args) { new WalkStand().start(); }
    @Override public void simpleInitApp() {
        viewPort.setBackgroundColor(ColorRGBA.LightGray);
        DirectionalLight dl = new DirectionalLight();
        dl.setDirection(new Vector3f(-0.1f, -1f, -1).normalizeLocal());
        rootNode.addLight(dl);
        Node player = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
        player.setLocalScale(0.5f);
        rootNode.attachChild(player);
        AnimComposer composer = player.getControl(AnimComposer.class);

        composer.actionSequence("WalkSequence",
                Tweens.smoothStep(
                        composer.action("Walk"),
                        composer.action("Dodge"), // smooth
                        composer.action("Walk"), // smooth
                        composer.action("stand")) // not smooth
        );
        composer.setCurrentAction("WalkSequence");
    }
}

After paging through the sources of Tweens et.al. I got the impression it might be caused by the fact that the “stand” action has a length of zero?

smoothStep() just adjusts the time curve from being linear to starting slow and ending slow… like smooth step in shaders.

image

Ah, I see. So basically I need to design my animations in a way that they seamlessly match together? Because when I just tried actionSequence

        composer.actionSequence("WalkSequence",
                        composer.action("Walk"),
                        composer.action("Dodge"), // smooth
                        composer.action("Walk"), // smooth
                        composer.action("stand") // not smooth

the results were just as unsatisfactory :confused:

if it’s length is zero then most probably that’s the case. By default, the transition length is 0.4 seconds. (you can modify it if you want)

This means when an action finishes it will smoothly transit to the next action in 0.4 seconds.

But if the length of action is less than the transition length it will override the transition length. In your case it will become 0 so you will observe a sudden change in animation.

1 Like

I don’t have the code/docs in front of me but I think there is a way to stretch a tween or something. At least I’d hope there is a way to have a particular animation take longer.

…in which case you could wrap the ‘stand’ action in something to make it last 0.4 seconds.

Yeah, this will probably do the job, and hopefully fix OPs problem with smooth transition.

Logically, yes… but I think actually, no:

If delegate.getLength() is 0 then no scaling will happen.

I haven’t dug very deeply into the action/tween stuff as Nehon interpreted it… but I feel like things get stuck sometimes between defaulting to looping and being able to be sequenced.

For example, one approach would have been to let all actions run to completion. If you want them to loop then you wrap them in a loop() tween with some length. Given how folks sometimes want their animations to not loop and then struggle with that, I’m guessing that’s not the way things were implemented.

…else it would be easy to say “loop this instant action for 0.4 seconds”.

And actually maybe that’s lacking even now. If I want a sequence of “walk for 5 seconds then run for 10 seconds” are there tweens to make that happen?

Yes, this is where I ended up “paging through the sources of Tweens et.al.”

I also did not find anything that would allow to run an animation for a specified amount of time in the anim packages. Probably cinematic would be the way to go?

No, we should be able to do it without bringing the cinematic garbage into this. The new animation package makes cinematics largely unnecessary for anything but cross-object animation.

It should be pretty simple to make a loop() tween. In theory. Just modulo time by delegate.length before passing it to delegate.

Edit: but also note that on some level it’s weird to me that if I run that “stand” animation then he stands there forever… and then if I run “walk” that it wouldn’t blend from stand? That seems wrong.

1 Like

I guess I’ll have a go at that then. Probably taking Tweens.Stretch as a template.

Yes, but that wont be a problem for smooth transition I guess. (which is what OP wants).

Because in the below line getLength() will return the desired lenght anyway and not the 0.

Ahah.

Still no way to loop an animation for some time in a sequence. And that seems useful to me.

Edit: or do tweens normally loop if given times past their end?

Nope, that is not possible with default tweens, currently one needs to create custom loop tweens for this.

So from what I have gather after only half successfully implementing Tweens.loop(duration, delegates) is that AnimComposer does the actual looping by resetting layer.time=0 when Tween.interpolate returns false:

    @Override
    protected void controlUpdate(float tpf) {
        for (Layer layer : layers.values()) {
            Action currentAction = layer.currentAction;
            if (currentAction == null) {
                continue;
            }
            layer.advance(tpf);

            currentAction.setMask(layer.mask);
            boolean running = currentAction.interpolate(layer.time);
            currentAction.setMask(null);

            if (!running) {
                layer.time = 0;
            }
        }
    }

AnimComposer does the looping for the whole action. But if your action was a sequence of tweens “walk 4 seconds”, “wave 2 seconds” then you’d want to loop walk and wave to get them up to the time necessary in the sequence.

This is a contrived example but there are going to be times where you are running things in parallel and you’d like to have one of the items loop instead of just run and stop, etc…

Action walk = composer.action("Walk");
Action walk10s = new BaseAction(Tweens.loop(10, walk));
composer.actionSequence("walk 10s, then stand", walk10s, Tweens.callMethod(composer, "setCurrentAction", "stand"));
composer.setCurrentAction("walk 10s, then stand");

kind of works, but still does not blend between walk and stand. But adding loop would actually have some value then …
Edit: I guess a better name would be duration or so … looks as if it loops 10 times and not 10 time units …

Or loopDuration(), loopCount()… both seem like they could be useful.

1 Like

I’ll see what I can come up with tomorrow. Currently wrapping my head around the fact that there seems to be no way to go from Tween to BlendAction (so that I can use something along the lines of

var blendspace = ...
var walk = (BlendableAction) composer.action("walk"); // ClipAction implements BlendableAction
var stand = Tweens.stretch(1, composer.action("stand"); // not BlendableAction :/
new BlendAction(blendspace, walk, stand); ...

But maybe I’m just not seeing something …

I thought we started all of this with actionSequence().

I’m not familiar with this part of the API but wouldn’t it be something like actionSequence(“walkThenStand”, action(“walk”), actionSequence(“stand”, stand)))

…where stand is the stretched tween.