• Runtimes
  • chaining animations together correctly

Hi All,
Apologies for the vague subject line I'm having problems with getting animations to work correctly for me.

I have managed to get my jump and double jump working correctly. I am now trying to get my character running and then rolling and then back to running. The problem is I want to keep left or right pressed the whole time and then the animation to go into the roll then back to run (when I let go of the roll button) I'm just getting confused and can't see the forest for the tree.

i am using libgdx statemachine with ashley, i have a inputsystem for the movement and all the movement code is in the player class. i think im just getting confused with how to get it all working together correctly.

i have given some sample code below

PlayerState Enum

run(){
        @Override
        public void update(PlayerAgent entity) {
            if (entity.stateComp.stateChanged) {
                entity.stateComp.stateChanged = false;
                Gdx.app.log("PlayerState:", "Run State");
                //entity.spineComp.animationState.setAnimation(0, "Run", true);
                entity.setAnimation(entity.spineComp.playerStates.get(EntityStateComponent.EntityMoveStates.run), true);
            }
            if (entity.bodyComp.body.getLinearVelocity().x == 0 && entity.bodyComp.body.getLinearVelocity().y == 0) {
                entity.setState(idle);
            }
        }

},
rollstart(){
        @Override
        public void update(PlayerAgent entity) {
            if (entity.stateComp.stateChanged) {
                entity.stateComp.stateChanged = false;
                Gdx.app.log("PlayerState:", "Rollstart State");
                entity.setAnimation(entity.spineComp.playerStates.get(EntityStateComponent.EntityMoveStates.rollstart), true);

            }
    }
},
rollend(){
        @Override
        public void update(PlayerAgent entity) {
            if (entity.stateComp.stateChanged) {
                entity.stateComp.stateChanged = false;
                Gdx.app.log("PlayerState:", "Rollend State");
                entity.setAnimation(entity.spineComp.playerStates.get(EntityStateComponent.EntityMoveStates.rollend), true);
            }
        }
...
...

InputSystem


 protected void processEntity(Entity entity, float deltaTime) {
if (typeComp.type == EntityTypeComponent.EntityType.PLAYERone) {
            updateInput(deltaTime);
        }
}

   void updateInput (float delta) {

    if (pdataComp.health == 0) return;

    if (rollPressed){
        //do nothing for now
    }else if (leftPressed) {
        directionComp.direction = -1;
        EntityManager.getPlayerAgent().moveLeft();
    } else if (rightPressed) {
        directionComp.direction = 1;
        EntityManager.getPlayerAgent().moveRight();
    }
    if (downPressed) {
        EntityManager.getPlayerAgent().moveDown();
    }
    if (upPressed){
        //EntityManager.getPlayerAgent().jump();
    }
    
    if (touched) {
        //EntityManager.getPlayerAgent().shoot();
    }else if (touched = false){
        //EntityManager.getPlayerAgent().spine2d.animationState.setAnimation(1, player.view.shootAnimationFinish, false);
    }
}

....
....
//code from KeyUp

....
@Override
    public boolean keyDown(int keycode) {
        switch(keycode){
            case Input.Keys.ESCAPE:
                //game.setScreen(new MenuScreen(game));
                Gdx.app.exit();
                break;
            case Input.Keys.W:
            case Input.Keys.SPACE:
            case Input.Keys.UP:
                if (pdataComp.health == 0) return false;
                upPressed = true;
                if(!EntityManager.getPlayerAgent().isTouchingGround){
                    EntityManager.getPlayerAgent().doublejump();
                }
                if (EntityManager.getPlayerAgent().isTouchingGround){
                    EntityManager.getPlayerAgent().jump();
                }
                break;
           case Input.Keys.R:
                if(EntityManager.getPlayerAgent().stateMachine.getCurrentState() == PlayerState.run){
                    EntityManager.getPlayerAgent().roll();
                }
                break;
....
....

public boolean keyUp (int keycode) {
        switch(keycode){
            case Input.Keys.W:
            case Input.Keys.SPACE:
            case Input.Keys.UP:
                if (pdataComp.health == 0) return false;
                if (EntityManager.getPlayerAgent().stateMachine.getCurrentState() == PlayerState.spinjumpstart) EntityManager.getPlayerAgent().setState(PlayerState.spinjumpend);
                if(bodyComp.body.getLinearVelocity().y > 0) bodyComp.body.setLinearVelocity(bodyComp.body.getLinearVelocity().x ,bodyComp.body.getLinearVelocity().y * 0.01f);
                upPressed = false;
                break;
           case Input.Keys.R:
                if (EntityManager.getPlayerAgent().stateMachine.getCurrentState() == PlayerState.rollstart) EntityManager.getPlayerAgent().setState(PlayerState.rollend);
                break;
....
....

playeragent movement code

...

public void jump() {
            bodyComp.body.setLinearVelocity(bodyComp.body.getLinearVelocity().x, minMaxVelocityComp.maxVelocity.y /1.0f);
            setState(PlayerState.jumpstart);
            setState(PlayerState.jumpmidair);
            isTouchingGround = false;
    }

public void doublejump(){
    if (jumpComp.jumpCount == 1){
        bodyComp.body.setLinearVelocity(bodyComp.body.getLinearVelocity().x, minMaxVelocityComp.maxVelocity.y /1.0f);
        setState(PlayerState.spinjumpstart);
        isTouchingGround = false;
    }
}

public void roll() {
        if (isGrounded()) {
            if (stateMachine.getCurrentState() == PlayerState.run) {
                if (directionComp.direction == 1) {
                    bodyComp.body.setLinearVelocity(10.0f, bodyComp.body.getLinearVelocity().y);
                }
                if (directionComp.direction == -1) {
                    bodyComp.body.setLinearVelocity(-10.0f, bodyComp.body.getLinearVelocity().y);
                }
                setState(PlayerState.rollstart);
            }
        }
    }

i used a lot of code from the spineboy example to get the animation working...
i hope the example code helps explain what im doing. let me know if you need me to make anything clearer...

Regards
Amarino

Related Discussions
...

Are you able to play your animations sequentially in Preview and they look like you want? If so, then your only problem is the state machine that results in the same at runtime?

Your code is not easy to follow, and of course isn't executable (which would probably be too large given the ES lib involved). What exactly is the problem? What happens when you hold run, then hold roll, then release roll?

It's freezes on the roll the animation and doesn't continue all the way through to the end unless I stop pressing the left or right key and the roll button...which isn't what I want...I want to have both the movement button pressed and the roll button pressed too and when I release the roll button I want it to start running again...The animations are all working correctly...I'm just not implementing the states correctly and the code surrounding it....I was hoping someone else might be able to offer a suggestion on how best to go about it...I'm sure I'll see it eventually if I come at it after a rest... fingers crossed

You should look into why the roll animation stays at the start. Usually this is a symptom that you are calling AnimationState setAnimation every frame, so it keeps setting the roll animation instead of letting it play.

10일 후

ok, I got the roll animation working correctly! The problem was too many other systems and classes were messing with the states, I stripped everything back and rewrote what I needed again and it all worked correctly.

I now want to ensure I get a complete roll everytime I press the roll button. what happens is that I have to keep the button pressed for a set amount of time (around 0.3 to 0.5 seconds) for the roll animation to complete. if I just tap the roll button it doesn't complete the roll but instead moves back into the idle stance, it starts the roll but reverses back to my idle animation instead of completing the whole roll animation and then going into my idle animation.
Is this to do with my settings for the mix times between my animation? any ideas?

 if (entity.bodyComp.body.getLinearVelocity().x == 0 && entity.bodyComp.body.getLinearVelocity().y == 0) {
entity.setState(idle);
}

You are setting the idle animation, probably something like that in your newest code. If a roll is in progress, don't set other animations. Or, you could play roll on a higher track, which would overwrite the lower track pose for everything keyed in roll. This isn't a great solution though, because if something is keyed in the lower track and not keyed in roll, you'll see it when rolling.

yeah i have something like that in my code but my roll code sets the velocity to higher amount, it takes at least 0.4 secs for the friction to slow down the entity before the statement above becomes true, (also that statement only applies when it touchingGround is false) which is about the how long it takes the roll animation to complete.
i'll recheck all the other places where i set my animation to idle and add something like this?

 if (entity.bodyComp.body.getLinearVelocity().x == 0 && entity.bodyComp.body.getLinearVelocity().y == 0) && entity.StateMachine.getCurrentState() != RollStart){
entity.setState(idle);
}

ok solved it by changing the rollend state to the following:

rollend(){
    @Override
    public void update(PlayerAgent entity) {
        if (entity.stateComp.stateChanged) {
            entity.stateComp.stateChanged = false;
            Gdx.app.log("PlayerState:", "Rollend State");
            //entity.setAnimation(entity.spineComp.playerStates.get(EntityStateComponent.EntityMoveStates.rollend), true);
            entity.spineComp.animationState.addAnimation(0, "Roll_b_end", false, 0);
            EntityManager.getPlayerAgent().rollCount = 0;
            entity.spineComp.animationState.addAnimation(0,"IdleAll",true,0);
        }
    }


};


Sorry to tack on another question to the end of this thread. but is there an easy way to flag the end of an animation? I want to block any other animations from taking place until for example my roll animation has completed. is there a flag I can use? or will I have to check the animation duration to check if the roll animation is complete?

You can poll by checking if the AnimationState getCurrent track is the animation you care about. Otherwise you can register an event, see AnimationState addListener and TrackEntry listener (which will be setListener for libgdx).