【问题标题】:Java collision detection between two Shape objects?两个Shape对象之间的Java碰撞检测?
【发布时间】:2013-03-19 10:08:42
【问题描述】:

我想知道判断 Shape 对象是否与另一个形状相交的最佳方法。 目前我在我的游戏中进行了碰撞检测,只要它涉及与矩形相交的形状,反之亦然。我遇到的问题是 Shape 类中的 intersects() 方法只能将 Rectangle 或 Point 作为参数,而不是另一个 Shape。有没有一种有效的方法来测试两个 Shape 对象是否以任何方式重叠? 我尝试的一种方法是使用 for 循环生成一个点区域来测试它们是否在形状中,然后构建一个 Point 对象数组以发送到另一个形状进行测试,但这显着降低了我的帧速率,因为所有不必要的比较。

我在这里寻找并寻找类似的东西,但没有真正找到任何东西。如果重复,请提前道歉。

【问题讨论】:

  • 是否只考虑形状的边界框就足以满足复杂的形状?如果是这样,那么它很简单。如果不是,那我觉得有点棘手……
  • 不,边界框不起作用。它必须是形状的实际周长。我尝试使用 getBounds() 方法,但是如果您尝试将角色沿对角线移动越过墙壁,即使精灵没有撞到墙壁,它们也会卡住。

标签: java 2d collision shape intersection


【解决方案1】:

未测试,但为什么不测试:

import java.awt.geom.Area;

...

public static boolean testIntersection(Shape shapeA, Shape shapeB) {
   Area areaA = new Area(shapeA);
   areaA.intersect(new Area(shapeB));
   return !areaA.isEmpty();
}

Area 实现了 Shape,但添加了一些可能有用的方法

【讨论】:

  • 这不会产生很多垃圾吗?
  • @TastyLemons 我怀疑一个 Area 实例使用了非常多的堆空间,并且由于“相交”操作改变了该区域,因此尝试重用它不会有太多好处。无论哪种方式,JVM 的 GC 策略都旨在更好地处理短期对象而不是长期对象:programmers.stackexchange.com/questions/149563/…
  • 我只是想如果你每次运行这个方法。对于几个对象,这可能会导致一些问题。
  • @TastyLemons 这是一个很好的观点,在 GC 暂停可能对您的程序非常有害的低延迟场景中,分析此代码并尝试确定内存的使用方式是值得的。 JVM 可能足够聪明,也可能不够聪明,无法以某种方式优化它;如果有问题,可能值得研究 Java 的各种垃圾收集策略或构建一个完全避开 Area 的解决方案
【解决方案2】:

您也可以使用形状本身的边界,然后比较边界:

public boolean collidesWith(Shape other) {
    return shape.getBounds2D().intersects(other.getBounds2D());
}

这对眼睛好一点。

【讨论】:

  • 这将给出边界框碰撞,而不是提问者想要的形状相交。但是,这很可能比我的答案更高效,这毫无价值。一方面,不需要制作防御性副本,因为这个intersect 方法不会改变形状。此外,此intersect 的合同规定可以降低准确性以提高性能:docs.oracle.com/javase/7/docs/api/java/awt/geom/…
【解决方案3】:

尽管 user2221343 已经回答了 Monkeybro10 的问题,但我认为在某些情况下了解形状的轮廓可能会有所帮助,如果您使用他所描述的技术:

例如,如果您绘制两个多边形,如果它们仅发生在多边形的确切轮廓上,则不会检测到它们的碰撞。仅当包含在多边形轮廓内的区域重叠时,才会检测到碰撞。 如果填充两个多边形,但不绘制它们,即使在可见区域的轮廓上也会检测到碰撞。

我写了一个小例子来说明我的意思。取消注释绘制或填充命令,并通过取消注释给定线将第二个多边形垂直上升一个像素。运行代码并在 JFrame 中观察结果。如果第二个多边形升起,并且两个多边形只能通过“填充”命令可见,则它们与其轮廓相交并检测到碰撞。如果第二个多边形没有上升,并且两个多边形都可以通过“绘制”命令看到,它们与它们的轮廓相交但不会检测到碰撞:

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Polygon;
import java.awt.geom.Area;

import javax.swing.JFrame;

public class Test {

    private JFrame frame;
    private Polygon polygon1;
    private Polygon polygon2;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    Test window = new Test();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the application.
     */
    public Test() {
        initialize();
    }

    /**
     * Initialize the contents of the frame.
     */
    private void initialize() {
        frame = new JFrame(){
            private static final long serialVersionUID = 1L;

            @Override
            public void paint(Graphics g){

                super.paint(g);

                doDrawing(g);

            }
        };
        frame.setBounds(100, 100, 450, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        int nShape1 = 4;
        int xPoly1[] = {30,50,50,30};
        int yPoly1[] = {30,30,50,50};
        polygon1 = new Polygon(xPoly1,yPoly1,nShape1);

        int nShape2 = 4;
        int xPoly2[] = {35,55,55,35};
        int yPoly2[] = {50,50,70,70};

        // uncomment next line to rise second polygon vertically by one pixel
        //yPoly2[] = {49,49,69,69};

        polygon2 = new Polygon(xPoly2,yPoly2,nShape2);
    }
    public synchronized void doDrawing(Graphics g){
        g.setColor(new Color(255,0,0));

        // if you draw the polygon, collision on the exact outline won't be detected.
        // uncomment draw or fill command to see what I mean.
        g.drawPolygon(polygon1);
        g.fillPolygon(polygon1);

        g.setColor(new Color(0,0,255));

        // if you draw the polygon, collision on the exact outline won't be detected.
        // uncomment draw or fill command to see what I mean.
        g.drawPolygon(polygon2);
        g.fillPolygon(polygon2);

        Area area = new Area(polygon1);
        area.intersect(new Area(polygon2));
        if(!area.isEmpty()){
            System.out.println("intersects: yes");
        }
        else{
            System.out.println("intersects: no");
        }
    }

}

【讨论】:

    【解决方案4】:

    如果您认为区域相交太昂贵,您可以先进行边界检查: shapeA.getBounds().intersects(shapeB.getBounds())

    如果通过,则进行区域相交检查。

    if( myShape.getBounds().intersects(otherShape.getBounds()) ){
        Area a = new Area(myShape);
        a.intersect(new Area(otherShape));
        if(!a.isEmpty()){
            // do something
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多