Animate Player, использующий конечный автомат в Ashley ECS - PullRequest
0 голосов
/ 29 сентября 2018

У меня большой школьный проект, и я разрабатываю игру Pixel Platformer с использованием libGDX.

В основном я использую ECS, но у меня были проблемы с рендерингом анимаций моего игрока, потому что яне может различить состояние игрока или, по крайней мере, атакующего.

Класс состояния игрока

Позвольте мне просто показать вам, поэтому у меня есть эти состояния "хода":

public enum PlayerState implements State<PlayerAgent> {

Idle(){
    @Override
    public void update(PlayerAgent agent) {
        agent.moveOnGround();
        if(!agent.isTouchingGround) {
            if (agent.body.getLinearVelocity().y < -0.05)
                agent.stateMachine.changeState(Falling);
            else
                agent.stateMachine.changeState(Jumping);
        }else{
            if (agent.body.getLinearVelocity().x != 0)
                agent.stateMachine.changeState(Walking);
        }
    }
},

Walking() {
    @Override
    public void update(PlayerAgent agent) {
        agent.moveOnGround();
        if(!agent.isTouchingGround) {
            if (agent.body.getLinearVelocity().y < -0.05)
                agent.stateMachine.changeState(Falling);
            else
                agent.stateMachine.changeState(Jumping);
        }else{
            if (agent.body.getLinearVelocity().x == 0)
                agent.stateMachine.changeState(Idle);
        }
    }
},

Jumping() {
    @Override
    public void enter(PlayerAgent agent) {
    }

    @Override
    public void update(PlayerAgent agent) {
        agent.moveOnAir();
        if (agent.body.getLinearVelocity().y < 0)
            agent.stateMachine.changeState(Falling);
        /* else if (agent.jumpOnAir())
            agent.stateMachine.changeState(DoubleJumping);*/
    }
},

DoubleJumping () {
    @Override
    public void update(PlayerAgent agent) {
        agent.moveOnAir();
        if (agent.body.getLinearVelocity().y < 0)
            agent.stateMachine.changeState(Falling);
    }
},

Falling() {

    @Override
    public void update(PlayerAgent agent) {
        agent.moveOnAir();
        if (agent.isTouchingGround) {
            agent.stateMachine.changeState(Idle);
        } else {
            if (agent.stateMachine.getPreviousState() != DoubleJumping) {
                if (agent.jumpOnAir())
                    agent.stateMachine.changeState(DoubleJumping);
            }
        }
    }
};

@Override
public void enter(PlayerAgent agent) {
    // System.out.println(this.toString());
}

@Override
public void update(PlayerAgent agent) {

}

@Override
public void exit(PlayerAgent agent) {
    agent.timer = 0.0f;
}

@Override
public boolean onMessage(PlayerAgent agent, Telegram telegram) {
    return false;
}
}

Player Animations Enum

Но мне понадобятся, по крайней мере, следующие состояния для анимации игрока:

public enum PlayerAnimations {

 Idle, Walking, Jumping, DoubleJumping, Falling, Attack, JumpAttack, FallingAttack, FallAttack, Hit, Die;

}

И еще много чего вы можете увидеть на этой упакованной текстуре: Атлас анимации игрока

Класс PlayerAgent:

public class PlayerAgent implements Updateable {

    private Entity player;

    protected Body body;
    private TransformComponent transform;
    SensorCollisionComponent sensors;

    protected static StateMachine<PlayerAgent, PlayerState>  stateMachine;

    public boolean isTouchingGround = true;
    public boolean isTouchingWallLeft = false;
    public boolean isTouchingWallRight = false;

    public static float timer = 0.0f;

    public PlayerAgent(Entity player) {
        this.player = player;
        body = player.getComponent(B2dBodyComponent.class).body;
        transform = player.getComponent(TransformComponent.class);
        sensors = player.getComponent(SensorCollisionComponent.class);

        stateMachine = new DefaultStateMachine<PlayerAgent, PlayerState>(this, PlayerState.Idle);
    }

    @Override
    public void update(float deltaTime) {
        isTouchingGround = (sensors.numFoot > 0);
        isTouchingWallLeft = (sensors.numLeftWall > 0);
        isTouchingWallRight = (sensors.numRightWall > 0);

        stateMachine.update();

        if (!KeyboardController.left && !KeyboardController.right)
            body.setLinearVelocity(MathUtils.lerp(body.getLinearVelocity().x, 0, 0.2f), body.getLinearVelocity().y);

    }


    public static PlayerState getCurrentState(){
        return stateMachine.getCurrentState();
    }
    public static PlayerState getLastState(){
        return stateMachine.getPreviousState();
    }
    public static boolean isInState(PlayerState state){
        return stateMachine.isInState(state);
    }

    public boolean moveOnGround() {
        if (KeyboardController.left) {
            body.setLinearVelocity(MathUtils.lerp(body.getLinearVelocity().x, -3f, 0.1f), body.getLinearVelocity().y);
            transform.flipX = true;
        }
        if (KeyboardController.right){
            body.setLinearVelocity(MathUtils.lerp(body.getLinearVelocity().x, 3f, 0.1f), body.getLinearVelocity().y);
            transform.flipX = false;
        }
        if (KeyboardController.up) {
            body.applyLinearImpulse(0, 2.7f, body.getWorldCenter().x, body.getWorldCenter().y, true);
            KeyboardController.up = false;
        }
            return (KeyboardController.left || KeyboardController.right);
    }

    public boolean moveOnAir(){
        if (KeyboardController.left){
            body.setLinearVelocity(MathUtils.lerp(body.getLinearVelocity().x, -1.5f, 0.1f), body.getLinearVelocity().y);
            transform.flipX = true;
        }if (KeyboardController.right){
            transform.flipX = false;
            body.setLinearVelocity(MathUtils.lerp(body.getLinearVelocity().x, 1.5f, 0.1f), body.getLinearVelocity().y);
        }
            return (KeyboardController.left || KeyboardController.right);
    }

    public boolean jumpOnAir(){
        if (KeyboardController.up) {
            body.applyLinearImpulse(0, 2.5f, body.getWorldCenter().x, body.getWorldCenter().y, true);
            KeyboardController.up = false;
            return true;
        }
        return false;
    }

}

Код анимации

Код в AnimationSystem, которая заботится о игроке:

PlayerComponent pc = pm.get(entity);
                if (pc.attacking) {

                    switch (PlayerAgent.getCurrentState()) {
                        case Idle:
                        case Walking:
                            if(PlayerAgent.getLastState() == PlayerState.Falling) {
                                key = PlayerAnimations.FallAttack.ordinal();
                                if(ani.animations.get(key).isAnimationFinished(PlayerAgent.timer)) pc.attacking = false;
                            }else
                                key = PlayerAnimations.Attack.ordinal();
                            break;
                        case Jumping:
                        case DoubleJumping:
                            key = PlayerAnimations.JumpAttack.ordinal();
                            break;
                        case Falling:
                            key = PlayerAnimations.FallingAttack.ordinal();
                            break;
                        default:
                            key = PlayerAnimations.Idle.ordinal();
                            break;
                    }
                } else {
                    switch (PlayerAgent.getCurrentState()) {
                        case Idle:
                            key = PlayerAnimations.Idle.ordinal();
                            break;
                        case Walking:
                            key = PlayerAnimations.Walking.ordinal();
                            break;
                        case Jumping:
                            key = PlayerAnimations.Jumping.ordinal();
                            break;
                        case DoubleJumping:
                            key = PlayerAnimations.DoubleJumping.ordinal();
                            break;
                        case Falling:
                            key = PlayerAnimations.Falling.ordinal();
                            break;
                        default:
                            key = PlayerAnimations.Idle.ordinal();
                            break;
                    }

                }
                PlayerAgent.timer += deltaTime;
            }

Я думал о том, чтобы иметь состояния внутри такого состояния, как Заземленное -> Холостой ходьба.Но я до сих пор не знаю, как мне сделать плеер AttackStates.

1 Ответ

0 голосов
/ 03 октября 2018

после некоторого копания я нашел решение, по крайней мере, на данный момент, поэтому для всех, у кого есть такая же проблема, вот как я ее решил.

PlayerAttackState.java

Я начал с созданияновое состояние атаки с именем PlayerAttackState.

public enum PlayerAttackState implements State<PlayerAgent> {


        NONE(){
            @Override
            public void enter(PlayerAgent agent) {
                agent.attacking = false;
                agent.lastAttack = System.currentTimeMillis();
            }

            @Override
            public void update(PlayerAgent agent) {
                if (KeyboardController.attack && !agent.attacking) {
                    agent.attacking = true;
                    KeyboardController.attack = false;
                    agent.attackStateMachine.changeState(Attacking);
                    //agent.playerComp.attack();
                }
            }
        },

        Attacking(){
            @Override
            public void enter(PlayerAgent agent) {

                agent.timer  = 0f;
                agent.attackCombo++;
                if (agent.attackCombo > 3 || System.currentTimeMillis() - agent.lastAttack > 100) agent.attackCombo = 1;
                if (agent.stateMachine.isInState(PlayerState.Jumping) || agent.stateMachine.isInState(PlayerState.DoubleJumping))
                    agent.attackStateMachine.changeState(AirAttack);

            }

            @Override
            public void update(PlayerAgent agent) {
                super.update(agent);
                if (agent.stateMachine.isInState(PlayerState.Jumping) || agent.stateMachine.isInState(PlayerState.DoubleJumping))
                    agent.attackStateMachine.changeState(AirAttack);

            }
        },

        AirAttack(){
            @Override
            public void enter(PlayerAgent agent) {
                if (agent.attackCombo >= 2) {
                    agent.attackCombo = 1;
                    agent.attackStateMachine.changeState(FallingAttack);
                }
            }

            @Override
            public void update(PlayerAgent agent) {
                super.update(agent);
                if (agent.attackCombo == 3) {
                    agent.attackCombo = 1;
                    agent.attackStateMachine.changeState(FallingAttack);
                }
            }
        },

        FallingAttack(){
            @Override
            public void enter(PlayerAgent agent) {

            }

            @Override
            public void update(PlayerAgent agent) {
                if (agent.stateMachine.isInState(PlayerState.Idle) || agent.stateMachine.isInState(PlayerState.Walking))
                    agent.attackStateMachine.changeState(FallAttack);
                else if (agent.stateMachine.isInState(PlayerState.DoubleJumping))
                    agent.attackStateMachine.changeState(AirAttack);
            }
        },

        FallAttack(){
            @Override
            public void update(PlayerAgent agent) {
                super.update(agent);
            }
        },

    ;

        @Override
        public void enter(PlayerAgent agent) {

        }

        @Override
        public void update(PlayerAgent agent) {
            if (agent.animation.isAnimationFinished(agent.timer))
                agent.attackStateMachine.changeState(NONE);
        }

        @Override
        public void exit(PlayerAgent agent) {
        }

        @Override
        public boolean onMessage(PlayerAgent agent, Telegram telegram) {
            return false;
        }
    }

PlayerAgent.java

Поэтому после этого мне нужно было добавить конечный автомат для него в классе PlayerAgent и некоторые другие переменные.

public class PlayerAgent implements Updateable {

    private Entity player;

    protected Body body;
    private TransformComponent transform;
    public SensorCollisionComponent sensors;
    public PlayerComponent playerComp;
    public AttackComponent attackComp;

    protected static StateMachine<PlayerAgent, PlayerState>  stateMachine;
    protected static StateMachine<PlayerAgent, PlayerAttackState> attackStateMachine;

    public boolean isTouchingGround = true;
    public boolean isTouchingWallLeft = false;
    public boolean isTouchingWallRight = false;
    public static boolean attacking = false;

    public static Animation animation = null;

    public static float timer = 0.0f;
    public static int attackCombo = 0;
    public long lastAttack = 0l;


    public PlayerAgent(Entity player) {
        this.player = player;
        body = player.getComponent(B2dBodyComponent.class).body;
        transform = player.getComponent(TransformComponent.class);
        sensors = player.getComponent(SensorCollisionComponent.class);
        playerComp = player.getComponent(PlayerComponent.class);
        attackComp = player.getComponent(AttackComponent.class);


        stateMachine = new DefaultStateMachine<PlayerAgent, PlayerState>(this, PlayerState.Idle);
        attackStateMachine = new DefaultStateMachine<PlayerAgent, PlayerAttackState>(this, PlayerAttackState.NONE);
    }

    @Override
    public void update(float deltaTime) {

        isTouchingGround = (sensors.numFoot > 0);
        isTouchingWallLeft = (sensors.numLeftWall > 0);
        isTouchingWallRight = (sensors.numRightWall > 0);

        stateMachine.update();
        attackStateMachine.update();

        if (!KeyboardController.left && !KeyboardController.right)
            body.setLinearVelocity(MathUtils.lerp(body.getLinearVelocity().x, 0, 0.2f), body.getLinearVelocity().y);

    }


    public static PlayerState getCurrentState(){
        return stateMachine.getCurrentState();
    }
    public static PlayerAttackState getAttackState(){return attackStateMachine.getCurrentState();}
    public static PlayerState getLastState(){
        return stateMachine.getPreviousState();
    }
    public static boolean isInState(PlayerState state){
        return stateMachine.isInState(state);
    }

    public boolean moveOnGround() {
        if (KeyboardController.left) {
            body.setLinearVelocity(MathUtils.lerp(body.getLinearVelocity().x, -3f, 0.1f), body.getLinearVelocity().y);
            transform.flipX = true;
        }
        if (KeyboardController.right){
            body.setLinearVelocity(MathUtils.lerp(body.getLinearVelocity().x, 3f, 0.1f), body.getLinearVelocity().y);
            transform.flipX = false;
        }
        if (KeyboardController.up) {
            body.applyLinearImpulse(0, 2.7f, body.getWorldCenter().x, body.getWorldCenter().y, true);
            KeyboardController.up = false;
        }
            return (KeyboardController.left || KeyboardController.right);
    }

    public boolean moveOnAir(){
        if (KeyboardController.left){
            body.setLinearVelocity(MathUtils.lerp(body.getLinearVelocity().x, -1.5f, 0.1f), body.getLinearVelocity().y);
            transform.flipX = true;
        }if (KeyboardController.right){
            transform.flipX = false;
            body.setLinearVelocity(MathUtils.lerp(body.getLinearVelocity().x, 1.5f, 0.1f), body.getLinearVelocity().y);
        }
            return (KeyboardController.left || KeyboardController.right);
    }

    public boolean jumpOnAir(){
        if (KeyboardController.up) {
            body.applyLinearImpulse(0, 2.5f, body.getWorldCenter().x, body.getWorldCenter().y, true);
            KeyboardController.up = false;
            return true;
        }
        return false;
    }

}

PlayerAnimations.java

Наконец, мне нужно было настроить анимацию для игрока, поэтому я создал перечисление под названием PlayerAnimations.

public enum PlayerAnimations {

    Idle,
    Walking,
    Jumping, DoubleJumping,
    Falling,
    Attack_1, Attack_2, Attack_3,
    AirAttack_1, AirAttack_2,
    FallingAttack, FallAttack,
    Hit,
    Die

}

AnimationSystem.java

А затем визуализируйте их, используя AniamtionSystem.

     switch (PlayerAgent.getAttackState()) {
                case NONE:
                    switch (PlayerAgent.getCurrentState()) {
                        case Idle:
                            key = PlayerAnimations.Idle.ordinal();
                            break;
                        case Walking:
                            key = PlayerAnimations.Walking.ordinal();
                            break;
                        case Jumping:
                            key = PlayerAnimations.Jumping.ordinal();
                            break;
                        case DoubleJumping:
                            key = PlayerAnimations.DoubleJumping.ordinal();
                            break;
                        case Falling:
                            key = PlayerAnimations.Falling.ordinal();
                            break;
                        default:
                            key = PlayerAnimations.Idle.ordinal();
                            break;
                    }
                    break;
                case Attacking:
                    switch (PlayerAgent.attackCombo) {
                        case 1:
                            key = PlayerAnimations.Attack_1.ordinal();
                            break;
                        case 2:
                            key = PlayerAnimations.Attack_2.ordinal();
                            break;
                        case 3:
                            key = PlayerAnimations.Attack_3.ordinal();
                            break;
                        default:
                            key = PlayerAnimations.Attack_1.ordinal();
                            System.out.println("Attack Combo Error");
                            break;
                    }
                    break;
                case AirAttack:
                    switch (PlayerAgent.attackCombo) {
                        case 1:
                            key = PlayerAnimations.AirAttack_1.ordinal();
                            break;
                        case 2:
                            key = PlayerAnimations.AirAttack_2.ordinal();
                            break;
                        default:
                            key = PlayerAnimations.AirAttack_1.ordinal();
                            System.out.println("Air Attack Combo Error");
                            break;
                    }
                    break;
                case FallingAttack:
                    key = PlayerAnimations.FallingAttack.ordinal();
                    break;
                case FallAttack:
                    key = PlayerAnimations.FallAttack.ordinal();
                    break;
                default:
                    key = PlayerAnimations.Idle.ordinal();
                    break;
            }
            timer = PlayerAgent.timer;
            PlayerAgent.timer += deltaTime;
            PlayerAgent.animation = ani.animations.get(key);
        }

        tex.region = ani.animations.get(key).getKeyFrame(timer);

Надеюсь, это поможет кому-нибудь и, еще раз, извините за мой плохой английский.Если у вас есть какие-либо вопросы, не стесняйтесь спрашивать меня.

...