检查使用JavaFX形状的碰撞(Checking Collision of Shapes with

2019-07-20 08:00发布

我试图做一些碰撞检测。 对于这个测试我使用简单的矩形Shape ,并检查他们的Bound ,推测如果他们碰撞。 虽然检测不正常工作。 我已经用不同的方式来移动物体(搬迁,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;
    }
}

Answer 1:

看起来你在你的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; }
}

示例程序输出。 在样品圈已经上拖动,目前用户在拖动它已被标记为与另一圈碰撞(由画吧蓝色)一个圆圈 - 仅用于演示目的目前正被拖动的圈子有它的撞色标记。

基于其他问题评论

我张贴到链路相交演示应用程序中的现有的评论是说明使用不同边界类型的,而不是作为特定类型的碰撞检测的样品。 为了您的使用情况下,你不需要更改侦听器的额外的复杂性和检查各种不同类型的边界类型的 - 只是解决一个类型就足够了。 大多数碰撞检测只会有兴趣可视边界,而不是其他的JavaFX界类型的交叉点如布局界限或一个节点的本地界限。 所以,你可以:

  1. 检查的交集getBoundsInParent (如你在原来的问题所做的那样),其上最小的矩形框,这将包括该节点的视觉四肢或工程
  2. 使用Shape.intersect(shape1, shape2)如果你需要基于节点的视觉形状,而不是视觉形状的边框,以检查程序。

我应该使用setLayoutX或平移X为矩形

的layoutX和layoutY属性旨在用于定位或铺设节点。 的平移X和translateY属性旨在用于向一个节点的视觉位置临时改变(例如,当节点正经历的动画)。 对于你的榜样,虽然这两个属性会的工作,它可能是更好的形式使用的布局特性比翻译的,这样,如果你是想运行就像一个TranslateTransition节点上,它会更明显发生了什么的开始和结束翻译值应为这些值将是相对于所述节点的当前配置位置,而不是父组中的位置。

你可以使用这些布局并在你的样品中串联转换坐标的另一种方法是,如果你有这样的事情的ESC拖动操作过程中取消。 您可以设置layoutX,Y您节点的初始位置,开始拖动操作这台平移X,Y值,如果用户按下ESC键,设置平移X,Y回0取消拖动操作,或者如果用户释放鼠标设置layoutX,Y向layoutX,Y +平移X,Y和设置平移X,Y回0的想法是,所述翻译是值被用于从它的原始布局位置的节点的视觉坐标的临时修改。

将交叉工作,即使圆圈是动画? 我的意思是没有拖动鼠标圈,如果我让他们随机走动会发生什么。 将颜色在这种情况下也改变吗?

要做到这一点,只需更改其中的碰撞检测函数被调用和冲突处理方法调用。 而不是基于鼠标拖动事件(如上面的示例)交叉检查,而不是检查碰撞上的每个节点的一个变化监听内boundsInParentProperty()

block.boundsInParentProperty().addListener((observable, oldValue, newValue) -> 
        checkShapeIntersection(block)
);

注意:如果你有很多的形状被设计为动画,然后碰撞检查中每帧一次的游戏循环会比运行碰撞检查更有效,只要任何节点移动(如在boundsInParentProperty变化监听器上面完成)。



文章来源: Checking Collision of Shapes with JavaFX