javafx-2 - 使用JavaFX检查形状碰撞

标签 javafx-2 javafx collision-detection

我正在尝试进行一些碰撞检测。对于此测试,我使用简单的矩形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);
  }
}

请注意,您正在执行的检查(基于父级的边界)将报告矩形的交点,该交点将同一父级组中节点的可见边界包围起来。

替代实现

如果需要,我更新了原始样本,以便能够根据Node的视觉形状而不是视觉形状的边界框进行检查。这使您可以准确地检测非矩形形状(例如圆形)的碰撞。关键是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; }
}

示例程序输出。在示例中,圆圈已被拖动,并且用户当前正在拖动一个已标记为与另一个圆圈碰撞的圆圈(通过将其涂成蓝色)-出于演示目的,仅当前拖动的圆圈已标记了碰撞颜色。

基于其他问题的评论

我在先前的评论中张贴到intersection demo application的链接是为了说明各种边界类型的用法,而不是作为特定类型的碰撞检测示例。对于您的用例,您不需要更改侦听器的额外复杂性,也不需要检查各种不同类型的边界类型-仅设置一种类型就足够了。大多数冲突检测只会对可视边界的交集感兴趣,而不是其他JavaFX边界类型,例如节点的布局边界或局部边界。因此,您可以:
  • 检查getBoundsInParent的交集(就像您在原始问题中所做的一样),该交集在最小的矩形框上有效,该矩形框将包含节点的视觉末端或
  • 如果需要基于Node的视觉形状而不是视觉形状的边界框进行检查,请使用Shape.intersect(shape1, shape2)例程。

  • Should I be using setLayoutX or translateX for the rectangle



    layoutX和layoutY属性用于定位或布置节点。 translateX和translateY属性用于临时更改节点的视觉位置(例如,当节点进行动画处理时)。对于您的示例,尽管这两个属性都可以使用,但使用布局属性可能比使用翻译属性更好的形式,这样,如果您确实想在节点上运行诸如TranslateTransition之类的内容,则开始和结束的位置将更加明显。最终转换值应为那些值将相对于节点的当前布局位置而不是相对于父组中的位置。

    您可以使用这些布局并在样本中一并转换坐标的另一种方法是,在拖动操作过程中是否需要取消诸如ESC之类的功能。您可以将layoutX,Y设置为节点的初始位置,启动拖动操作以设置translateX,Y值,如果用户按ESC,则将translateX,Y设置回0以取消拖动操作,或者如果用户释放鼠标将layoutX,Y设置为layoutX,Y + translateX,Y,并将translateX,Y设置回0。其想法是平移值是用于从其原始布局位置临时修改节点的视觉坐标的值。

    will the intersect work even though the circles are animated? I mean without dragging the circle by mouse, what will happen if I made them to move around randomly. Will the colour change in this case also?



    为此,只需更改调用碰撞检测函数和调用碰撞处理程序的位置。而不是根据鼠标拖动事件检查交叉点(如上面的示例),而是检查每个节点的 boundsInParentProperty() 上的更改侦听器内是否存在冲突。
    block.boundsInParentProperty().addListener((observable, oldValue, newValue) -> 
            checkShapeIntersection(block)
    );
    

    注意:如果您有许多要动画的形状,则在game loop中每帧检查一次碰撞比在任何节点移动时运行一次碰撞检查更为有效(就像上面的boundsInParentProperty更改侦听器中所做的那样)。

    关于javafx-2 - 使用JavaFX检查形状碰撞,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15013913/

    相关文章:

    JavaFX如何将对话框/警报带到屏幕前面

    jQuery 碰撞

    java - 在 swing 中显示 HTML5

    java - 使用正在运行的 JavaFX 应用程序,打开一个带有自己的独立 Controller 类的新窗口

    java - 如何更改 javaFX 中舞台框架的颜色?

    javafx - 有没有办法在javafx中制作图像饱和动画?

    c++ - 理论 - 如何判断元素是否重叠?

    java - 如何正确检查两个移动椭圆(圆形)之间的碰撞并更新位置+速度

    JavaFX TableColumn 在可调整大小的列上设置PreferredWidth

    java - 如何避免大量 JavaFX 坐标转换(由 UI 响应引起)