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

如何在JavaFX中实现两个汽车组碰撞反弹并转向?

解决JavaFX汽车组弹跳与碰撞问题

问题核心

你原来的代码用Timeline预定义固定运动路径,无法动态响应碰撞和边界触碰。要实现持续弹跳+碰撞转向的效果,需要实时更新位置并逐帧检测碰撞,改用AnimationTimer驱动物理计算更合适。

修改后的完整代码

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.geometry.Bounds;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.BorderWidths;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;

public class ShapeTest extends Application {

    private double car1XVel = 2; // 汽车1 X方向速度
    private double car1YVel = 1.5; // 汽车1 Y方向速度
    private double car2XVel = -1.8; // 汽车2 X方向速度
    private double car2YVel = 2.2; // 汽车2 Y方向速度

    @Override
    public void start(Stage stage) {
        Pane pane = new Pane();
        Scene scene = new Scene(pane, 300, 300);

        // ====================== 汽车1 ======================
        Rectangle body = new Rectangle(150, 50, 100, 50);
        body.setFill(Color.RED);
        Circle wheel1 = new Circle(175, 100, 15);
        Circle wheel2 = new Circle(225, 100, 15);
        Rectangle roof = new Rectangle(160, 30, 80, 20);
        roof.setFill(Color.DARKBLUE);
        Rectangle window1 = new Rectangle(170, 35, 25, 15);
        Rectangle window2 = new Rectangle(205, 35, 25, 15);
        Shape windows = Shape.union(window1, window2);
        windows.setFill(Color.LIGHTBLUE);

        Group carGroup = new Group(body, wheel1, wheel2, roof, windows);
        carGroup.relocate(5, 5);
        pane.getChildren().add(carGroup);

        // ====================== 汽车2 ======================
        Rectangle body1 = new Rectangle(150, 50, 100, 50);
        body1.setFill(Color.YELLOW);
        Circle wheel11 = new Circle(175, 100, 15);
        Circle wheel22 = new Circle(225, 100, 15);
        Rectangle roof1 = new Rectangle(160, 30, 80, 20);
        roof1.setFill(Color.CYAN);
        Rectangle window11 = new Rectangle(170, 35, 25, 15);
        Rectangle window22 = new Rectangle(205, 35, 25, 15);
        Shape windows1 = Shape.union(window11, window22);
        windows1.setFill(Color.PINK);

        Group carGroup1 = new Group(body1, wheel11, wheel22, roof1, windows1);
        carGroup1.relocate(200, 200);
        pane.getChildren().add(carGroup1);

        // 设置Pane边框
        pane.setBorder(new Border(new BorderStroke(Color.RED,
                BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths.DEFAULT)));

        // ====================== 动画与碰撞逻辑 ======================
        new AnimationTimer() {
            @Override
            public void handle(long now) {
                // 获取Pane的有效活动边界(扣除边框)
                Bounds paneBounds = pane.getLayoutBounds();
                double borderLeft = pane.getBorder().getInsets().getLeft();
                double borderRight = pane.getBorder().getInsets().getRight();
                double borderTop = pane.getBorder().getInsets().getTop();
                double borderBottom = pane.getBorder().getInsets().getBottom();

                // 更新汽车1位置并检测边界碰撞
                carGroup.setLayoutX(carGroup.getLayoutX() + car1XVel);
                carGroup.setLayoutY(carGroup.getLayoutY() + car1YVel);
                Bounds car1Bounds = carGroup.getBoundsInParent();
                if (car1Bounds.getMinX() <= borderLeft || car1Bounds.getMaxX() >= paneBounds.getWidth() - borderRight) {
                    car1XVel *= -1;
                }
                if (car1Bounds.getMinY() <= borderTop || car1Bounds.getMaxY() >= paneBounds.getHeight() - borderBottom) {
                    car1YVel *= -1;
                }

                // 更新汽车2位置并检测边界碰撞
                carGroup1.setLayoutX(carGroup1.getLayoutX() + car2XVel);
                carGroup1.setLayoutY(carGroup1.getLayoutY() + car2YVel);
                Bounds car2Bounds = carGroup1.getBoundsInParent();
                if (car2Bounds.getMinX() <= borderLeft || car2Bounds.getMaxX() >= paneBounds.getWidth() - borderRight) {
                    car2XVel *= -1;
                }
                if (car2Bounds.getMinY() <= borderTop || car2Bounds.getMaxY() >= paneBounds.getHeight() - borderBottom) {
                    car2YVel *= -1;
                }

                // 两车碰撞检测与处理
                if (car1Bounds.intersects(car2Bounds)) {
                    // 简单弹性碰撞:反转双方速度
                    car1XVel *= -1;
                    car1YVel *= -1;
                    car2XVel *= -1;
                    car2YVel *= -1;
                    // 轻微分离避免两车卡在一起
                    carGroup.setLayoutX(carGroup.getLayoutX() + car1XVel * 2);
                    carGroup.setLayoutY(carGroup.getLayoutY() + car1YVel * 2);
                }
            }
        }.start();

        stage.setTitle("Bouncing Cars");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

关键逻辑说明

  • 替换Timeline为AnimationTimer:AnimationTimer的handle方法会逐帧执行,能实时响应碰撞并调整运动方向,比预定义路径的Timeline更适合动态物理效果。
  • 速度变量控制:为每个汽车组设置独立的X/Y方向速度,通过反转速度值实现转向。
  • 边界碰撞处理:计算Pane的有效活动区域(扣除边框宽度),当汽车边界触碰Pane边界时,反转对应方向的速度。
  • 两车碰撞处理:用getBoundsInParent().intersects()判断两车是否相交,碰撞后反转双方速度,并轻微移动汽车避免卡滞。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 21:57:56