使用边界框进行碰撞检测

4

我正在为课程做一个编程项目。我们正在编制打砖块游戏,该游戏包括一个球、一个挡板、四个边缘和砖块。当球与不同物体相撞时,球会弹开,并且每个物体在碰撞时执行不同的操作。以下是我的代码,当前它无法正常工作。我试图使用对象的位置(其中心点)构建有界框,并使用每个边缘的值(顶部、底部、左侧、右侧)来计算是否发生碰撞。我想到可能有两种类型的碰撞,一种是角落相撞,另一种是一个物体撞击另一个物体的中间某个地方。请查看我的代码并在需要时提供帮助。我不知道是否有更好的方法来实现我的想法,但目前我的代码无法正常工作,并且几乎总是返回true。

以下是代码的一部分,它检查每个能够与其他可以碰撞的对象相撞的对象。它还移动每个对象并打开游戏的时钟。

/**
     * Tell the GameWorld that the "game clock" has ticked. A clock tick in the GameWorld has the 
     * following effects: (1) all movable objects are told to update their positions according to there 
     * current heading and speed, (2) the "elapsed game time" is incremented by one and (3) all Items are 
     * checked for a collision.
     */
    public void tickClock() {
        gameClock++;
        Iterator theElements0 = listOfGameObjects.getIterator();
        while (theElements0.hasNext()){
            GameObject gObj = (GameObject) theElements0.getNext();
            if ( gObj instanceof IMovable){
                IMovable mObj = (IMovable)gObj;
                mObj.move(gameClock);
            }
        }
        Iterator theElements1 = listOfGameObjects.getIterator();
        while (theElements1.hasNext()){
            GameObject gObj0 = theElements1.getNext();//get a collidable object.
            if(gObj0 instanceof ICollider){
                ICollider curObj = (ICollider) gObj0;
                //check if this object collides with any OTHER object.
                Iterator theElements2 = listOfGameObjects.getIterator();
                while(theElements2.hasNext()){
                    GameObject gObj1 = theElements2.getNext();
                    if(gObj1 != curObj && gObj1 instanceof ICollider) {
                        ICollider otherObj = (ICollider) gObj1;
                        if (curObj.collidesWith(otherObj)){
                            curObj.handleCollision(otherObj);
                        }
                    }
                }   
            }
        }
        setChanged();
        notifyObservers();
    }

这段代码用于确定对象是否与另一个对象发生碰撞,以及在发生碰撞时采取适当的行动。这段代码是特别针对球形对象的,因此当它撞到砖块时所执行的操作是反转ySpeed值。
public boolean collidesWith(ICollider otherObj) {
        GameObject gObj = (GameObject) otherObj;
        //this collider
        int r1 = (int) (getX() + getWidth()/2);
        int l1 = (int) (getX() - getWidth()/2);
        int t1 = (int) (getY() + getHeight()/2);
        int b1 = (int) (getY() - getHeight()/2);

        //the other collider
        int r2 = (int) (gObj.getX() + gObj.getWidth()/2);
        int l2 = (int) (gObj.getX() - gObj.getWidth()/2);
        int t2 = (int) (gObj.getY() + gObj.getHeight()/2);
        int b2 = (int) (gObj.getY() - gObj.getHeight()/2);

        //corner collision check
        if(r1>l2 && t2>b1 && t1>t2 && b1>b2){
            System.out.println("Corner Collision check 1");
            return true;
        }
        if(r2>l1 && t2>b1 && t1>t2 && b1>b2){
            System.out.println("Corner Collision check 2");
            return true;
        }
        if(r2>l1 && t1>b2 && t2>t1 && b2>b1){
            System.out.println("Corner Collision check 3");
            return true;
        }
        if(r1>l2 && t1>b2 && t2>t1 && b2>b1){
            System.out.println("Corner Collision check 4");
            return true;
        }

        //middle collision check
        if(l1>l2 && r1<r2 && t1<t2 && b1<b2){
            System.out.println("middle collision check 1");
            return true;
        }
        if(l1>l2 && r1<r2 && t1>t2 && b1>b2){
            System.out.println("middle Collision check 2");
            return true;
        }
        if(l1<l2 && r1<r2 && t1<t2 && b1>b2){
            System.out.println("middle Collision check 3");
            return true;
        }
        if(l1>l2 && r1>r2 && t1<t2 && b1>b2){
            return true;
        }

        return false;
    }

    public void handleCollision(ICollider otherObject) {
        if(otherObject instanceof Brick){
            System.out.println("Brick Hit");
            ySpeed = -(ySpeed);
        }
    }

另外,记得你可以在阶段中检查碰撞,这通常比直接检查是否发生碰撞更有效。首先检查某些物体可能会发生碰撞的“松散可能性”,然后再检查它们是否确实发生了碰撞。这是因为大多数情况下,你的物体根本不会接近碰撞。你可以添加代码到你的答案中,检查球是否在一个区域(比如屏幕的下半部分,减去挡板和侧面),在那里肯定不可能发生碰撞,也可以进行短路处理。 - dann.dev
1
你考虑过使用Java 2D API吗?你可以获得一个现成的Rectangle类,内含一个现成的intersects(Rectangle)方法。 :) - ZeroOne
1
请参考 Stack Overflow:如何确定两个矩形是否重叠? - ZeroOne
1个回答

9
你只需要检查边界框的边缘。一些伪代码可能如下所示。
if Rect1[RIGHT] < Rect2[LEFT] or
   Rect1[LEFT] > Rect2[RIGHT] or
   Rect1[TOP] < Rect2[BOTTOM] or
   Rect1[BOTTOM] > Rect2[TOP]
then return false
else return true

这句话的意思是,如果沿着X或Y坐标系统分离盒子可能存在任何间隙,则不可能发生碰撞。这是SAT(分离轴定理)的一个非常简单的版本。
下面是一个图像,直观地展示了这个概念,同样的想法也适用于此处。

Visualisation of SAT

这意味着类似以下内容应该可以工作。请注意,我没有测试过,但可能会指导您朝正确的方向前进。
public boolean collidesWith(ICollider otherObj) {
    GameObject gObj = (GameObject) otherObj;
    //this collider
    int r1 = (int) (getX() + getWidth()/2);
    int l1 = (int) (getX() - getWidth()/2);
    int t1 = (int) (getY() + getHeight()/2);
    int b1 = (int) (getY() - getHeight()/2);

    //the other collider
    int r2 = (int) (gObj.getX() + gObj.getWidth()/2);
    int l2 = (int) (gObj.getX() - gObj.getWidth()/2);
    int t2 = (int) (gObj.getY() + gObj.getHeight()/2);
    int b2 = (int) (gObj.getY() - gObj.getHeight()/2);

    if (r1 < l2 || l1 > r2 || t1 < b2 || b1 > t2)
       return false;
    else
       return true;

    /* Or could be shortened down to
    return !(r1 < l2 || l1 > r2 || t1 < b2 || b1 > t2) */

}

你会说代码大幅减少了;)


我之前尝试过这个。问题在于,当某个对象与游戏世界中的每个对象进行比较时,即使该对象(球)位于屏幕中央且未接触任何物体,此操作始终返回true。如果您想象两个正方形直接相邻但不接触,则r1将小于l2,这是不正确的,因此在我最初发布的示例中,我尝试比较所有对应的边,但也没有成功。 - T. Thomas
抱歉,我认为我打错了。返回值应该相反 :/ 。我已经编辑了答案。 - rflood89

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接