I have a big school project and I'm developing a Pixel Platformer game using libGDX.
I'm using ECS for the most part, but I've been having trouble rendering my player's animations because I can not distinguish the state of the player or at least the attack ones.
Player State Class
Let me just show you, so I have this "move" states:
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
But I'll need at least this states for player animation:
public enum PlayerAnimations {
Idle, Walking, Jumping, DoubleJumping, Falling, Attack, JumpAttack, FallingAttack, FallAttack, Hit, Die;
}
And many more has u can see on this packed texture: Player Animations Atlas
PlayerAgent Class:
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;
}
}
Animation Code
Code in the AnimationSystem that takes care of the player:
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;
}
I was thinking of having states inside a state like Grounded -> Idle, Walking. But I still don't know how I can make the player AttackStates.