在 Java 圖形界面 (Swing/AWT) 遊戲中,實現角色移動控制:
KeyListener 的 keyPressed/keyReleased 是**單獨**觸發的,無法直接感知「同時按下」。必須**自行維護按鍵狀態集合**。
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.Set;
public class GameKeyListener extends KeyAdapter {
// 使用 Set 存儲當前按下的鍵碼,自動去重,查詢 O(1)
private final Set pressedKeys = new HashSet<>();
@Override
public void keyPressed(KeyEvent e) {
pressedKeys.add(e.getKeyCode());
}
@Override
public void keyReleased(KeyEvent e) {
pressedKeys.remove(e.getKeyCode());
}
// 提供查詢方法
public boolean isPressed(int keyCode) {
return pressedKeys.contains(keyCode);
}
// 獲取當前移動向量 (dx, dy)
public Point getMoveVector() {
int dx = 0, dy = 0;
if (isPressed(KeyEvent.VK_A)) dx -= 1;
if (isPressed(KeyEvent.VK_D)) dx += 1;
if (isPressed(KeyEvent.VK_W)) dy -= 1; // Y軸通常向下為正
if (isPressed(KeyEvent.VK_S)) dy += 1;
return new Point(dx, dy); // 返回 (-1,-1) 表示左上斜向等
}
}
// 遊戲主迴圈 (如 javax.swing.Timer 每 16ms 執行一次)
Timer gameLoop = new Timer(16, e -> {
Point move = keyListener.getMoveVector();
if (move.x != 0 || move.y != 0) {
// 歸一化斜向速度 (避免斜向比直向快 √2 倍)
double length = Math.sqrt(move.x * move.x + move.y * move.y);
double vx = (move.x / length) * SPEED;
double vy = (move.y / length) * SPEED;
player.x += vx;
player.y += vy;
}
repaint(); // 觸發重繪
});
gameLoop.start();
JPanel 或 JFrame 可獲取焦點:setFocusable(true); requestFocusInWindow();。Scene.setOnKeyPressed 配合 KeyCodeCombination 或 InputMap/ActionMap (Swing) 可實現更解耦的命令模式綁定。「同時按下」本質是「狀態疊加」。通過 Set<Integer> 維護即時按鍵狀態,在遊戲邏輯幀中合成移動向量,是實現流暢八方向移動的標準模式。