Java Swing双玩家躲猫猫游戏:GameManager适配改造求助
解决方案:重构游戏对象结构以支持双玩家
你当前的问题核心是将每个玩家作为独立JPanel加入JFrame,这种设计不符合游戏开发的常规模式:Swing布局管理器会自动调整组件位置,手动设null布局后JPanel默认尺寸为0x0,导致面板消失;且多JPanel的独立坐标体系会让碰撞检测、统一绘制变得异常复杂。
正确思路是:用单一游戏画布面板承载所有游戏对象的绘制与输入处理,玩家仅作为普通数据类存在,不再是UI组件。
1. 重构GameObject为普通类(取消JPanel继承)
去掉UI组件相关逻辑,只保留游戏对象核心属性与绘制方法:
import java.awt.Graphics; import java.awt.Image; abstract class GameObject { protected int x; protected int y; protected int width; protected int height; protected Image image; public GameObject(int x, int y, int width, int height) { this.x = x; this.y = y; this.width = width; this.height = height; } // 供画布调用的绘制方法 public void draw(Graphics g) { if (image != null) { g.drawImage(image, x, y, width, height, null); } } // 获取碰撞矩形(用于碰撞检测) public java.awt.Rectangle getBounds() { return new java.awt.Rectangle(x, y, width, height); } // Getter/Setter public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } }
2. 修改Player类,适配新的GameObject
保留移动、精灵切换逻辑,去掉JPanel相关代码:
import java.awt.event.KeyEvent; class Player extends GameObject { private char type; private int speed; private char directionX = 'R'; private boolean isMoving = false; private String[] sprites; public Player(int x, int y, int width, int height, char type) { super(x, y, width, height); this.type = type; String spritesSurvivor[] = {"src/char1.gif", "src/char2.gif", "src/charRun1.gif", "src/charRun2.gif", "src/dead.gif"}; String spritesHunter[] = {"src/hunter1.gif", "src/hunter2.gif", "src/hunterRun1.gif", "src/hunterRun2.gif", "src/dead.gif"}; if (type == 'S') { sprites = spritesSurvivor; speed = 10; } else if (type == 'H') { sprites = spritesHunter; speed = 15; } changeSprite(); } // 区分玩家按键:猎人用WASD,幸存者用方向键 public void move(int keyCode) { isMoving = true; switch (keyCode) { // 猎人控制 case KeyEvent.VK_W: y -= speed; break; case KeyEvent.VK_S: y += speed; break; case KeyEvent.VK_A: x -= speed; directionX = 'L'; break; case KeyEvent.VK_D: x += speed; directionX = 'R'; break; // 幸存者控制 case KeyEvent.VK_UP: if (type == 'S') y -= speed; break; case KeyEvent.VK_DOWN: if (type == 'S') y += speed; break; case KeyEvent.VK_LEFT: if (type == 'S') { x -= speed; directionX = 'L'; } break; case KeyEvent.VK_RIGHT: if (type == 'S') { x += speed; directionX = 'R'; } break; } checkBounds(); changeSprite(); } public void stop(int keyCode) { isMoving = false; changeSprite(); } private void changeSprite() { // 保留原有精灵切换逻辑,示例如下 if (isMoving) { image = java.awt.Toolkit.getDefaultToolkit().getImage(sprites[directionX == 'R' ? 2 : 3]); } else { image = java.awt.Toolkit.getDefaultToolkit().getImage(sprites[directionX == 'R' ? 0 : 1]); } } private void checkBounds() { // 替换为实际画布尺寸,可通过构造函数传入GamePanel引用获取 x = Math.max(0, Math.min(x, getWidth() - width)); y = Math.max(0, Math.min(y, getHeight() - height)); } }
3. 创建GamePanel作为游戏画布
负责绘制所有玩家、处理键盘输入、检测碰撞:
import java.awt.Graphics; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import javax.swing.JPanel; class GamePanel extends JPanel implements Runnable { private Player hunter; private Player survivor; private Thread gameThread; private boolean isGameRunning = true; public GamePanel() { // 初始化双玩家初始位置 hunter = new Player(100, 100, 50, 50, 'H'); survivor = new Player(800, 500, 50, 50, 'S'); // 键盘监听(需获取焦点) setFocusable(true); addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { hunter.move(e.getKeyCode()); survivor.move(e.getKeyCode()); } @Override public void keyReleased(KeyEvent e) { hunter.stop(e.getKeyCode()); survivor.stop(e.getKeyCode()); } }); // 启动游戏循环线程 gameThread = new Thread(this); gameThread.start(); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // 绘制背景(可选) g.setColor(java.awt.Color.LIGHT_GRAY); g.fillRect(0, 0, getWidth(), getHeight()); // 绘制双玩家 hunter.draw(g); survivor.draw(g); } @Override public void run() { // 60FPS固定帧率游戏循环 long lastTime = System.nanoTime(); double nsPerTick = 1000000000.0 / 60; double delta = 0; while (isGameRunning) { long now = System.nanoTime(); delta += (now - lastTime) / nsPerTick; lastTime = now; if (delta >= 1) { checkCollision(); repaint(); delta--; } } } // 碰撞检测逻辑 private void checkCollision() { if (hunter.getBounds().intersects(survivor.getBounds())) { isGameRunning = false; System.out.println("游戏结束!猎人抓住了幸存者!"); // 可添加弹窗、重置等结束逻辑 } } }
4. 修改GameManager,接入GamePanel
现在只需将画布加入JFrame即可:
import javax.swing.JFrame; class GameManager extends JFrame { public GameManager() { setTitle("Hide and Seek"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setExtendedState(JFrame.MAXIMIZED_BOTH); setResizable(true); add(new GamePanel()); setVisible(true); } public static void main(String[] args) { new GameManager(); } }
内容的提问来源于stack exchange,提问作者Eduardo Rodrigues
相关产品推荐
相关产品推荐

