如何在PySide6中实现POV与圆形、矩形的碰撞检测?
POV与物体(圆形/矩形)的碰撞检测实现方案
首先明确:你的POV本质是扇形区域,由原点、最大半径、起始角度、结束角度四个核心参数定义。碰撞检测的核心是判断目标物体(圆形/矩形)是否与该扇形区域存在交集,以下是针对两种物体的具体实现方案:
一、POV与圆形的碰撞检测
需满足以下任一条件即可判定碰撞:
- POV原点在圆形内部
- 圆形的圆心在扇形区域内
- 圆形与扇形的两条射线边界相交
- 圆形与扇形的圆弧边界相交
代码实现
import math def is_angle_in_range(angle, start_angle, end_angle): # 统一转为0-2π的弧度范围,处理跨0度的视角区间(如350°到10°) start = start_angle % (2 * math.pi) end = end_angle % (2 * math.pi) target_angle = angle % (2 * math.pi) if start < end: return start <= target_angle <= end else: return target_angle >= start or target_angle <= end def calc_distance(p1, p2): return math.hypot(p1[0]-p2[0], p1[1]-p2[1]) def circle_pov_collision(pov_origin, pov_radius, pov_start, pov_end, circle_center, circle_radius): center_dist = calc_distance(pov_origin, circle_center) # 快速排除:圆完全在POV最大范围外 if center_dist > pov_radius + circle_radius: return False # POV原点在圆内,直接判定碰撞 if center_dist < circle_radius: return True # 判断圆心是否在扇形区域内 center_angle = math.atan2(circle_center[1]-pov_origin[1], circle_center[0]-pov_origin[0]) if is_angle_in_range(center_angle, pov_start, pov_end) and center_dist <= pov_radius: return True # 检测圆形与扇形射线边界的交点 def ray_circle_intersect(ray_origin, ray_dir, circle_center, circle_radius): oc = (circle_center[0]-ray_origin[0], circle_center[1]-ray_origin[1]) tca = oc[0] * ray_dir[0] + oc[1] * ray_dir[1] if tca < 0: return False dist_sq = (oc[0]**2 + oc[1]**2) - tca**2 return dist_sq <= circle_radius**2 # 生成射线方向的单位向量 start_dir = (math.cos(pov_start), math.sin(pov_start)) end_dir = (math.cos(pov_end), math.sin(pov_end)) if ray_circle_intersect(pov_origin, start_dir, circle_center, circle_radius): return True if ray_circle_intersect(pov_origin, end_dir, circle_center, circle_radius): return True # 检测圆形与扇形圆弧边界的交点 if abs(center_dist - pov_radius) <= circle_radius: if is_angle_in_range(center_angle, pov_start, pov_end): return True return False
二、POV与矩形的碰撞检测
需满足以下任一条件即可判定碰撞:
- POV原点在矩形内部
- 矩形任意顶点在扇形区域内
- 扇形的射线边界与矩形任意边相交
- 扇形的圆弧边界与矩形任意边相交
代码实现
def point_in_rect(point, rect): # rect格式:(x, y, width, height),对应PySide6的QRect参数 rx, ry, rw, rh = rect return rx <= point[0] <= rx+rw and ry <= point[1] <= ry+rh def ray_segment_intersect(ray_origin, ray_dir, seg_p1, seg_p2): # 射线与线段相交检测(跨立实验) def cross_product(a, b): return a[0]*b[1] - a[1]*b[0] v1 = (seg_p1[0]-ray_origin[0], seg_p1[1]-ray_origin[1]) v2 = (seg_p2[0]-ray_origin[0], seg_p2[1]-ray_origin[1]) ray_normal = (-ray_dir[1], ray_dir[0]) # 射线方向的垂直向量 cross1 = cross_product(v1, ray_normal) cross2 = cross_product(v2, ray_normal) # 线段跨立射线,且交点在射线正方向 if (cross1 * cross2) < 0: t = cross_product((seg_p2[0]-seg_p1[0], seg_p2[1]-seg_p1[1]), (ray_origin[0]-seg_p1[0], ray_origin[1]-seg_p1[1])) t /= cross_product((seg_p2[0]-seg_p1[0], seg_p2[1]-seg_p1[1]), ray_dir) if t >= 0: return True # 处理端点在射线上的情况 if cross1 == 0 and (v1[0]*ray_dir[0] + v1[1]*ray_dir[1]) >= 0: return True if cross2 == 0 and (v2[0]*ray_dir[0] + v2[1]*ray_dir[1]) >= 0: return True return False def arc_segment_intersect(arc_center, arc_radius, start_angle, end_angle, seg_p1, seg_p2): # 圆弧与线段相交检测:先找线段与圆的交点,再判断交点是否在圆弧角度区间内 def line_circle_intersect(p1, p2, center, radius): dx = p2[0]-p1[0] dy = p2[1]-p1[1] a = dx**2 + dy**2 b = 2 * (dx*(p1[0]-center[0]) + dy*(p1[1]-center[1])) c = (p1[0]-center[0])**2 + (p1[1]-center[1])**2 - radius**2 discriminant = b**2 - 4*a*c if discriminant < 0: return [] t1 = (-b - math.sqrt(discriminant)) / (2*a) t2 = (-b + math.sqrt(discriminant)) / (2*a) intersections = [] if 0 <= t1 <= 1: intersections.append((p1[0]+t1*dx, p1[1]+t1*dy)) if 0 <= t2 <= 1 and t1 != t2: intersections.append((p1[0]+t2*dx, p1[1]+t2*dy)) return intersections points = line_circle_intersect(seg_p1, seg_p2, arc_center, arc_radius) for point in points: angle = math.atan2(point[1]-arc_center[1], point[0]-arc_center[0]) if is_angle_in_range(angle, start_angle, end_angle): return True return False def rect_pov_collision(pov_origin, pov_radius, pov_start, pov_end, rect): rx, ry, rw, rh = rect # 矩形的四个顶点和四条边 rect_points = [(rx, ry), (rx+rw, ry), (rx+rw, ry+rh), (rx, ry+rh)] rect_edges = [(rect_points[i], rect_points[(i+1)%4]) for i in range(4)] # 快速排除:矩形完全在POV最大范围外 max_point_dist = max(calc_distance(pov_origin, p) for p in rect_points) rect_diag_half = math.hypot(rw, rh)/2 if max_point_dist > pov_radius + rect_diag_half: return False # POV原点在矩形内,直接判定碰撞 if point_in_rect(pov_origin, rect): return True # 检测矩形顶点是否在扇形内 for point in rect_points: dist = calc_distance(pov_origin, point) if dist > pov_radius: continue angle = math.atan2(point[1]-pov_origin[1], point[0]-pov_origin[0]) if is_angle_in_range(angle, pov_start, pov_end): return True # 检测扇形射线与矩形边的交点 start_dir = (math.cos(pov_start), math.sin(pov_start)) end_dir = (math.cos(pov_end), math.sin(pov_end)) for edge in rect_edges: if ray_segment_intersect(pov_origin, start_dir, edge[0], edge[1]): return True if ray_segment_intersect(pov_origin, end_dir, edge[0], edge[1]): return True # 检测扇形圆弧与矩形边的交点 for edge in rect_edges: if arc_segment_intersect(pov_origin, pov_radius, pov_start, pov_end, edge[0], edge[1]): return True return False
三、PySide6集成示例
在你的场景更新或绘制逻辑中,直接调用上述函数即可完成碰撞检测:
# 假设player是你的视角载体,game_objects是场景中的物体列表 pov_origin = (player.x, player.y) pov_radius = 200 # 视角最大范围 pov_start = player.rotation_rad - math.pi/4 # 左边界角度(45°视野) pov_end = player.rotation_rad + math.pi/4 # 右边界角度 for obj in game_objects: if obj.type == "circle": collided = circle_pov_collision(pov_origin, pov_radius, pov_start, pov_end, (obj.x, obj.y), obj.radius) elif obj.type == "rect": collided = rect_pov_collision(pov_origin, pov_radius, pov_start, pov_end, (obj.x, obj.y, obj.width, obj.height)) # 根据碰撞结果处理逻辑(比如标记物体为可见) obj.visible = collided
内容的提问来源于stack exchange,提问作者DSA5252
相关产品推荐
相关产品推荐

