当前位置: 首页 > 知识库问答 >
问题:

使用JavaFX检查形状的碰撞

宇文曦
2023-03-14

我正在尝试做一些碰撞检测。对于这个测试,我使用了一个简单的矩形形状,并检查它们的边界,以确定它们是否碰撞。尽管检测没有如预期那样工作。我尝试过使用不同的方法来移动对象(重定位、设置布局X、Y)以及不同的绑定检查(boundsInLocal、BoundsParrent等),但我仍然无法做到这一点。如您所见,检测仅适用于一个对象,即使有三个对象,也只有一个对象检测碰撞。这是一些演示问题的工作代码:

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

共有1个答案

易和怡
2023-03-14

看起来您的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);
  }
}

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

替代实施

如果需要,我更新了原始示例,以便它能够基于节点的视觉形状而不是视觉形状的边界框进行检查。这样可以准确检测非矩形形状(如圆形)的碰撞。关键是形状。与(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边界类型,例如节点的布局边界或局部边界。因此,您可以:

    < li >检查< code>getBoundsInParent(如您在原始问题中所做的那样)的交集,它作用于将包含节点可视endpoint的最小矩形框,或者 < li >如果需要根据节点的可视形状而不是可视形状的边界框进行检查,请使用< code>Shape.intersect(shape1,shape2)例程。

我应该为矩形使用setLayoutX还是translateX

layoutX和layoutY属性用于定位或布局节点。translateX和translateY属性用于临时更改节点的可视位置(例如,当节点正在播放动画时)。对于您的示例,虽然这两个属性都可以使用,但使用布局属性可能比translate属性更好,这样,如果您确实想在节点上运行TranslateTransition之类的操作,那么开始和结束translate值应该是什么会更明显,因为这些值将相对于节点的当前布局位置,而不是相对于父组中的位置。

在示例中使用这些布局并串联转换坐标的另一种方法是,如果在拖动操作过程中要取消 ESC 之类的内容。您可以将 layoutX,Y 设置为节点的初始位置,启动设置 translateX,Y 值的拖动操作,如果用户按 ESC,则将 translateX,Y 设置回 0 以取消拖动操作,或者如果用户将鼠标集 layoutX,Y 释放到 layoutX,Y translateX,Y 并设置 translateX,Y 返回到 0。这个想法是,转换是值用于从其原始布局位置临时修改节点的视觉坐标。

即使圆圈是动画的,intersect还能工作吗?我的意思是不用鼠标拖动圆圈,如果我让它们随意移动会发生什么。在这种情况下颜色也会改变吗?

为此,只需更改调用冲突检测函数和调用冲突处理程序的位置。不是基于鼠标拖动事件检查交叉点(如上例),而是检查每个节点的< code > boundsInParentProperty()上的更改侦听器中的冲突。

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

注意:如果您有很多形状正在动画化,那么在游戏循环中每帧检查一次碰撞将比在任何节点移动时运行碰撞检查更有效(如上面的边界InParentProperty更改侦听器中所做的那样)。

有关处理非矩形形状输入的其他信息

对于输入检测而非碰撞检测,因此与您的问题没有直接关系,如果您需要鼠标或触摸与非矩形节点的交互,请查看< code>node.pickOnBounds设置。

 类似资料:
  • 我想做一个游戏,有每一个水平加载从一个图像。我想在Photoshop中把整个关卡画出来,然后把它设置为背景并允许玩家走过去。我想要另一个看不见的图像超过顶部,这将是黑色的所有地方,我想要碰撞。 我不想使用瓷砖的原因,这是更容易与矩形碰撞和诸如此类,因为将有复杂的角,并不是所有将是矩形。 这是一个好主意吗?有没有可能轻松做到?这将是一个巨大的CPU拥有者还是有更好的方法做到这一点?

  • 我知道如何检查一个圆是否要与一个正方形相撞,我知道如何检测一个正方形是否要与一个正方形相撞,但是如果一个多边形要与一个正方形相撞,我该如何检测呢? 或者更好的是,当一个多边形将要与另一个多边形碰撞时。 或者更好的是,当由非直线组成的形状与另一个类似的形状(多边形或圆形/矩形)发生冲突时 有什么方法可以得到一个图形可能占用的像素和另一个图形可能占用的像素,并检查它们是否相同? 我希望有一些解决方案不

  • 创建自定义按钮形状很容易完成,但是如何确保新形状也是按钮本身的“碰撞框”? 在本例中,我创建了两个 hexagnol 形状的按钮,并将它们正确对齐。问题是按钮的碰撞框仍然是矩形的,当您将鼠标从上部按钮移动到下部按钮时,您会注意到碰撞框是严格矩形的,并且使自定义形状变得毫无用处。 有没有办法创建自定义碰撞形状或碰撞检查? 完整的工作示例:

  • 所以我明白,我现在没有用最好的方式来编写这个代码;这是一种测试运行。我正在尝试使用矩形和intersects属性进行墙碰撞(抱歉,如果我没有使用正确的术语)。到目前为止,我有2个矩形在屏幕上。1玩家控制和游戏中与之冲突的其他玩家。当它们碰撞时,玩家停止移动。问题是,如果玩家试图移动到矩形,而他们已经碰撞,那么玩家不能在垂直于移动的任何方向移动,即如果玩家拿着右箭头键移动到矩形,那么他们不能向上或向

  • 我是JavaFX的新手,在碰撞检测方面遇到了麻烦。我在一个八边形内有一个圆圈,我希望通过从墙壁上反弹来留在八边形内。目前,如果我在尝试移动圆圈时检查按键事件内部的碰撞,它往往会跳来跳去,但是,如果我将碰撞检查放在按键事件之外,则不会发生任何事情。目前,我的代码只检查与左右墙的碰撞。这是我的代码:

  • 当前有三种类型的碰撞形状: 圆形:快速简单的碰撞形状 线段:主要作为静态形状。可以倾斜以便给之一个厚度。 凸多边形:最慢,但却为最灵活的碰撞形状。 如果你愿意,你可以在一个刚体上添加任意数量的形状。这就是为什么两种类型(形状和刚体)是分离开的。这将会给你足够的灵活性来给相同对象的不同区域提供不同的摩擦力、弹性以及回调值。 当创建不同类型的形状的时候,你将永远得到一个cpShape*指针返回。这是因