Java Graphics2D玩家跳跃后无法恢复原动作的技术问题
基于Java Graphics2D的游戏角色跳跃后无法恢复前置动作的解决方案
问题说明
同时按住D键(向右移动)和SPACE键(跳跃),松开SPACE键后,角色无法继续向右移动——当前按键处理逻辑在keyReleased中直接重置了移动状态,完全没考虑其他按键仍处于按压状态的情况。
代码问题分析
- 单键状态覆盖:
keyPressed中直接设置dx和动画,但没有维护当前所有按下的按键,多键按压时后触发的按键会覆盖之前的状态 - 释放时盲目重置:
keyReleased里直接把moving设为false、非SPACE键时把dx设为0,不管其他键是否还按着,导致松开跳跃键时,移动状态被错误清空 - 动作记录逻辑错误:用
ArrayList记录动作的思路不对,它只能记录操作顺序,无法实时反映当前的按键状态
解决方案:维护实时按键状态集合
核心思路是用一个集合存储当前所有按下的按键码,在每一帧的update中根据集合内的按键来计算角色状态,而不是在单次按键事件中直接修改状态。
具体修改步骤
- 新增集合存储当前按下的按键:
private Set<Integer> pressedKeys = new HashSet<>(); - 新增跳跃触发标志位(避免按住SPACE连续跳):
private boolean jumpTriggered = false; keyPressed时仅将按键码加入集合,不直接修改角色状态keyReleased时移除按键码,重置跳跃触发标志- 在
update方法中根据集合内的按键,计算移动方向、切换动画、处理跳跃
修改后的完整代码
import java.awt.Image; import java.awt.event.KeyEvent; import java.util.HashSet; import java.util.Set; import javax.swing.ImageIcon; public class Player2 { private boolean blocked = false; private int x, y, dx, dy; Image imagem; private int altura, largura; private boolean direita = false; private boolean esquerda = false; private final int GRAVITY = 1; private final int JUMP_FORCE = -20; private final int GROUND_Y = 750; private int yVel; boolean moving = false; // 新增:存储当前按下的按键集合 private Set<Integer> pressedKeys = new HashSet<>(); // 新增:跳跃触发标志,避免按住SPACE连续跳 private boolean jumpTriggered = false; public Player2() { x = 100; y = GROUND_Y; yVel = 0; } public void update() { // 重置移动速度 dx = 0; moving = false; // 处理移动按键 if (pressedKeys.contains(KeyEvent.VK_D)) { dx = 5; direita = true; esquerda = false; moving = true; setRunRightImage(); } if (pressedKeys.contains(KeyEvent.VK_A)) { dx = -5; direita = false; esquerda = true; moving = true; setRunLeftImage(); } // 处理下蹲按键(仅在地面且未移动时) if (pressedKeys.contains(KeyEvent.VK_S) && y == GROUND_Y && !moving) { setCrouchImage(); blocked = true; } else { blocked = false; } // 处理跳跃按键(仅在地面且未触发过跳跃) if (pressedKeys.contains(KeyEvent.VK_SPACE) && y == GROUND_Y && !jumpTriggered) { yVel = JUMP_FORCE; setJumpImage(); jumpTriggered = true; moving = true; } // 如果没有任何移动动作且在地面,切换到 idle 动画 if (!moving && y == GROUND_Y && !blocked) { setIdleImage(); } // 应用重力和位置更新 x += dx; yVel += GRAVITY; y += yVel; if (y >= GROUND_Y) { y = GROUND_Y; yVel = 0; // 回到地面后重置跳跃触发标志 jumpTriggered = false; } } void keyPressed(KeyEvent botaoPressionado) { pressedKeys.add(botaoPressionado.getKeyCode()); // 处理ESC菜单 if (botaoPressionado.getKeyCode() == KeyEvent.VK_ESCAPE) { Menu menu = new Menu(); menu.setVisible(true); } } public void keyReleased(KeyEvent botaoSoltado) { pressedKeys.remove(botaoSoltado.getKeyCode()); // 松开SPACE时重置跳跃触发标志 if (botaoSoltado.getKeyCode() == KeyEvent.VK_SPACE) { jumpTriggered = false; } } // 封装图片设置方法,避免重复代码 private void setIdleImage() { ImageIcon referencia = new ImageIcon("res\\Colour2//player//120x80_gifs//__Idle.gif"); imagem = referencia.getImage(); } private void setRunRightImage() { ImageIcon referencia = new ImageIcon("res\\Colour2//player//120x80_gifs//__RunNormal.gif"); imagem = referencia.getImage(); } private void setRunLeftImage() { ImageIcon referencia = new ImageIcon("res\\Colour2//player//120x80_gifs//__RunBehind.gif"); imagem = referencia.getImage(); } private void setJumpImage() { ImageIcon referencia = new ImageIcon("res\\Colour2//player//120x80_gifs//__Jump.gif"); imagem = referencia.getImage(); } private void setCrouchImage() { ImageIcon referencia = new ImageIcon("res\\Colour2//player//120x80_gifs//__Crouch.gif"); imagem = referencia.getImage(); } void load() { setIdleImage(); altura = imagem.getHeight(null); largura = imagem.getWidth(null); } // 原nadaAinda方法保留空实现以防报错 private void nadaAinda() {} }
关键优化点
- 用
HashSet实时维护当前按下的按键,彻底解决多键按压的状态丢失问题 - 将状态计算逻辑统一放到
update方法,每一帧都根据当前按键状态刷新,动作切换更准确 - 新增跳跃触发标志,避免按住SPACE键时角色连续跳跃
- 封装图片设置方法,减少代码重复,便于维护
内容的提问来源于stack exchange,提问作者XN 2X
相关产品推荐
相关产品推荐

