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.
after some digging i found a solution, at least for now, so for anyone that has the same problem here is how i solved it.
PlayerAttackState.java
I started by creating a new attack state called
PlayerAttackState
.PlayerAgent.java
So after this I needed to add a state machine for it in the
PlayerAgent
class and some other variables.PlayerAnimations.java
Finally I needed to set up the animations for the player so i created a enum called
PlayerAnimations
.AnimationSystem.java
And then render them using the
AniamtionSystem
.Hope this can help some one and, once again, sorry for my poor english. If you have any question feel free to ask me.