圆矩形碰撞的碰撞发现面 [英] Issue finding side of collision for Circle-Rectangle collision

查看:74
本文介绍了圆矩形碰撞的碰撞发现面的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我发现了一个功能很好的函数,它可以检测圆形和矩形之间的碰撞,还可以找到接触点.然后,我使用该点来确定圆形击中矩形的哪一侧,以便反映圆形.但是,当圆的中心位于矩形内部时,该函数将最接近的点作为圆的中心,并且将其处理为碰到顶点而不是边.这是我的代码:

I have found a function that works quite well for detecting collisions between a circle and a rectangle, and also finding the point of contact. I then use that point to determine which side of the rectangle the circle hit so I can reflect the circle. However, when the center of the circle is inside the rectangle, the function gives the closest point as the center of circle, and it handles it as hitting a vertex rather than a side. Here is my code:

def collide_rect(box, ball_): 
    #convenience
    left = box.rect.left
    right = left + box.rect.width
    top = box.rect.top
    bottom = top + box.rect.height

    #find the closest point
    closest = (max(left, min(ball_.center[0], right)), max(top, min(ball_.center[1], bottom)))
    dx = ball_.center[0] - closest[0]
    dy = ball_.center[1] - closest[1]

    #handle the collsion
    if math.hypot(dx, dy) <= ball.radius:
        #Hit on the top or bottom
        if left <= closest[0] <= right and (closest[1] == top or closest[1] == bottom):
            ball_.vector = (ball_.vector[0], -1*ball_.vector[1])
        #Hit on the side
        elif top <= closest[1] <= bottom and (closest[0] == left or closest[0] == right):
            ball_.vector = (-1*ball_.vector[0], ball_.vector[1])
        #Hit a vertex
        else:
            ball_.vector = (-1*ball_.vector[0], -1*ball_.vector[1])

        return True
    else:
        return False 

请注意, ball_.vector 是圆的方向向量,而 ball.radius 是类变量.任何以更好的方式找到碰撞面的帮助将不胜感激!

Note that ball_.vector is the circle's direction vector and ball.radius is a class variable. Any help with a better way to find the side of collision would be greatly appreciated!

推荐答案

通过在矩形上找到,可以找到圆的圆的中心和矩形的中心.

You can find the side of the circle, by finding the on the rectangle, which is on the straight line, which is given by the center of the circle and the center of the rectangle.

可以通过中心点之间的偏移与矩形大小的最小关系来计算矩形和圆上的点.
在以下算法中,矩形由中心点( r_cpt )和大小( r_size )定义,圆由中心点( c_cpt)和半径( c_rad ):

The point on the rectangle and the circle can be computed by the minimum relation of the offset between the center points and the size of the rectangle.
In the following algorithm, the rectangle is defined by the center point (r_cpt) and the size (r_size) and the circle is defined by the center point (c_cpt) and the radius (c_rad):

def intersectRectangleCircle(r_cpt, r_size, c_cpt, c_rad):

    v2_c_cpt = pygame.math.Vector2(c_cpt)
    v2_r_cpt = pygame.math.Vector2(r_cpt)

    offset = v2_c_cpt - v2_r_cpt
    if offset.x == 0 and offset.y == 0:
        return [v2_c_cpt, v2_r_cpt]

    if offset.x == 0:   
        ratio = r_size[1] / abs(offset.y)
    elif offset.y == 0: 
        ratio = r_size[0] / abs(offset.x)
    else:
        ratio  = min(r_size[0] / abs(offset.x), r_size[1] / abs(offset.y))
    ratio *= 0.5

    p1 = v2_r_cpt + (offset * ratio)
    offset.scale_to_length(c_rad)
    p2 = v2_c_cpt - offset

    return [p1, p2]

圆的方向是从矩形的中心点到矩形轮廓上的点的向量给出的:

The direction to the circle is the given by the vector from the center point of the rectangle to the point on the rectangle contour:

isect_pts = intersectRectangleCircle(rect_center, rect_size, circle_center, circle_diameter/2)
dx, dy = isect_pts[0].x - rect_center[0], isect_pts[1].y - rect_center[1] 

请参见示例,( dx dy )由洋红色线表示:

See the example, (dx, dy) is represented by the magenta colored line:

import pygame
import math

pygame.init()
screen = pygame.display.set_mode((500, 500))

def intersectRectangleCircle(r_cpt, r_size, c_cpt, c_rad):

    v2_c_cpt = pygame.math.Vector2(c_cpt)
    v2_r_cpt = pygame.math.Vector2(r_cpt)

    offset = v2_c_cpt - v2_r_cpt
    if offset.x == 0 and offset.y == 0:
        return [v2_c_cpt, v2_r_cpt]

    if offset.x == 0:   
        ratio = r_size[1] / abs(offset.y)
    elif offset.y == 0: 
        ratio = r_size[0] / abs(offset.x)
    else:
        ratio  = min(r_size[0] / abs(offset.x), r_size[1] / abs(offset.y))
    ratio *= 0.5

    p1 = v2_r_cpt + (offset * ratio)
    offset.scale_to_length(c_rad)
    p2 = v2_c_cpt - offset

    return [p1, p2]


def inBetween(p1, p2, px):

    v = pygame.math.Vector2(p2) - pygame.math.Vector2(p1)
    d = v.length()
    if d == 0:
        return False
    v.normalize_ip()

    vx = pygame.math.Vector2(px) - pygame.math.Vector2(p1)
    dx = v.dot(vx)
    
    return dx >= 0 and dx <= d

done = False
while not done:

    for event in pygame.event.get(): 
        if event.type == pygame.QUIT:
            done = True


    rect_center     = screen.get_rect().center
    rect_size       = screen.get_width() // 5, screen.get_height() // 10
    rect            = pygame.Rect(rect_center[0] - rect_size[0] // 2, rect_center[1] - rect_size[1] // 2, *rect_size)
    circle_center   = pygame.mouse.get_pos()
    circle_diameter = min(*screen.get_size()) // 5

    isect_pts = intersectRectangleCircle(rect_center, rect_size, circle_center, circle_diameter/2)
    dx, dy = isect_pts[0].x - rect_center[0], isect_pts[1].y - rect_center[1] 

    screen.fill((255,255,255))
    pygame.draw.rect(screen, (0, 0, 0), rect, 3)
    pygame.draw.circle(screen, (0, 0, 0), circle_center, circle_diameter // 2, 3)

    pygame.draw.line(screen, (0, 0, 255), rect_center, circle_center, 1)
    pygame.draw.line(screen, (255, 0, 255), rect_center, (round(isect_pts[0].x), round(isect_pts[0].y)), 3)
    for i in range(2):
        px, py = round(isect_pts[i].x), round(isect_pts[i].y)
        col = (255, 0, 0) if inBetween(rect_center, circle_center, (px, py)) else (0, 255, 0)
        pygame.draw.line(screen, col, (px-5, py), (px+5, py), 3)
        pygame.draw.line(screen, col, (px, py-5), (px, py+5), 3)

    pygame.display.flip()

pygame.quit()
quit()

这篇关于圆矩形碰撞的碰撞发现面的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆