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

Java弹跳小球碰撞异常求助:图标触碰后无法正常反弹

Java Swing弹跳小球交叉碰撞异常修复方案

问题背景

基于Java Swing开发弹跳小球程序,使用JLabel承载小球图标。仅开启X轴速度反转时小球可正常反弹,但启用Y轴速度反转后,小球交叉碰撞时图标出现异常,需实现碰撞后正常反弹。

问题根源

  1. 逻辑顺序错位:原代码先更新小球位置,再检测边界与碰撞,导致碰撞检测使用的是更新前的位置,逻辑混乱。
  2. 碰撞后未分离小球:小球碰撞后重叠,会持续触发碰撞检测,导致速度反复反转,引发卡顿或图标异常。
  3. 速度反转逻辑不合理:直接同时反转X/Y速度,未根据实际碰撞方向(X轴或Y轴方向重叠)选择对应速度分量反转,不符合物理碰撞逻辑。

修复后代码

启动类(无需修改)

package odev5_m_metin;

import javax.swing.*;

public class giris {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                odev_intersect o1 = new odev_intersect();
                o1.setVisible(true);
            }
        });
    }
}

主类(修复后)

package odev5_m_metin;

import javax.swing.*;
import java.util.Timer;
import java.util.TimerTask;
import java.awt.Rectangle;

public class odev_intersect extends JFrame {

    private JPanel panel1;
    private JLabel label1;
    private JLabel label2;
    private JLabel label3;

    static int x, y, x2, y2, x3, y3;
    static int hizx = 5, hizy = 4, hizx2 = 5, hizy2 = 7, hizx3 = 5, hizy3 = 5;
    static Timer time1;
    static Rectangle r1, r2, r3;

    public odev_intersect() {
        add(panel1);
        setSize(500, 500);
        setTitle("Intersect Ödev");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // 初始化小球位置
        x = 100;
        y = 200;
        x2 = 200;
        y2 = 200;
        x3 = 300;
        y3 = 300;

        label1.setBounds(x, y, 50, 50);
        label2.setBounds(x2, y2, 50, 50);
        label3.setBounds(x3, y3, 50, 50);

        time1 = new Timer();
        time1.schedule(new TimerTask() {
            @Override
            public void run() {
                // 先更新碰撞检测用的矩形(当前位置)
                r1 = new Rectangle(x, y, 50, 50);
                r2 = new Rectangle(x2, y2, 50, 50);
                r3 = new Rectangle(x3, y3, 50, 50);

                // 1. 检测边界碰撞并反转速度
                checkBoundaryCollision();

                // 2. 检测小球间碰撞并处理
                checkBallCollision(r1, r2, 1, 2);
                checkBallCollision(r1, r3, 1, 3);
                checkBallCollision(r2, r3, 2, 3);

                // 3. 更新小球位置
                x += hizx;
                y += hizy;
                x2 += hizx2;
                y2 += hizy2;
                x3 += hizx3;
                y3 += hizy3;

                // 更新UI
                label1.setBounds(x, y, 50, 50);
                label2.setBounds(x2, y2, 50, 50);
                label3.setBounds(x3, y3, 50, 50);
            }
        }, 0, 10);
    }

    // 检测边界碰撞
    private void checkBoundaryCollision() {
        if (x <= 0 || x >= 450) hizx *= -1;
        if (y <= 0 || y >= 450) hizy *= -1;

        if (x2 <= 0 || x2 >= 450) hizx2 *= -1;
        if (y2 <= 0 || y2 >= 450) hizy2 *= -1;

        if (x3 <= 0 || x3 >= 450) hizx3 *= -1;
        if (y3 <= 0 || y3 >= 450) hizy3 *= -1;
    }

    // 处理小球间碰撞
    private void checkBallCollision(Rectangle a, Rectangle b, int ballA, int ballB) {
        if (a.intersects(b)) {
            // 计算重叠量
            int overlapX = Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x);
            int overlapY = Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y);

            // 判断碰撞方向:重叠量小的方向为碰撞主方向
            if (overlapX < overlapY) {
                // X轴方向碰撞,反转X速度
                if (ballA == 1) hizx *= -1;
                if (ballA == 2) hizx2 *= -1;
                if (ballA == 3) hizx3 *= -1;

                if (ballB == 1) hizx *= -1;
                if (ballB == 2) hizx2 *= -1;
                if (ballB == 3) hizx3 *= -1;

                // 分离小球:沿X轴推开
                if (a.x < b.x) {
                    a.x -= overlapX / 2;
                    b.x += overlapX / 2;
                } else {
                    a.x += overlapX / 2;
                    b.x -= overlapX / 2;
                }
            } else {
                // Y轴方向碰撞,反转Y速度
                if (ballA == 1) hizy *= -1;
                if (ballA == 2) hizy2 *= -1;
                if (ballA == 3) hizy3 *= -1;

                if (ballB == 1) hizy *= -1;
                if (ballB == 2) hizy2 *= -1;
                if (ballB == 3) hizy3 *= -1;

                // 分离小球:沿Y轴推开
                if (a.y < b.y) {
                    a.y -= overlapY / 2;
                    b.y += overlapY / 2;
                } else {
                    a.y += overlapY / 2;
                    b.y -= overlapY / 2;
                }
            }

            // 更新对应小球的坐标
            updateBallPosition(ballA, a);
            updateBallPosition(ballB, b);
        }
    }

    // 更新小球坐标
    private void updateBallPosition(int ball, Rectangle rect) {
        switch (ball) {
            case 1:
                x = rect.x;
                y = rect.y;
                break;
            case 2:
                x2 = rect.x;
                y2 = rect.y;
                break;
            case 3:
                x3 = rect.x;
                y3 = rect.y;
                break;
        }
    }
}

关键修复说明

  • 调整逻辑顺序:先基于当前位置检测碰撞,再更新位置,确保碰撞检测的准确性。
  • 碰撞分离处理:计算小球碰撞后的重叠量,沿碰撞方向将小球推开,避免持续触发碰撞检测。
  • 方向化速度反转:根据X/Y轴的重叠量判断碰撞主方向,仅反转对应轴的速度,符合物理碰撞逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 16:45:22