Java Swing中JFrame内双矩形差异化移动及标签游戏功能实现求助
标签游戏开发问题与解决方案
问题描述
开发一个标签游戏,需求如下:
- 通过键盘方向键控制蓝色大矩形在JFrame内移动
- 红色小矩形自动远离大矩形
- 大矩形触碰小矩形后,屏幕刷新可重新追逐
- 添加分数面板显示实时分数和游戏倒计时
当前问题:两个矩形重叠在一起,无法实现差异化移动,附上现有代码:
IT类代码
import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.awt.geom.*; public class IT extends JPanel implements ActionListener, KeyListener { Timer shapeTimer = new Timer(5, this); public double xPos = 0, yPos = 0, movementX = 0, movementY = 0; public int rectSize = 50; public int rectSize2 = 35; public int windowWidth; int windowHeight; public int xBound; public int yBound; public IT(int w, int h){ shapeTimer.start(); addKeyListener(this); setFocusable(true); setFocusTraversalKeysEnabled(false); windowWidth = w; windowHeight = h; xBound = (windowWidth - rectSize); yBound = (windowHeight - rectSize); } public void paintComponent(Graphics g){ super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; Rectangle2D movableRect = new Rectangle2D.Double(xPos, yPos, rectSize, rectSize); g2.setColor(Color.BLUE); g2.draw(movableRect); g2.fill(movableRect); Rectangle2D movableRect2 = new Rectangle2D.Double(xPos, yPos, rectSize2, rectSize2); g2.setColor(Color.RED); g2.draw(movableRect2); g2.fill(movableRect2); } public void actionPerformed(ActionEvent e){ repaint(); xPos += movementX; yPos += movementY; } public void moveUp(){ if (yPos == 0){ movementY = 0; movementX = 0; } movementY = -0.5; movementX = 0; } public void moveDown(){ if (yPos == yBound){ movementY = 0; movementX = 0; } movementY = 0.5; movementX = 0; } public void moveLeft() { if (xPos == 0){ movementY = 0; movementX = 0; } movementX = -0.5; movementY = 0; } public void moveRight(){ if (xPos == xBound) { movementY = 0; movementX = 0; } movementX = 0.5; movementY = 0; } public void enlargeSquare(){ rectSize++; rectSize2++; } public void shrinkSquare(){ rectSize--; rectSize2--; } public void keyPressed(KeyEvent e){ int keyCode = e.getKeyCode(); if (keyCode == KeyEvent.VK_UP){ moveUp(); } if (keyCode == KeyEvent.VK_DOWN){ moveDown(); } if (keyCode == KeyEvent.VK_RIGHT){ moveRight(); } if (keyCode == KeyEvent.VK_LEFT){ moveLeft(); } if (keyCode == KeyEvent.VK_OPEN_BRACKET) { shrinkSquare(); } if (keyCode == KeyEvent.VK_CLOSE_BRACKET) { enlargeSquare(); } } public void keyTyped(KeyEvent e){ } public void keyReleased(KeyEvent e){ int keyCode = e.getKeyCode(); if (keyCode == KeyEvent.VK_UP){ movementX = 0; movementY = 0; } if (keyCode == KeyEvent.VK_DOWN){ movementX = 0; movementY = 0; } if (keyCode == KeyEvent.VK_RIGHT){ movementX = 0; movementY = 0; } if (keyCode == KeyEvent.VK_UP){ movementX = 0; movementY = 0; } } }
MainTester类代码
import javax.swing.*; import javax.swing.JFrame; public class MainTester { public static void main(String[] args) { // TODO Auto-generated method stub int frameWidth = 850; int frameHeight = 650; JFrame frmMain = new JFrame(); frmMain.setSize(frameWidth, frameHeight); IT it = new IT(frameWidth, frameHeight); frmMain.add(it); frmMain.setVisible(true); frmMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frmMain.setTitle("Tag Game"); } }
解决方案
1. 解决矩形重叠问题
问题根源是红色矩形和蓝色矩形使用了相同的xPos和yPos坐标。需要给红色矩形单独设置位置变量:
- 添加
redX、redY存储红色矩形坐标 - 初始化时将红色矩形放在屏幕随机位置(避免初始重叠)
2. 实现红色矩形自动远离逻辑
在actionPerformed方法中,计算红蓝矩形的位置差,让红色矩形向远离蓝色矩形的方向移动:
- 计算x和y方向的距离差
- 根据差值设置红色矩形的移动方向,移动速度可自行调整
3. 添加碰撞检测与重置机制
使用Rectangle2D的intersects方法检测两个矩形是否碰撞:
- 碰撞后重置红色矩形位置为随机点
- 分数加1
4. 集成分数面板与倒计时器
- 添加
score变量存储分数,timeLeft变量存储剩余时间 - 使用另一个
Timer处理倒计时 - 在
paintComponent中绘制分数和倒计时文本
修改后的完整代码
IT类代码
import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import java.util.Random; public class IT extends JPanel implements ActionListener, KeyListener { private final Timer gameTimer = new Timer(5, this); private final Timer countdownTimer = new Timer(1000, this); // 蓝色矩形属性 private double blueX = 0, blueY = 0, blueMoveX = 0, blueMoveY = 0; private final int blueSize = 50; // 红色矩形属性 private double redX, redY; private final int redSize = 35; private final double redSpeed = 0.3; // 游戏属性 private int score = 0; private int timeLeft = 60; // 倒计时60秒 private final int windowWidth; private final int windowHeight; private final Random random = new Random(); public IT(int w, int h){ windowWidth = w; windowHeight = h; // 初始化红色矩形到随机位置 resetRedRect(); // 启动计时器 gameTimer.start(); countdownTimer.start(); addKeyListener(this); setFocusable(true); setFocusTraversalKeysEnabled(false); } @Override public void paintComponent(Graphics g){ super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; // 绘制蓝色矩形 Rectangle2D blueRect = new Rectangle2D.Double(blueX, blueY, blueSize, blueSize); g2.setColor(Color.BLUE); g2.fill(blueRect); // 绘制红色矩形 Rectangle2D redRect = new Rectangle2D.Double(redX, redY, redSize, redSize); g2.setColor(Color.RED); g2.fill(redRect); // 绘制分数和倒计时 g2.setColor(Color.BLACK); g2.setFont(new Font("Arial", Font.BOLD, 20)); g2.drawString("分数: " + score, 20, 30); g2.drawString("剩余时间: " + timeLeft + "s", windowWidth - 150, 30); // 游戏结束提示 if (timeLeft <= 0) { g2.setFont(new Font("Arial", Font.BOLD, 40)); g2.drawString("游戏结束! 最终分数: " + score, windowWidth/2 - 200, windowHeight/2); gameTimer.stop(); countdownTimer.stop(); } } @Override public void actionPerformed(ActionEvent e){ if (e.getSource() == gameTimer) { // 更新蓝色矩形位置 updateBluePosition(); // 更新红色矩形位置(自动远离) updateRedPosition(); // 检测碰撞 checkCollision(); repaint(); } else if (e.getSource() == countdownTimer) { if (timeLeft > 0) { timeLeft--; } } } private void updateBluePosition() { blueX += blueMoveX; blueY += blueMoveY; // 边界检测,防止蓝色矩形移出窗口 blueX = Math.max(0, Math.min(blueX, windowWidth - blueSize)); blueY = Math.max(0, Math.min(blueY, windowHeight - blueSize)); } private void updateRedPosition() { // 计算红蓝矩形中心坐标 double blueCenterX = blueX + blueSize/2; double blueCenterY = blueY + blueSize/2; double redCenterX = redX + redSize/2; double redCenterY = redY + redSize/2; // 计算方向差 double dx = redCenterX - blueCenterX; double dy = redCenterY - blueCenterY; double distance = Math.sqrt(dx*dx + dy*dy); if (distance > 0) { // 归一化方向向量,乘以速度 redX += (dx / distance) * redSpeed; redY += (dy / distance) * redSpeed; // 边界检测,防止红色矩形移出窗口 redX = Math.max(0, Math.min(redX, windowWidth - redSize)); redY = Math.max(0, Math.min(redY, windowHeight - redSize)); } } private void checkCollision() { Rectangle2D blueRect = new Rectangle2D.Double(blueX, blueY, blueSize, blueSize); Rectangle2D redRect = new Rectangle2D.Double(redX, redY, redSize, redSize); if (blueRect.intersects(redRect)) { score++; resetRedRect(); } } private void resetRedRect() { // 随机生成红色矩形位置,确保不与蓝色矩形初始重叠 do { redX = random.nextInt(windowWidth - redSize); redY = random.nextInt(windowHeight - redSize); } while (new Rectangle2D.Double(redX, redY, redSize, redSize).intersects(blueX, blueY, blueSize, blueSize)); } // 蓝色矩形移动控制 public void moveUp(){ blueMoveY = -0.5; blueMoveX = 0; } public void moveDown(){ blueMoveY = 0.5; blueMoveX = 0; } public void moveLeft(){ blueMoveX = -0.5; blueMoveY = 0; } public void moveRight(){ blueMoveX = 0.5; blueMoveY = 0; } @Override public void keyPressed(KeyEvent e){ int keyCode = e.getKeyCode(); switch(keyCode) { case KeyEvent.VK_UP: moveUp(); break; case KeyEvent.VK_DOWN: moveDown(); break; case KeyEvent.VK_LEFT: moveLeft(); break; case KeyEvent.VK_RIGHT: moveRight(); break; } } @Override public void keyTyped(KeyEvent e){} @Override public void keyReleased(KeyEvent e){ blueMoveX = 0; blueMoveY = 0; } }
MainTester类代码
import javax.swing.*; public class MainTester { public static void main(String[] args) { int frameWidth = 850; int frameHeight = 650; JFrame frmMain = new JFrame("Tag Game"); frmMain.setSize(frameWidth, frameHeight); frmMain.add(new IT(frameWidth, frameHeight)); frmMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frmMain.setVisible(true); } }
关键修改点说明
- 分离矩形坐标:给红色矩形单独设置
redX、redY变量,不再与蓝色矩形共享坐标 - 自动远离逻辑:通过计算中心距离差,让红色矩形向远离蓝色矩形的方向移动
- 碰撞检测:使用
intersects方法判断矩形重叠,碰撞后重置红色位置并加分 - 计时与分数:新增倒计时计时器,在界面实时显示分数和剩余时间,时间结束后停止游戏
- 边界优化:修复原代码中边界判断的精度问题(使用Math.max/min替代等于判断,避免浮点精度误差)
内容的提问来源于stack exchange,提问作者killered1
相关产品推荐
相关产品推荐

