[SOLVED] Animation transition not seamless as expected

When an animation is playing on a lower layer when an animation on a higher layer goes to loop or go to the next animation in sequence, the armature will instantly assume the current pose of the lower layer, then interpolate to the current pose of the higher layer. This makes the animation not seamless as I expected it should.

Here is my code:

// "main1" is lower than "main2".
// Both layers control all joints.
Tween shootCycle = Tweens.sequence(Tweens.loopDuration(1f, anim.action("aim-pistol")), anim.action("shoot-pistol"));
anim.addAction("shoot-cycle", new BaseAction(shootCycle));
anim.setCurrentAction("idle", "main1");
anim.setCurrentAction("shoot-cycle", "main2");

How should I address this issue?

Try to decrease maxTransitionWeight on idle animation and see if it will resolve the issue.

Another solution is to use one layer and after the shoot cycle is finished switch back to idle animation on the same layer.

1 Like
var idle = (ClipAction)anim.action("idle");
idle.setMaxTransitionWeight(.1f);

It helps make the problem less dramatic, but it is still visible.

That would work until I eventually add a looped/sequenced animation to another layer for masking, and experience the whole issue again.

I’m also using layers to make switching to the correct animation easier.

Perhaps you can create your custom AnimationMask and in contains() method add an extra check to see if the joint is also affected by an upper layer then return false.

You should modify

to something like

    @Override
    public boolean contains(Object target) {
        return affectedJoints.get(((Joint) target).getId()) && !isAffectedByUpperLayers(target);
    }

you need to implement the isAffectedByUpperLayers().

now a lower layer would see if the joint is also affected by any upper layer and ignores updating that joint.

1 Like

Here is my implementation. It does not work.

private boolean isAffectedByUpperLayers(Object target) {
    boolean active = false;
    for (String name : anim.getLayerNames()) {
        if (name.equals(layer)) {
            active = true;
            continue;
        }
        if (!active) {
            continue;
        }
        AnimLayer lyr = anim.getLayer(name);
        if (lyr.getMask().contains(target)) {
            return true;
        }
    }
    return false;
}

I think it’s broken because of this line.

if (lyr.getMask().contains(target)) {

contains does not check if the joint is currently in use, right? So basically all lower layers will find joints affected by higher layers, and never play anything on those joints. Is there a better way to check for joint use?

You can test with layer.getCurrentAction() != null; and only if there is a running action check that layer.

2 Likes

That should have been obvious… :stuck_out_tongue:

That fixed the issue, thanks!

2 Likes

Glad it is solved.