在 Ashley ECS 中使用有限状态机对播放器进行动画处理



我有一个很大的学校项目,我正在使用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;
}
}

播放器动画枚举

但是对于播放器动画,我至少需要此状态:

public enum PlayerAnimations {
Idle, Walking, Jumping, DoubleJumping, Falling, Attack, JumpAttack, FallingAttack, FallAttack, Hit, Die;
}

在这个包装纹理上,你可以看到更多: 玩家动画图集

玩家代理类:

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;
}
}

动画代码

动画系统中负责照顾玩家的代码:

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;
}

我正在考虑在像接地 ->空闲,行走这样的状态内拥有状态。 但我仍然不知道如何让玩家攻击状态。

经过一番挖掘,我找到了解决方案,至少目前是这样,所以对于任何有同样问题的人来说,这就是我解决它的方法。

玩家攻击状态.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;
}
}

玩家代理.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;
}
}

播放器动画.java

最后,我需要为播放器设置动画,所以我创建了一个名为PlayerAnimations的枚举。

public enum PlayerAnimations {
Idle,
Walking,
Jumping, DoubleJumping,
Falling,
Attack_1, Attack_2, Attack_3,
AirAttack_1, AirAttack_2,
FallingAttack, FallAttack,
Hit,
Die
}

动画系统.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);

希望这可以帮助一些人,再次为我的英语差感到抱歉。如果您有任何问题,请随时问我。

相关内容

  • 没有找到相关文章

最新更新