java - 矩形的递归碰撞检测不太有效

标签 java recursion javafx collision-detection

我有一堆大小可变的矩形,它们随机放置在一个区域中。

我正在尝试进行(递归)碰撞检测,通过移动它们的位置来确保它们不会发生碰撞。

但是,仍然有问题(一些矩形仍然发生碰撞),我不知道是什么......可能是我无法正确进行递归。如果有人检查一下,我将不胜感激。

这是代码,只需复制并粘贴并运行它,您就会立即看到结果。需要 JavaFx:

import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

import java.util.*;

public class Example extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    private Pane root = new Pane();

    @Override
    public void start(Stage stage) throws Exception {

        Scene scene = new Scene(root, 800, 600);

        stage.setTitle("Collision Problem");
        stage.setScene(scene);
        stage.setOnCloseRequest(e -> System.exit(0));
        stage.show();

        run();
    }

    private static class Node2D {

        private double x, y, w, h;

        public Node2D() {
            w = randInt(40, 80);
            h = randInt(20, 40);
        }

        public void setPosition(double x, double y) {
            this.x = x;
            this.y = y;
        }

        public double getWidth() {
            return w;
        }

        public double getHeight() {
            return h;
        }
    }

    private static class LayoutEntity {

        private Node2D obj = new Node2D();

        private double x, y;

        public void setLocationInLayout(double x, double y) {
            this.x = x;
            this.y = y;
        }

        public double getXInLayout() {
            return x;
        }

        public double getYInLayout() {
            return y;
        }
    }

    public void run() {

        LayoutEntity[] entitiesToLayout = new LayoutEntity[100];
        for (int i = 0; i < entitiesToLayout.length; i++)
            entitiesToLayout[i] = new LayoutEntity();

        randomizePositions(entitiesToLayout);

        collisionDetection(entitiesToLayout);

        for (LayoutEntity entity : entitiesToLayout) {
            Node2D node = entity.obj;
            node.setPosition(entity.getXInLayout(), entity.getYInLayout());
        }

        // Print possible collisions
        displayNodes(entitiesToLayout);
    }

    private void displayNodes(LayoutEntity[] all) {
        for (LayoutEntity entity : all) {
            Node2D node = entity.obj;

            Rectangle rect = new Rectangle(node.x, node.y, node.w, node.h);
            rect.setStroke(Color.BLACK);
            rect.setFill(null);

            root.getChildren().add(rect);
        }
    }

    private void randomizePositions(LayoutEntity[] entities) {
        for (LayoutEntity entity : entities) {
            entity.setLocationInLayout(randInt(0, 512), randInt(0, 512));
        }
    }

    private void collisionDetection(LayoutEntity[] entities) {
        collisionDetection(Arrays.asList(entities));
    }

    private void collisionDetection(Collection<LayoutEntity> c) {

        List<LayoutEntity> collisions = new ArrayList<>();

        for (LayoutEntity e1 : c) {
            for (LayoutEntity e2 : c) {
                if (e1 == e2)
                    continue;
                boolean collides = checkAndResolveCollision(e1, e2);
                if (collides)
                    collisions.add(e1);
            }
        }

        checkRecursively(collisions, c);
    }

    private void checkRecursively(List<LayoutEntity> collisions,
            Collection<LayoutEntity> all) {

        if (collisions.isEmpty())
            return;

        for (LayoutEntity e1 : all) {

            for (int i = 0; i < collisions.size(); i++) {

                LayoutEntity e2 = collisions.get(i);

                if (e2 == e1)
                    continue;

                boolean collides = checkAndResolveCollision(e1, e2);
                if (collides) {

                    if (collisions.contains(e1))
                        continue;

                    collisions.add(e1);

                    checkRecursively(collisions, all);
                }
            }
        }
    }

    private boolean checkAndResolveCollision(LayoutEntity e1, LayoutEntity e2) {

        Node2D n1 = e1.obj;

        // Ideally, I also want to add a gap around the boxes
        double extraSpace = 0;

        double w1 = n1.getWidth() + extraSpace * 2;
        double h1 = n1.getHeight() + extraSpace * 2;

        Rectangle2D b1 = new Rectangle2D(e1.getXInLayout() - extraSpace,
                e1.getYInLayout() - extraSpace, w1, h1);

        Node2D n2 = e2.obj;

        double w2 = n2.getWidth() + extraSpace * 2;
        double h2 = n2.getHeight() + extraSpace * 2;

        Rectangle2D b2 = new Rectangle2D(e2.getXInLayout() - extraSpace,
                e2.getYInLayout() - extraSpace, w2, h2);

        if (b1.intersects(b2)) {

            Point2D trans = getMinimumTranslation(b1, b2);

            double x = e1.getXInLayout() + trans.getX();
            double y = e1.getYInLayout() + trans.getY();

            e1.setLocationInLayout(x, y);

            return true;
        }
        return false;
    }

    private Point2D getMinimumTranslation(Rectangle2D source, Rectangle2D target) {

        double mtdx, mtdy;

        Point2D amin = new Point2D(source.getMinX(), source.getMinY());
        Point2D amax = new Point2D(source.getMaxX(), source.getMaxY());
        Point2D bmin = new Point2D(target.getMinX(), target.getMinY());
        Point2D bmax = new Point2D(target.getMaxX(), target.getMaxY());

        double left = (bmin.getX() - amax.getX());
        double right = (bmax.getX() - amin.getX());
        double top = (bmin.getY() - amax.getY());
        double bottom = (bmax.getY() - amin.getY());

        if (left > 0 || right < 0)
            return Point2D.ZERO;

        if (top > 0 || bottom < 0)
            return Point2D.ZERO;

        if (Math.abs(left) < right)
            mtdx = left;
        else
            mtdx = right;

        if (Math.abs(top) < bottom)
            mtdy = top;
        else
            mtdy = bottom;

        // zero the axis with the largest mtd value.
        if (Math.abs(mtdx) < Math.abs(mtdy))
            mtdy = 0;
        else
            mtdx = 0;

        return new Point2D(mtdx, mtdy);
    }

    private static Random rand = new Random();

    public static int randInt(int min, int max) {
        return rand.nextInt((max - min) + 1) + min;
    }
}

最佳答案

通过阅读你的代码,我可以预测的是,问题出在逻辑上。在 checkAndResolveCollision() 内,当您使用

Node 分配新坐标时
e1.setLocationInLayout(x, y);

您错过了检查分配给该节点的新坐标是否与我们已经针对该节点检查过的任何其他节点重叠。

您必须生成逻辑,无论何时更改节点的坐标,都必须再次针对每个其他节点进行检查

希望有帮助

关于java - 矩形的递归碰撞检测不太有效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25932254/

相关文章:

javascript - 遍历页面上的所有 <div> 标签

java - EXE4J Jar 到 exe 与 jre 集成导致运行时错误

java - javafx 数轴显示不正确

java - EditText 上的“完成”按钮的监听器?

java - 在 Spring Batch 中使用 getNamedParameterJdbcTemplate() 方法运行 JUnit 时出现问题

java.lang.ArrayIndexOutOfBoundsException : length=1; index=1 at forloop

mysql - SQL/MySQL 递归地从同一个表中提取

java - Gradle 定义测试的安全策略

C++ 递归搜索函数(学校作业)

java - 当循环的类型为 "javafx.scene.control.Tab"时,如何跳过增强型 for 循环中的第一个索引?