Javafx-2 – use JavaFX to check the collision of shapes

I tried to do some collision detection For this test, I use simple rectangular shapes and check their bound to determine if they conflict with each other Although the test did not meet expectations I have tried to use different methods to move objects (relocation, setlayoutx, y) and different binding checks (boundsinlocal, boundsinparrent, etc.), but I still can't make this work You can see that detection only applies to one object, even if there are three objects, only one object can be detected Here are some working codes that demonstrate the problem:

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,Color.RED));
        rectangleArrayList.add(new Rectangle(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;
    }
}

Solution

It seems that there is a slight logic error in your checkbounds routine - you are correctly detecting collisions (based on boundaries), but subsequent collision checks in the same routine will overwrite the filling of blocks

Try something like this – it adds a flag so that the routine doesn't "forget" to detect a conflict:

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);
  }
}

Note that the check you are doing (based on the boundary in the parent) will report the intersection of rectangles containing the visible boundary of nodes in the same parent group

Alternative execution

If necessary, I updated the original sample so that I can check according to the visual shape of the node, not the bounding box of the visual shape This can accurately detect the collision of non rectangular shape (such as circle) This is shape The key of intersects (shape1, shape2) method

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);

    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; }
}

Sample program output In the example, the circle has been dragged and the user is dragging a circle that has been marked to collide with another circle (by drawing blue) – for demonstration purposes, only the currently dragged circle has a collision color mark

Comments based on other questions

In the previous comments, I posted the link to the intersection demo application to illustrate the use of various boundary types, not as a specific type of collision detection sample For your use case, you don't need to change the other complexity of the listener and check for different types of boundary types - just one type Most collision detection is only interested in the intersection of visual boundaries, rather than other JavaFX boundary types, such as node layout boundaries or local boundaries So you can:

>Check the intersection of getboundsinparent (as you did in the original problem), which works on the smallest rectangular box that will contain the node's visual limbs > if you need to check according to the node's visual shape instead of the visual shape's border, use shape Intersect (shape1, shape2) routine

The layoutx and layouty properties are used to locate or layout nodes The translatex and translatey attributes are designed to temporarily change the visual position of a node (for example, when the node is animating) For your example, although any attribute can work normally, this is the better form of using layout attributes than translation versions. If you want to run a method like translatetransition on a node, the start and end conversion values should be those values that will be relative to the current layout location of the node rather than the location in the parent group

Another way you can use these layouts and translate coordinates together in the example is if you have some coordinates similar to ESC to cancel during the drag operation You can set layoutx, y as the initial position of the node, start the drag operation, set the translatex, y value, if the user presses ESC, set translatex, y to 0 to cancel the drag operation, or if the user releases the mouse, set layoutx, y to layoutx, y, translatex, y and set translatex, y to 0 The idea is that the transformation value is used to temporarily modify the visual coordinates of the node from its original layout position

To do this, simply change the location where the collision detection function is called and call the collision handler Instead of checking intersections based on mouse drag events (as in the above example), check for conflicts in the change listener on each node's boundsinparentproperty()

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

Note: if you have many shapes that are animated, it is more effective to check the frame once per frame in the game loop than to perform conflict checking whenever any node moves (as done in the boundsinparentproperty change listener above)

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>