如何实现JavaFX中StackPane集合的全局碰撞检测?
实现集合内所有元素的两两碰撞检测问题
我正在尝试对一组图形进行两两碰撞检测,但目前仅能实现邻近三个节点间的碰撞检测。其中addToRectCollection方法用于向集合中添加StackPane,拖拽功能由相关方法实现。请问需要修改或添加哪些代码,才能实现集合内所有元素间的碰撞检测?
以下是我的代码:
public class CollisionDetection extends Application { final ObjectProperty<Point2D> mousePosition = new SimpleObjectProperty<>(); Group gp = new Group(); // 尝试检测该列表中元素的碰撞 ArrayList<StackPane> stackPanes = new ArrayList<>(); @Override public void start(Stage stage) throws Exception { Scene sc = new Scene(gp, 700, 300); Button btn = new Button(); btn.setText("CREATE REct"); btn.setOnAction(event -> { Rectangle r = createRect(0); r.setFill(Color.RED); Text text = new Text("."); StackPane stack = new StackPane(); StackPane.setMargin(r, new Insets(8, 8, 8, 8)); stack.getChildren().addAll(r, text); ChoiceBox<String> dropdown = new ChoiceBox<>(); stack.getChildren().add(dropdown); makeDraggable(stack); addToRectCollection(stack); gp.getChildren().add(stack); }); gp.getChildren().add(btn); stage.setScene(sc); stage.show(); } private void addToRectCollection(StackPane r) { stackPanes.add(r); stackPanes.forEach(rect -> { stackPanes.forEach(rect2 -> { if (!rect.equals(rect2)) { setOnDragegd(rect2, rect); setOnDragegd(rect, rect2); } }); }); } private void setOnDragegd(StackPane pane, StackPane paneToSetDragged) { paneToSetDragged.setOnMouseDragged((MouseEvent event) -> { double deltaX = event.getSceneX() - mousePosition.get().getX(); double deltaY = event.getSceneY() - mousePosition.get().getY(); paneToSetDragged.setLayoutX(paneToSetDragged.getLayoutX() + deltaX); paneToSetDragged.setLayoutY(paneToSetDragged.getLayoutY() + deltaY); mousePosition.set(new Point2D(event.getSceneX(), event.getSceneY())); var intersect = paneToSetDragged.getBoundsInParent().intersects(pane.getBoundsInParent()); if (intersect) { pane.setStyle("-fx-background-color: #6a5246"); paneToSetDragged.setStyle("-fx-background-color: #6a5246"); Group g = new Group(); g.getChildren().add(paneToSetDragged); g.getChildren().add(pane); gp.getChildren().add(g); paneToSetDragged.setLayoutX(paneToSetDragged.getLayoutX() - deltaX); paneToSetDragged.setLayoutY(paneToSetDragged.getLayoutY() - deltaY); } else { pane.setStyle("-fx-background-color: #ffffff"); paneToSetDragged.setStyle("-fx-background-color: #ffffff"); } }); } private void makeDraggable(StackPane ractangleToSetDragged) { ractangleToSetDragged.setOnMousePressed((MouseEvent event) -> mousePosition.set(new Point2D(event.getSceneX(), event.getSceneY()))); ractangleToSetDragged.setOnMouseDragged((MouseEvent event) -> { double deltaX = event.getSceneX() - mousePosition.get().getX(); double deltaY = event.getSceneY() - mousePosition.get().getY(); ractangleToSetDragged.setLayoutX(ractangleToSetDragged.getLayoutX() + deltaX); ractangleToSetDragged.setLayoutY(ractangleToSetDragged.getLayoutY() + deltaY); mousePosition.set(new Point2D(event.getSceneX(), event.getSceneY())); }); } public Rectangle createRect(int x) { Rectangle r = new Rectangle(); r.setX(x); r.setY(60); r.setWidth(300); r.setHeight(150); return r; } public static void main(String[] args) { launch(args); } }
问题分析与修改方案
原代码核心问题
- 重复绑定拖拽事件:
addToRectCollection中的双重循环会给所有元素重复绑定setOnMouseDragged事件,每次添加新元素都会覆盖旧的拖拽逻辑,导致只有最后绑定的单个元素碰撞检测生效。 - 碰撞检测范围有限:每次拖拽仅对比单个元素,没有遍历整个集合完成两两检测。
- 冗余逻辑:拖拽时创建新
Group的操作会导致界面元素结构混乱,且回退位置的逻辑不符合正常拖拽碰撞的预期。
修改步骤
- 移除冗余的
setOnDragegd方法:将碰撞检测逻辑整合到makeDraggable的拖拽事件中,确保每次拖拽时遍历整个集合。 - 简化
addToRectCollection方法:仅负责将元素添加到集合,无需额外绑定事件。 - 实现全集合碰撞检测:拖拽时遍历
stackPanes,与当前拖拽元素以外的所有元素逐一检测碰撞,并统一处理样式变化。 - 优化碰撞后的逻辑:移除创建新
Group的操作,保留样式提示即可,如需阻止穿透可调整位置回退逻辑。
修改后的完整代码
import javafx.application.Application; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.geometry.Insets; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.ChoiceBox; import javafx.scene.input.MouseEvent; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; import javafx.scene.layout.StackPane; import javafx.scene.text.Text; import javafx.stage.Stage; import javafx.geometry.Point2D; import java.util.ArrayList; public class CollisionDetection extends Application { final ObjectProperty<Point2D> mousePosition = new SimpleObjectProperty<>(); Group gp = new Group(); ArrayList<StackPane> stackPanes = new ArrayList<>(); @Override public void start(Stage stage) throws Exception { Scene sc = new Scene(gp, 700, 300); Button btn = new Button(); btn.setText("CREATE REct"); btn.setOnAction(event -> { Rectangle r = createRect(0); r.setFill(Color.RED); Text text = new Text("."); StackPane stack = new StackPane(); StackPane.setMargin(r, new Insets(8, 8, 8, 8)); stack.getChildren().addAll(r, text); ChoiceBox<String> dropdown = new ChoiceBox<>(); stack.getChildren().add(dropdown); makeDraggable(stack); addToRectCollection(stack); gp.getChildren().add(stack); }); gp.getChildren().add(btn); stage.setScene(sc); stage.show(); } private void addToRectCollection(StackPane r) { stackPanes.add(r); } private void makeDraggable(StackPane draggedPane) { draggedPane.setOnMousePressed((MouseEvent event) -> { mousePosition.set(new Point2D(event.getSceneX(), event.getSceneY())); // 重置所有元素样式为默认 stackPanes.forEach(pane -> pane.setStyle("-fx-background-color: #ffffff")); }); draggedPane.setOnMouseDragged((MouseEvent event) -> { double deltaX = event.getSceneX() - mousePosition.get().getX(); double deltaY = event.getSceneY() - mousePosition.get().getY(); // 记录拖拽前的位置,用于碰撞后回退 double prevX = draggedPane.getLayoutX(); double prevY = draggedPane.getLayoutY(); // 更新拖拽位置 draggedPane.setLayoutX(prevX + deltaX); draggedPane.setLayoutY(prevY + deltaY); mousePosition.set(new Point2D(event.getSceneX(), event.getSceneY())); boolean hasCollision = false; // 遍历所有元素检测碰撞 for (StackPane pane : stackPanes) { if (!draggedPane.equals(pane)) { boolean intersect = draggedPane.getBoundsInParent().intersects(pane.getBoundsInParent()); if (intersect) { pane.setStyle("-fx-background-color: #6a5246"); draggedPane.setStyle("-fx-background-color: #6a5246"); hasCollision = true; // 回退到拖拽前的位置,阻止穿透 draggedPane.setLayoutX(prevX); draggedPane.setLayoutY(prevY); } } } // 无碰撞时重置当前拖拽元素样式 if (!hasCollision) { draggedPane.setStyle("-fx-background-color: #ffffff"); } }); } public Rectangle createRect(int x) { Rectangle r = new Rectangle(); r.setX(x); r.setY(60); r.setWidth(300); r.setHeight(150); return r; } public static void main(String[] args) { launch(args); } }
内容的提问来源于stack exchange,提问作者user12420288
相关产品推荐
相关产品推荐

