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

libgdx中的圆-矩形碰撞侧检测

暨曾笑
2023-03-14

我花了数小时寻找解决方案:我正在用libgdx开发一个自上而下的小游戏(可能这与我使用的引擎有关)。现在我必须在我的角色(圆形)和墙(矩形)之间实现碰撞检测。如果可以滑动,我希望角色在碰撞时沿着墙滑动。让我解释一下:

  • 如果我向上移动45度,我可能会撞到墙的下面、左边或角落。
  • 如果我与左边相撞,我想停止x运动,只向上移动。如果我离开墙壁,那么我想继续向上移动。与下侧相同(停止y运动)
  • 如果我与角落相撞,我想停止移动(滑动不可能)。

我正在做的实际上是检查矩形的左线是否与我的圆相交。然后我检查墙壁和我的圆的左线与墙壁和我的圆的底线之间的交叉点。根据哪个交叉点发生,我设置我的圆的x/y位置,并将x/y速度设置为0。问题是,大多数时候不会发生碰撞,但会发生重叠。所以底部检查返回true,即使实际上圆圈只会与右侧碰撞。在这种情况下,两个交叉点测试都将返回true,我将重置两个速度,就像在角落碰撞中一样。我如何解决这个问题?还有更好的方法来检测碰撞和碰撞的边或角吗?我不需要精确的碰撞点,只需要矩形的边。

编辑:我必须说,矩形并不是平行于x轴旋转的。

共有2个答案

倪灿
2023-03-14

我假设你通过计算圆心与直线的距离来确定碰撞。我们可以简化这种情况,如果两个距离都等于且小于半径,则说明圆与角碰撞。平等当然应该有宽容。

更现实的方法是考虑x、y速度,并在等式检查中考虑它,这可能不是必需的。

苏振国
2023-03-14

您可以在下面找到关于圆/矩形碰撞的解释,但请注意,这种类型的碰撞可能不是您需要的。例如,如果您的角色有一个矩形边框,则算法将更简单、更快。即使您使用的是圆,也可能有一种更简单的方法足以满足您的目的。

我考虑过为此编写代码,但这需要很长时间,所以这里只是一个解释:

在所有这些之后,碰撞代码应该很简单:

// x, y - character coordinates
// r - character circle radius
// speedX, speedY - character speed
// intersectionX, intersectionY - intersection coordinates
// left, right, bottom, top - wall rect positions

// I strongly recomment using a const "EPSILON" value
// set it to something like 1e-5 or 1e-4
// floats can be tricky and you could find yourself on the inside of the wall
// or something similar if you don't use it :)

if (isLeftCollision) {
    x = intersectionX - EPSILON;
    if (speedX > 0) {
        speedX = 0;
    }
} else if (isRightCollision) {
    x = intersectionX + EPSILON;
    if (speedX < 0) {
        speedX = 0;
    }
}

if (isBottomCollision) {
    y = intersectionY - EPSILON;
    if (speedY > 0) {
        speedY = 0;
    }
} else if (isTopCollision) {
    y = intersectionY + EPSILON;
    if (speedY < 0) {
        speedY = 0;
    }
}

[更新]

这是一个简单的,我相信有效的段-aabb交集的实现,应该足以满足您的目的。这是一个稍微修改的Cohen-Sutherland算法。你也可以看看这个答案的第二部分。

public final class SegmentAabbIntersector {

    private static final int INSIDE = 0x0000;
    private static final int LEFT = 0x0001;
    private static final int RIGHT = 0x0010;
    private static final int BOTTOM = 0x0100;
    private static final int TOP = 0x1000;

    // Cohen–Sutherland clipping algorithm (adjusted for our needs)
    public static boolean cohenSutherlandIntersection(float x1, float y1, float x2, float y2, Rectangle r, Vector2 intersection) {

        int regionCode1 = calculateRegionCode(x1, y1, r);
        int regionCode2 = calculateRegionCode(x2, y2, r);

        float xMin = r.x;
        float xMax = r.x + r.width;
        float yMin = r.y;
        float yMax = r.y + r.height;

        while (true) {
            if (regionCode1 == INSIDE) {
                intersection.x = x1;
                intersection.y = y1;
                return true;
            } else if ((regionCode1 & regionCode2) != 0) {
                return false;
            } else {
                float x = 0.0f;
                float y = 0.0f;

                if ((regionCode1 & TOP) != 0) {
                    x = x1 + (x2 - x1) / (y2 - y1) * (yMax - y1);
                    y = yMax;
                } else if ((regionCode1 & BOTTOM) != 0) {
                    x = x1 + (x2 - x1) / (y2 - y1) * (yMin - y1);
                    y = yMin;
                } else if ((regionCode1 & RIGHT) != 0) {
                    y = y1 + (y2 - y1) / (x2 - x1) * (xMax - x1);
                    x = xMax;
                } else if ((regionCode1 & LEFT) != 0) {
                    y = y1 + (y2 - y1) / (x2 - x1) * (xMin - x1);
                    x = xMin;
                }

                x1 = x;
                y1 = y;
                regionCode1 = calculateRegionCode(x1, y1, r);
            }
        }
    }

    private static int calculateRegionCode(double x, double y, Rectangle r) {
        int code = INSIDE;

        if (x < r.x) {
            code |= LEFT;
        } else if (x > r.x + r.width) {
            code |= RIGHT;
        }

        if (y < r.y) {
            code |= BOTTOM;
        } else if (y > r.y + r.height) {
            code |= TOP;
        }

        return code;
    }
}

以下是一些代码示例用法:

public final class Program {

    public static void main(String[] args) {

        float radius = 5.0f;

        float x1 = -10.0f;
        float y1 = -10.0f;
        float x2 = 31.0f;
        float y2 = 13.0f;

        Rectangle r = new Rectangle(3.0f, 3.0f, 20.0f, 10.0f);
        Rectangle expandedR = new Rectangle(r.x - radius, r.y - radius, r.width + 2.0f * radius, r.height + 2.0f * radius);

        Vector2 intersection = new Vector2();

        boolean isIntersection = SegmentAabbIntersector.cohenSutherlandIntersection(x1, y1, x2, y2, expandedR, intersection);
        if (isIntersection) {
            boolean isLeft = intersection.x < r.x;
            boolean isRight = intersection.x > r.x + r.width;
            boolean isBottom = intersection.y < r.y;
            boolean isTop = intersection.y > r.y + r.height;

            String message = String.format("Intersection point: %s; isLeft: %b; isRight: %b; isBottom: %b, isTop: %b",
                    intersection, isLeft, isRight, isBottom, isTop);
            System.out.println(message);
        }

        long startTime = System.nanoTime();
        int numCalls = 10000000;
        for (int i = 0; i < numCalls; i++) {
            SegmentAabbIntersector.cohenSutherlandIntersection(x1, y1, x2, y2, expandedR, intersection);
        }
        long endTime = System.nanoTime();
        double durationMs = (endTime - startTime) / 1e6;

        System.out.println(String.format("Duration of %d calls: %f ms", numCalls, durationMs));
    }
}

这是我执行此操作得到的结果:

Intersection point: [4.26087:-2.0]; isLeft: false; isRight: false; isBottom: true, isTop: false
Duration of 10000000 calls: 279,932343 ms

请注意,这是i5-2400 CPU上的桌面性能。在Android设备上可能会慢得多,但我相信这已经足够了<我只是粗略地测试了一下,所以如果你发现任何错误,请告诉我。

如果你使用这种算法,我相信你不需要对起点在扩展墙矩形的角落的情况进行特殊处理,因为在这种情况下,你将在线开始时获得交点,冲突检测过程将继续到下一步(线圆碰撞)。

 类似资料:
  • 我正在编写一个游戏,涉及碰撞的一个移动的圆,由用户控制,和一个移动的矩形,由计算机控制。 完整的代码可以在这里找到:游戏 我在圆和矩形之间的碰撞检测方面遇到了麻烦。当矩形是静态的,碰撞检测工作完美。当圆和矩形的边缘在任一边接触时,程序就会按照它应该的方式进行操作。 这是碰撞检测功能。 谢谢。

  • 我在游戏中使用libGDX库。I用于检测两个矩形之间碰撞检测的用户重叠方法。 我想检测矩形上的触摸侧(顶部、底部、左侧或右侧): 谁能给我这个代码,但这需要快速的方法。 谢啦

  • 我一直在尝试在Libgdx中找到/创建矩形的碰撞检测,但似乎什么都做不到。我有一个叫bucket的矩形,宽度和高度为64,还有一个叫wall的矩形,宽度和高度为64。我试着让玩家不穿过矩形,可以在粘墙的同时继续移动,而不需要通过相位或随机传送。我的方法在有1个块的情况下有效,但当有多个块的时候,它就会中断,不起作用。 我知道这种方法很难看但这只是实验 如果有人能给我指出正确的方向或分享一些对我有帮

  • 我有一个问题,碰撞检测一个圆和一个矩形。我曾尝试用勾股定理来解决这个问题。但所有查询都不起作用。矩形与圆的矩形包围盒发生碰撞。

  • 我正在为我的播放器和bullet类创建一些围绕精灵的矩形,以使用LibGDX的Intersector类中的重叠方法检测碰撞。 我有一个问题: 当我实例化Player和Bullet时,我使用sprite.getBoundingRectangle()在精灵周围创建一个边界框,它返回一个Rectangle对象。我在主类的其他地方更新这些的移动。 当我更新子弹/玩家精灵的移动时,我是否也需要更新子弹/玩家

  • 我使用libgdx为一个项目制作了一个2D游戏,我的碰撞检测技术基于。在矩形中重叠方法,效果良好。我想了解其背后的实现,如果我要将其与intersects方法并行,我知道它是如何工作的。但这是唯一的吗?我读到,在离散碰撞检测方法中,不仅需要计算实体是否发生碰撞,还需要计算它们发生碰撞的“程度”(碰撞深度),并使用轴对齐的边界盒算法将实体推回。首先,我对矩形重叠技术是否与libgdx中的AAAB相同