asciibits

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!
asciibits
  • Сообщения: 11

Nate

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.
Аватара пользователя
Nate

Nate
  • Сообщения: 9845

asciibits

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.
asciibits
  • Сообщения: 11

Nate

Call setMixing for as many animations as you want.
Аватара пользователя
Nate

Nate
  • Сообщения: 9845

EvilEntity

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.
EvilEntity
  • Сообщения: 36

Søren

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.
Аватара пользователя
Søren

Shiu
  • Сообщения: 2396

EvilEntity

Thats good to know. Fixed that, thanks! Sadly it didnt help with mixing issues. Any suggestion much appreciated.
EvilEntity
  • Сообщения: 36

asciibits

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
asciibits
  • Сообщения: 11

Nate

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. :)
Аватара пользователя
Nate

Nate
  • Сообщения: 9845

EvilEntity

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.
EvilEntity
  • Сообщения: 36

Nate

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.
Аватара пользователя
Nate

Nate
  • Сообщения: 9845

EvilEntity

It sure as hell fixed mine problem, thanks!
EvilEntity
  • Сообщения: 36

asciibits

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.
asciibits
  • Сообщения: 11

Nate

I'm afraid I'll need an example that shows the issue.
Аватара пользователя
Nate

Nate
  • Сообщения: 9845

asciibits

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.
asciibits
  • Сообщения: 11

asciibits

Where can I send you an example project?
asciibits
  • Сообщения: 11

Nate

You can attach it to a post here or send to contact@esotericsoftware.com
Аватара пользователя
Nate

Nate
  • Сообщения: 9845

asciibits

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.
asciibits
  • Сообщения: 11

Søren

If you sent it to contact@esotericsoftware.com we didn't receive it, at least I don't see it in the inbox.
Аватара пользователя
Søren

Shiu
  • Сообщения: 2396

asciibits

OK, I have resent it now.
asciibits
  • Сообщения: 11

asciibits

Did you guys received the updated version?
asciibits
  • Сообщения: 11

Nate

Yes, I just tried it out. What am I looking for? It looks smooth to me. I clicked and watched the animation a few times.
Аватара пользователя
Nate

Nate
  • Сообщения: 9845

asciibits

a. After dropping the apple, the star rotation isn't 0 degrees, shouldn't he arrive to the position of the hanging animation?
b. at the second round, when switching the direction of the movement (from the left to right to the right to left), the apple is arriving from distance in a circle movement. The intended affect is the one you see at the first iteration, the apple should simply appear on the star.
asciibits
  • Сообщения: 11

Nate

It's hard to tell what's going on since the animations are so abstract. The animations don't change bones properties that are not keyed. If you don't key rotation for in_place, then switching from swirl_right to in_place will not change the rotation. I guess that is your problem.
Аватара пользователя
Nate

Nate
  • Сообщения: 9845

asciibits

Thank you for your time and effort, everything works perfectly.
asciibits
  • Сообщения: 11


Вернуться в Editor