检查形状与JavaFX的碰撞
我正在尝试做一些碰撞检测。 对于这个testing,我使用简单的矩形Shape
,并检查它们的Bound
,来判断它们是否相互碰撞。 虽然检测不能按预期工作。 我试图用不同的方式来移动对象(relocate,setLayoutX,Y)和不同的绑定检查(boundsInLocal,boundsInParrent等),但我仍然无法得到这个工作。 正如你所看到的,检测只适用于一个对象,即使你有三个对象只有一个检测到碰撞。 这是一些演示问题的工作代码:
import javafx.application.Application; import javafx.event.EventHandler; import javafx.scene.Cursor; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.input.MouseEvent; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; import javafx.stage.Stage; import java.util.ArrayList; public class CollisionTester extends Application { private ArrayList<Rectangle> rectangleArrayList; public static void main(String[] args) { launch(args); } public void start(Stage primaryStage) { primaryStage.setTitle("The test"); Group root = new Group(); Scene scene = new Scene(root, 400, 400); rectangleArrayList = new ArrayList<Rectangle>(); rectangleArrayList.add(new Rectangle(30.0, 30.0, Color.GREEN)); rectangleArrayList.add(new Rectangle(30.0, 30.0, Color.RED)); rectangleArrayList.add(new Rectangle(30.0, 30.0, Color.CYAN)); for(Rectangle block : rectangleArrayList){ setDragListeners(block); } root.getChildren().addAll(rectangleArrayList); primaryStage.setScene(scene); primaryStage.show(); } public void setDragListeners(final Rectangle block) { final Delta dragDelta = new Delta(); block.setOnMousePressed(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent mouseEvent) { // record a delta distance for the drag and drop operation. dragDelta.x = block.getTranslateX() - mouseEvent.getSceneX(); dragDelta.y = block.getTranslateY() - mouseEvent.getSceneY(); block.setCursor(Cursor.NONE); } }); block.setOnMouseReleased(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent mouseEvent) { block.setCursor(Cursor.HAND); } }); block.setOnMouseDragged(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent mouseEvent) { block.setTranslateX(mouseEvent.getSceneX() + dragDelta.x); block.setTranslateY(mouseEvent.getSceneY() + dragDelta.y); checkBounds(block); } }); } private void checkBounds(Rectangle block) { for (Rectangle static_bloc : rectangleArrayList) if (static_bloc != block) { if (block.getBoundsInParent().intersects(static_bloc.getBoundsInParent())) { block.setFill(Color.BLUE); //collision } else { block.setFill(Color.GREEN); //no collision } } else { block.setFill(Color.GREEN); //no collision -same block } } class Delta { double x, y; } }
看起来你的checkBounds例程中有一个轻微的逻辑错误 – 你正确地检测到冲突(基于边界),但是当你在同一个例程中执行后续的冲突检查时,它将覆盖你的块的填充。
尝试这样的事情 – 它添加了一个标志,以便例程不会“忘记”发现了碰撞:
private void checkBounds(Shape block) { boolean collisionDetected = false; for (Shape static_bloc : nodes) { if (static_bloc != block) { static_bloc.setFill(Color.GREEN); if (block.getBoundsInParent().intersects(static_bloc.getBoundsInParent())) { collisionDetected = true; } } } if (collisionDetected) { block.setFill(Color.BLUE); } else { block.setFill(Color.GREEN); } }
请注意,您正在执行的检查(基于父项中的界限)将报告包含同一父组内的节点的可见边界的矩形的交点。
替代实现
如果您需要的话,我更新了您的原始样本,以便能够根据节点的视觉形状而不是视觉形状的边框进行检查。 这使您可以准确检测非矩形形状(如圆形)的碰撞。 关键是Shape.intersects(shape1,shape2)方法。
import javafx.application.Application; import javafx.event.EventHandler; import javafx.scene.*; import javafx.scene.input.MouseEvent; import javafx.scene.paint.Color; import javafx.stage.Stage; import java.util.ArrayList; import javafx.scene.shape.*; public class CircleCollisionTester extends Application { private ArrayList<Shape> nodes; public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { primaryStage.setTitle("Drag circles around to see collisions"); Group root = new Group(); Scene scene = new Scene(root, 400, 400); nodes = new ArrayList<>(); nodes.add(new Circle(15, 15, 30)); nodes.add(new Circle(90, 60, 30)); nodes.add(new Circle(40, 200, 30)); for (Shape block : nodes) { setDragListeners(block); } root.getChildren().addAll(nodes); checkShapeIntersection(nodes.get(nodes.size() - 1)); primaryStage.setScene(scene); primaryStage.show(); } public void setDragListeners(final Shape block) { final Delta dragDelta = new Delta(); block.setOnMousePressed(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent mouseEvent) { // record a delta distance for the drag and drop operation. dragDelta.x = block.getLayoutX() - mouseEvent.getSceneX(); dragDelta.y = block.getLayoutY() - mouseEvent.getSceneY(); block.setCursor(Cursor.NONE); } }); block.setOnMouseReleased(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent mouseEvent) { block.setCursor(Cursor.HAND); } }); block.setOnMouseDragged(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent mouseEvent) { block.setLayoutX(mouseEvent.getSceneX() + dragDelta.x); block.setLayoutY(mouseEvent.getSceneY() + dragDelta.y); checkShapeIntersection(block); } }); } private void checkShapeIntersection(Shape block) { boolean collisionDetected = false; for (Shape static_bloc : nodes) { if (static_bloc != block) { static_bloc.setFill(Color.GREEN); Shape intersect = Shape.intersect(block, static_bloc); if (intersect.getBoundsInLocal().getWidth() != -1) { collisionDetected = true; } } } if (collisionDetected) { block.setFill(Color.BLUE); } else { block.setFill(Color.GREEN); } } class Delta { double x, y; } }
示例程序输出。 在示例中,圆圈已被拖动,用户正在拖动已标记为与另一个圆圈相撞的圆圈(通过将其绘成蓝色) – 为了演示目的,仅当前正在拖动的圆圈标记了碰撞颜色。
基于其他问题的评论
我在之前的评论中发布到交集演示应用程序的链接是为了说明各种边界types的使用,而不是作为特定types的碰撞检测示例。 对于您的用例,您不需要更改侦听器的额外复杂性,并检查各种不同types的边界types – 只需要安装一种types就足够了。 大多数碰撞检测只会对可视边界的交集感兴趣,而不是其他JavaFX边界types,例如节点的布局边界或局部边界。 所以你可以:
- 检查
getBoundsInParent
交集(正如你在原始问题中所做的那样),该交集在最小的矩形框上工作,该框将包含节点OR的视觉末端 - 如果您需要根据节点的可视形状而不是可视形状的边框进行检查
Shape.intersect(shape1, shape2)
请使用Shape.intersect(shape1, shape2)
例程。
我应该使用setLayoutX或translateX矩形
layoutX和layoutY属性用于定位或布局节点。 translateX和translateY属性用于临时更改节点的可视位置(例如节点正在进行animation时)。 对于你的例子,虽然两个属性都可以工作,但是使用布局属性可能比翻译属性更好,那样的话,如果你真的想在节点上运行类似于TranslateTransition的东西, end转换值应该是这些值将相对于节点的当前布局位置而不是父组中的位置。
另一种方法是在样本中使用这些布局和坐标转换,如果在拖拽操作过程中,您有像ESC一样的取消操作。 您可以将layoutX,Y设置到您的节点的初始位置,启动一个拖放操作来设置translateX,Y值,如果用户按下ESC,则将translateX,Y设置回0以取消拖动操作,或者如果用户释放鼠标将layoutX,Y设置为layoutX,Y + translateX,Y,并将translateX,Y设置回0.想法是,翻译是用于从其原始布局位置临时修改节点的视觉坐标的值。
即使圆圈是animation的,交叉的工作吗? 我的意思是不用鼠标拖动圆圈,如果我让它们随机移动,会发生什么。 在这种情况下颜色会改变吗?
要做到这一点,只需改变碰撞检测函数被调用的地方,并调用碰撞处理程序。 而不是根据鼠标拖动事件(如上面的示例)检查交集,而是检查每个节点的boundsInParentProperty()
的更改侦听器内的冲突。
block.boundsInParentProperty().addListener((observable, oldValue, newValue) -> checkShapeIntersection(block) );
注意:如果你有很多的animation形状,那么每当一个节点移动时(比如上面的boundsInParentProperty变化监听器),在游戏循环中每帧检查一次碰撞将比运行碰撞检查更有效率。