You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
    }
}

关键修改点说明

  1. 分离矩形坐标:给红色矩形单独设置redX、redY变量,不再与蓝色矩形共享坐标
  2. 自动远离逻辑:通过计算中心距离差,让红色矩形向远离蓝色矩形的方向移动
  3. 碰撞检测:使用intersects方法判断矩形重叠,碰撞后重置红色位置并加分
  4. 计时与分数:新增倒计时计时器,在界面实时显示分数和剩余时间,时间结束后停止游戏
  5. 边界优化:修复原代码中边界判断的精度问题(使用Math.max/min替代等于判断,避免浮点精度误差)

内容的提问来源于stack exchange,提问作者killered1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 08:20:27