• Editor
  • Mixing multiple animations

  • Изменено
Related Discussions
...

Hi,
I need help with mixing between multiple animations.
I couldn’t figure out how to switch the animations smoothly.
The location of the skeleton is always jumping when I switch between the animations.

I have a skeleton with four different animations. At any given time, one the animations is looping. When a certain event is being triggered, I need to switch the looping animations.
When animation is looping, I reset the animation time on every loop:

animRuntime += deltaTime;
if (state == STATE_A){
  if (animRuntime >stateAduration) {
                    animRuntime = 0;
                }
stateAanim.apply(skeleton, animRuntime, true);

My latest try to do the mixing is like this:

If (eventTrigered) {
					if (animRuntime > 0) {
                            stateAanim.apply(skeleton, animRuntime, true);
                            if (animRuntime + 0.5 >= stateAduration) {
                                stateBanim.mix(skeleton, animRuntime,true,
                                                             animRuntime / 0.4f);
                            }
						} else {
						state=STATE_B;
						}
					}

Many thanks!

You are passing true for loop to apply/mix, so you don't need any of the > duration logic. Try using AnimationState, it makes mixing a lot easier.

Thank you for the quick reply.

From what I understood, AnimationState can hold two states only, right?
if so, do I combine every two animations into one AnimationState?

I think an additional test, with more than two animations can be beneficial for others too.

Call setMixing for as many animations as you want.

Thats precisely the thing i have problem with. Im using animation state with multiple animations and it works. Sometimes. It pisses me off to no end. It works and it breaks for no reason. Also i need to reset root position every frame or it goes to 0.0. Im using AnimationStateTest as base. Here the relevant code:

public class Character extends GameObject{
	public static enum CharacterState {IDLE, ATTACKING, DEFENDING, DYING, DONE}
	protected Skeleton skeleton;
	protected Animation idleAnimation;
	protected Animation attackAnimation;
	protected Animation defendAnimation;
	protected Animation deathAnimation;
	protected AnimationState animationState;

public Character(Assets assets, Rectangle bounds, int level) {
	super(assets, bounds);
	this.level = level;
}

protected void updateBones(){
	final Bone root = skeleton.getRootBone();
	root.setX(bounds.x+64);
	root.setY(bounds.y);
	root.setScaleX(Assets.SCALE*0.5f);
	root.setScaleY(Assets.SCALE*0.5f);
	skeleton.updateWorldTransform();
}

protected void loadAnims(String name){
	SkeletonJson json = new SkeletonJson(assets.atlas);
	SkeletonData skeletonData = json.readSkeletonData(Gdx.files.internal("data/SD/anim/"+name+"-skeleton.json"));
	
	idleAnimation = json.readAnimation(Gdx.files.internal("data/SD/anim/"+name+"-idle.json"), skeletonData);
	attackAnimation = json.readAnimation(Gdx.files.internal("data/SD/anim/"+name+"-attack.json"), skeletonData);
	defendAnimation = json.readAnimation(Gdx.files.internal("data/SD/anim/"+name+"-hit.json"), skeletonData);
	deathAnimation = json.readAnimation(Gdx.files.internal("data/SD/anim/"+name+"-dead.json"), skeletonData);
	
	AnimationStateData mixing = new AnimationStateData();
	mixing.setMixing(idleAnimation, attackAnimation, 0.5f);
	mixing.setMixing(attackAnimation, idleAnimation, 0.5f);
	
	mixing.setMixing(defendAnimation, idleAnimation, 0.5f);
	mixing.setMixing(idleAnimation, defendAnimation, 0.5f);
	
	mixing.setMixing(defendAnimation, attackAnimation, 0.5f);
	mixing.setMixing(attackAnimation, defendAnimation, 0.5f);
	
	mixing.setMixing(deathAnimation, defendAnimation, 0.5f);
	mixing.setMixing(defendAnimation, deathAnimation, 0.5f);
	
	mixing.setMixing(deathAnimation, idleAnimation, 0.5f);
	mixing.setMixing(idleAnimation, deathAnimation, 0.5f);
	
	animationState = new AnimationState(mixing);
	animationState.setAnimation(idleAnimation, true);
	
	skeleton = new Skeleton(skeletonData);
	updateBones();
}

public void draw(SpriteBatch batch) {
	skeleton.getColor().set(1, 1, 1, 1);
	switch (state) {
	case IDLE:
		if (name.equals("zombie")){
			skeleton.getColor().set(0,1,0,1);
		}
		break;
	case ATTACKING:
		skeleton.getColor().set(Character.attackColor);
		break;
	case DEFENDING:
		skeleton.getColor().set(Character.deffendColor);
		break;
	case DYING:
		skeleton.getColor().set(Character.deadColor);
		break;
	case DONE:
		skeleton.getColor().set(Character.deaddColor);
		break;
	default:
		break;
	}
	updateBones();
	skeleton.draw(batch);		
}

public void update(float delta) {
	delta = delta/3;
	animationState.update(delta);

	switch (state) {
	case IDLE:
		if (animationState.getAnimation()!=idleAnimation)
			animationState.setAnimation(idleAnimation, true);
		break;
	case ATTACKING:
		if (animationState.getAnimation()!=attackAnimation)
			animationState.setAnimation(attackAnimation, true);
		
		if (animationState.getTime() >= attackAnimation.getDuration()){
			if (actedOn == null){
				state = CharacterState.IDLE;
			} else {
				state = CharacterState.DEFENDING;
				actedOn = null;
			}
		}
		break;
	case DEFENDING:
		if (animationState.getAnimation()!=defendAnimation)
			animationState.setAnimation(defendAnimation, true);
		
		if (animationState.getTime() >= defendAnimation.getDuration()){
			if (actedOn != null){
				attack(actedOn);
			} else {
				state = CharacterState.IDLE;
			}
			actedOn = null;
		}
		break;
	case DYING:
		if (animationState.getAnimation()!=deathAnimation)
			animationState.setAnimation(deathAnimation, true);

		if (animationState.getTime() >= deathAnimation.getDuration()){
			state = CharacterState.DONE;
		}
		break;
	}
	animationState.apply(skeleton);
}

Ive trimmed this a little. Suggestions are welcome.

Can't help you with the coding part, but the root going to 0.0 is most likely caused by having a key set on the root bone. That should be removed.

Thats good to know. Fixed that, thanks! Sadly it didnt help with mixing issues. Any suggestion much appreciated.

I'm experiencing similar issues as EvilEntity.
Perhaps using AnimationState works slightly better than before, but the results are still inconsistent. Sometimes transitions are smooth. Yet sometime the skeleton jumps to different position. and sometimes the second animation starts near the end of it's loop.

thanks

Sounds like there could be a bug in AnimationState. Will need to make a test case that reproduces it. Feel free to beat me to it. 🙂

Ive editted AnimationStateTest slightly to replicate the issue.

	
enum State{JUMPING, WALKING};
State characterState = State.WALKING;

public void render () {
	state.update(Gdx.graphics.getDeltaTime() / 3);
	Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
	batch.begin();
	state.apply(skeleton);

switch (characterState) {
case WALKING:
	if (state.getAnimation() != walkAnimation)
		state.setAnimation(walkAnimation, true);
	if (state.getTime() > 1)
		characterState = State.JUMPING;
	break;
case JUMPING:
	if (state.getAnimation() != jumpAnimation)
		state.setAnimation(jumpAnimation, false);
	if (state.getTime() > jumpAnimation.getDuration())
		characterState = State.WALKING;
	break;
default:
	break;
}

skeleton.updateWorldTransform();
skeleton.draw(batch);
batch.end();
}

While this makes sense in my head, I cant guaranty that it indeed does make sense. Perhaps I misunderstand how this is supposed to work.

Thanks. 🙂 I've updated AnimationStateTest to show the problem (or at least a problem, not sure if it is the same as you guys see) and I've committed a fix.

It sure as hell fixed mine problem, thanks!

The transitions are smoother now, but it’s still not perfect and still sometimes the second animation starts near the end of its loop.
I would have send you an example but unfortunately, university drank all my time and I really can’t work on it until next week.

I'm afraid I'll need an example that shows the issue.

Fully understood.
Next week I will retest if I can find error on my side and if I can't I'll create an example for you.
Again, thank you for your quick support. it is appreciated.

5 дней спустя

Where can I send you an example project?

You can attach it to a post here or send to contact@esotericsoftware.com

I sent you an example.
Not trying to rush you, I'm merely want to know that you received it.
Let me know if you haven't.

If you sent it to contact@esotericsoftware.com we didn't receive it, at least I don't see it in the inbox.