python - 问题寻找圆矩形碰撞的碰撞侧
问题描述
我找到了一个非常适合检测圆形和矩形之间的碰撞以及找到接触点的函数。然后我使用该点来确定圆形撞击矩形的哪一侧,以便我可以反映圆形。但是,当圆心在矩形内时,该函数将最近的点作为圆心,并将其处理为碰到顶点而不是边。这是我的代码:
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
是一个类变量。任何有关找到碰撞侧面的更好方法的帮助将不胜感激!
解决方案
您可以通过在由圆的中心和矩形的中心给出的直线上找到矩形上的 来找到圆的边。
矩形和圆上的点可以通过中心点之间的偏移量与矩形大小的最小关系来计算。
在以下算法中,矩形由中心点 ( r_cpt
) 和大小 ( r_size
) 定义,圆由中心点 ( c_cpt
) 和半径 ( 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]
圆的方向由从矩形中心点到矩形轮廓上的点的向量给出:
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
) 由洋红色线表示:
repl.it/@Rabbid76/PyGame-NearestPointOnRectangle
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()
推荐阅读
- algorithm - 在 Fortran 95 中实现 qsort
- javascript - 如何检测 NodeJs 计划的最后一次作业执行何时发生?
- javascript - 类中的 Pre ES6 静态函数
- javascript - Node.appendChild:参数1在纯JS中没有实现接口Node
- python - bs4 loop stop on the Mid of the links 为什么?
- amazon-web-services - 如何基于 cognito 用户池组角色访问 AWS APIGateway
- amazon-dynamodb - 如何在 dynamoDB 中实现 50 次写入的事务?
- java - 我的 Firebase 没有从 Firebase 数据库中检索数据
- javascript - 无法将组件连接到 redux 商店
- html - CSS过渡未在悬停时触发