检测矩形与圆形的碰撞

4

我有一个圆,它有一个中心点(Center_X,Center_Y),我想检测一个矩形是否在它的半径(Radius)内。你能告诉我如何完成这个任务吗?我尝试过使用

if (X - Center_X)^2 + (Y - Center_Y)^2 < Radius^2:
        print(1)

然后我尝试画一个圆来覆盖这个区域:

Circle = pygame.draw.circle(Window, Blue, (Center_X, Center_Y), Radius, 0)

但是它似乎排列不齐。我做错了什么吗?


矩形定义了四个角点,因此一般来说,您需要确保它们全部位于圆内。 - martineau
在这种情况下,您的问题标题应更改。您仍然需要潜在地检查所有四个点,但是一旦发现一个点位于圆内,您就可以停止测试。如果任何一个点在内部,则意味着它们正在碰撞。 - martineau
2
@martineau,检查任何4个点是否在圆内对于碰撞检查是不充分的。 - Michael Anderson
2
这不是 https://dev59.com/tXRC5IYBdhLWcg3wJNYN 的重复吗? - nonchip
@nonchip 这可能被视为重复,但有些差异可能使其不是重复,比如这个问题特别涉及Python,而另一个问题涉及几何。 - Edward
显示剩余7条评论
3个回答

3
这里是我在评论中描述的内容,还包括更正处理圆形位于较大矩形内(Michael Anderson在评论中指出)的情况所做的更改:
import math

def collision(rleft, rtop, width, height,   # rectangle definition
              center_x, center_y, radius):  # circle definition
    """ Detect collision between a rectangle and circle. """

    # complete boundbox of the rectangle
    rright, rbottom = rleft + width/2, rtop + height/2

    # bounding box of the circle
    cleft, ctop     = center_x-radius, center_y-radius
    cright, cbottom = center_x+radius, center_y+radius

    # trivial reject if bounding boxes do not intersect
    if rright < cleft or rleft > cright or rbottom < ctop or rtop > cbottom:
        return False  # no collision possible

    # check whether any point of rectangle is inside circle's radius
    for x in (rleft, rleft+width):
        for y in (rtop, rtop+height):
            # compare distance between circle's center point and each point of
            # the rectangle with the circle's radius
            if math.hypot(x-center_x, y-center_y) <= radius:
                return True  # collision detected

    # check if center of circle is inside rectangle
    if rleft <= center_x <= rright and rtop <= center_y <= rbottom:
        return True  # overlaid

    return False  # no collision detected

2
考虑一个小圆圈在一个大盒子里面。这将返回false。 - Michael Anderson
现在你还错过了一个情况,即一个小圆的中心恰好位于盒子的一条边的外侧(因此在该边上有两个交点,但不包括顶点)。 - Michael Anderson
@Michael:你所说的“也”错过是什么意思? - martineau
也许“你仍然没有检测到情况...”更准确。 - Michael Anderson
@Michael:感谢您的澄清。至于新的失败案例,虽然代码可能可以添加以处理它,但在这个时候,我认为更好的做法是使用问题“Circle-Rectangle collision detection (intersection)”中的一个答案。链接 - martineau
感谢您提供这个非常周到、可行的Python函数,它绝对有效。这正是我一直在寻找的! - Edward

1

使用 点到线段的最短距离 中的 dist 函数。

import math

def dist(p1, p2, c): 
    x1,y1 = p1
    x2,y2 = p2
    x3,y3 = c
    px = x2-x1
    py = y2-y1

    something = px*px + py*py

    u =  ((x3 - x1) * px + (y3 - y1) * py) / float(something)

    if u > 1:
        u = 1
    elif u < 0:
        u = 0

    x = x1 + u * px
    y = y1 + u * py

    dx = x - x3
    dy = y - y3

    dist = math.sqrt(dx*dx + dy*dy)

    return dist

这里是一个测试:

(保留HTML,不进行解释)
rect = [[0. ,  0. ],
       [ 0.2,  1. ],
       [ 2.2,  0.6],
       [ 2. , -0.4]]

c = 0.5, 2.0
r = 1.0

distances = [dist(rect[i], rect[j], c) for i, j in zip([0, 1, 2, 3], [1, 2, 3, 0])]
print distances
print any(d < r for d in distances)

输出:

[1.044030650891055, 1.0394155162323753, 2.202271554554524, 2.0592194189509323]
False

这里是情节:

enter image description here


正如@Michael Anderson向我指出的那样:考虑一个完全位于大盒子内部的小圆。任何距离计算都不会小于半径,因此你的方法也会对该情况返回“False”。此外,使用“< r”意味着触碰不会被视为碰撞。 - martineau
我明白了,要检测圆心是否在矩形内,您可以使用此处的 point_in_poly() 函数:https://dev59.com/YmQn5IYBdhLWcg3wtpBW - HYRY
检测矩形中的一个点并不需要使用point_in_poly()的普适性,因为在pygame中它们都是正立的。即使检测到一个小圆心刚好在盒子边缘之外但仍与其相交(通常是两个点),这种情况也会失败。 - martineau

1
你有两种常见的选择来进行这种类型的碰撞检测。
第一种是了解两个2D对象可以相互碰撞的方式。
  1. 一个顶点可以在另一个对象内部
  2. 它们的边可以交叉(即使没有顶点在内部)
  3. 其中一个可以完全位于另一个内部。
技术上只有情况1发生时,情况2才会发生,但通常情况1是更便宜的检查。 同样情况3由情况1检查,在检查两个对象的顶点的情况下。
我会按照以下方式进行。(按照便宜程度的顺序)
  1. 检查它们的包围盒是否相交。
  2. 检查正方形的任何顶点是否在圆内
  3. 检查圆心是否在矩形内
  4. 检查圆与边缘的相交。
第二种更普遍的方法基于形状的乘积/扩展的概念。 此操作允许您将交集问题转换为点包含问题。
在这种情况下,圆/矩形框交集可以替换为检查圆角矩形中的点。

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