我有一个很大的学校项目,正在使用libGDX开发Pixel Platformer游戏。
我大部分时间都在使用ECS,但是由于无法区分玩家或至少是攻击者的状态,我一直在渲染玩家的动画时遇到困难。
让我告诉你,所以我有以下“移动”状态:
setlocal /?
但是对于播放器动画,我至少需要以下状态:
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 Atlas
public enum PlayerAnimations {
Idle, Walking, Jumping, DoubleJumping, Falling, Attack, JumpAttack, FallingAttack, FallAttack, Hit, Die;
}
AnimationSystem中负责播放器的代码:
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;
}
}
我当时正在考虑让状态处于“接地->空闲,行走”状态之内。 但是我仍然不知道如何使玩家进入AttackStates。
答案 0 :(得分:0)
经过一番挖掘,至少到现在为止,我找到了解决方案,所以对于任何有相同问题的人,这都是我的解决方法。
我首先创建了一个名为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
类和其他一些变量中为其添加状态机。
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
的枚举。
public enum PlayerAnimations {
Idle,
Walking,
Jumping, DoubleJumping,
Falling,
Attack_1, Attack_2, Attack_3,
AirAttack_1, AirAttack_2,
FallingAttack, FallAttack,
Hit,
Die
}
然后使用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);
希望这可以帮助一些人,再次抱歉我的英语不好。如果您有任何问题,请随时问我。