Play Action to full length and blend to new action

Hi,

I can’t seem to figure out how to do the following simpler than I currently do:

  1. play animation JumpStart for its full length,
  2. blend to animation JumpLoop,
  3. and play animation JumpLoop continuously.

I have tried with:

AnimComposer animComposer = this.getSpatial().getControl(AnimComposer.class);

OnFinishedEventAction jumpStartSlow = new OnFinishedEventAction(animComposer.getAnimClip("JumpStart"));
jumpStartSlow.setSpeed(0.05f);
animComposer.addAction("JumpStartSlow", jumpStartSlow);

ClipAction jumpLoopSlow = new ClipAction(animComposer.getAnimClip("JumpLoop"));
jumpLoopSlow.setSpeed(0.8f);
animComposer.addAction("JumpLoopSlow", jumpLoopSlow);

animComposer.setCurrentAction("JumpStartSlow");

jumpStartSlow.subscribe((e) -> {
    animComposer.setCurrentAction("JumpLoopSlow");
});

Or using a BlendAction:

ClipAction jumpLoopSlow = new ClipAction(animComposer.getAnimClip("JumpLoop"));
jumpLoopSlow.setSpeed(0.8f);
animComposer.addAction("JumpLoopSlow", jumpLoopSlow);

OnFinishedEventAction onFinishJumpLoopAction = new OnFinishedEventAction(animComposer.getAnimClip("JumpLoop"));
BlendAction blendedStartToLoop = new BlendAction(
    new LinearBlendSpace(0, 1),
    new ClipAction(animComposer.getAnimClip("JumpStart")),
    onFinishJumpLoopAction
);
blendedStartToLoop.setSpeed(0.1f);
animComposer.addAction("jump-blended", blendedStartToLoop);
animComposer.setCurrentAction("jump-blended");

onFinishJumpLoopAction.subscribe((e) -> {
    animComposer.setCurrentAction("JumpLoopSlow");
});
OnFinishedEventAction
public class OnFinishedEventAction extends ClipAction {

    private List<ActionDoneEventListener> subscribers = new LinkedList<>();
    private boolean isInitialized = false;

    public OnFinishedEventAction(AnimClip clip) {
        super(clip);
    }

    public void subscribe (ActionDoneEventListener subscriber) {
        this.subscribers.add(subscriber);
    }

    @Override
    public boolean interpolate(double t) {
        boolean running = super.interpolate(t);

        if (!isInitialized && t < this.getLength()) {
            isInitialized = true;
        }

        if (isInitialized && !running) {
            subscribers.forEach(s -> s.onComplete(this));
            isInitialized = false;
        }
        return running;
    }

    @Override
    public String toString() {
        return "EventAction";
    }
}

Anyone ideas? :stuck_out_tongue:
Looking forward to your ideas.

2 Likes

Maybe try a “between action” that starts about where JumpStart is, and ends where JumpLoop begins? Doing that won’t work if JumpStart is spread over a large area with its movements, or if the camera is close in on your character (because the character’s animation might skip).

Just an idea.

1 Like

LinearBlendSpace creates something like 2 parallel linear sliders :

  • One Slider has the min------max values you specify
  • the other slider is the Action 0-----Action 1 values you insert via blendAction constructor
  • the bar of the 2nd slider is anchored to the first one, i mean the 1st slider ratios can tweak the current running animation to only use Action 0 or only use Action 1 or use both with a deltaFactor…

So, according to which current active index the animation would work,

According to those factors (the blendAction would choose which ClipAction to run) :

  1. firstActiveIndex
  2. secondActiveIndex
  3. deltaFactor or getWeight()

in which these factors is controlled by a BlendSpace

So, at LinearBlendSpace :

  • setting the setValue(maxValueOfBlendSpace) would give out :
    firstActiveIndex = 0, secondActiveIndex = 1 & getWeight() = 1
    & if(getWeight() >= 1) → only the secondActiveIndex would continue to run…but if getWeight() > 1 that would extrapolate between tracks (catastrophic)…

  • setting the setValue(minValueOfBlendSpace) would give out :
    firstActiveIndex = 0, secondActiveIndex = 1 & getWeight() = 0
    if(getWeight() == 0) → only the firstActiveIndex would continue to run…

  • setting the setValue(FastMath.interpolate(0.5f, minValueOfBlendSpace, maxValueOfBlendSpace) in between, will give :
    firstActiveIndex = 0, secondActiveIndex = 1 & getWeight() = value less than 1 not zero
    the 2 actions would run with a deltaFactor…

that is what i already know…So, you may probably need a BaseAppState that changes the value to max of the BlendSpace after the 1st run of the BlendAction…

Example :

        //9)feed the BlendableActions to a single BlendAction
        float minValueOfBlendSlider = 6;
        float maxValueOfBlendSlider = 12;
        LinearBlendSpace linearBlendSpace = new LinearBlendSpace(minValueOfBlendSlider, maxValueOfBlendSlider);
        //the 2 actions will run
        linearBlendSpace.setValue(FastMath.interpolateLinear(0.5f, 6, 12));

        blendAction = new BlendAction(linearBlendSpace, capRotationClip, bottleTractionClip);
        animComposer.addAction("SimulateBottleFall", blendAction);
        animComposer.makeLayer(LayerBuilder.LAYER_BLENDABLE_ANIM, new ArmatureMask());

EDIT :
i have just did that :

    @Override
    public void update(float tpf) {
        count += tpf;
        if(count > blendAction.getLength()){
            
//only the 2nd action would continue to run
linearBlendSpace.setValue(FastMath.interpolateLinear(1, 6, 12));
        }
    }

inside the appState that controls this animation & it works fine, at the first run the 2 animations are blended together using deltaFactor, then when the first run finishes, the 1st animation goes idle & only the second one runs…

1 Like

When you switch from one animation to another, it will already blend between them. So what is it not doing that you want it to do? That part isn’t clear to me.

Is the overlap just not big enough?

Edit: or is it that you are somehow trying to package all of this into one action, regardless of player input, etc.?

Edit2: and even still, I’m not sure why a plain sequence doesn’t do what you want.

2 Likes

Yep, and you can control the blend time with setTransitionLength method.

I won’t bother using a BlendAction action in this case.

2 Likes

Thanks for your answers.
As far as I know, a sequence repeats the whole sequence, whereas I only want to repeat the latter action. The situation is like this:
Dragon is flying with neck+head on idle animation. Player presses button, dragon does bite! (plays bite animation once). directly after the bite, dragon must go back to idle mode for neck and head part.

2 Likes