如何使用Python Turtle库预判移动矩形与随机位置静止矩形的碰撞
嘿,我来帮你搞定这个带旋转的移动矩形与静止矩形的碰撞检测问题!结合Python的turtle库,咱们可以用**分离轴定理(SAT)**来实现精准判断——毕竟你的移动矩形支持旋转,普通的轴对齐包围盒(AABB)就吃不住了,SAT是检测凸多边形碰撞的黄金标准,完美适配旋转后的矩形场景。
核心实现步骤
1. 封装矩形类
咱们先把矩形的核心属性(位置、尺寸、旋转角度)和顶点计算逻辑封装成类,这是SAT碰撞检测的基础——因为SAT需要用到多边形的顶点坐标来计算投影。
2. 实现分离轴定理(SAT)
SAT的核心逻辑很直白:如果存在任意一条轴,两个矩形在这条轴上的投影完全不重叠,那它们肯定没碰撞;反之,所有轴的投影都有重叠的话,就说明发生了碰撞。对于矩形来说,只需要检查两个矩形各自两条边的法向量作为分离轴就够了(因为对边的法向量是相同的,不用重复检查)。
3. 用turtle搭建交互与渲染
最后用turtle绘制两个矩形,绑定键盘事件来控制移动和旋转,每次更新后实时调用碰撞检测函数,把结果输出到命令窗口。
完整可运行代码
import turtle import math import random # 定义矩形类,包含位置、尺寸、旋转角度及顶点计算方法 class Rectangle: def __init__(self, x, y, width, height, angle=0): self.x = x # 矩形中心x坐标 self.y = y # 矩形中心y坐标 self.width = width self.height = height self.angle = angle # 顺时针旋转角度(单位:度) # 计算旋转后的四个顶点世界坐标 def get_vertices(self): rad = math.radians(self.angle) cos_a = math.cos(rad) sin_a = math.sin(rad) # 未旋转时的本地顶点(相对于中心) half_w = self.width / 2 half_h = self.height / 2 local_points = [ (-half_w, -half_h), (half_w, -half_h), (half_w, half_h), (-half_w, half_h) ] # 应用旋转+位移转换为世界坐标 world_points = [] for px, py in local_points: rotated_x = px * cos_a - py * sin_a rotated_y = px * sin_a + py * cos_a world_x = rotated_x + self.x world_y = rotated_y + self.y world_points.append((world_x, world_y)) return world_points # 分离轴定理碰撞检测函数 def is_colliding(rect1, rect2): vertices1 = rect1.get_vertices() vertices2 = rect2.get_vertices() # 获取所有需要检查的分离轴(矩形边的法向量) def get_axes(vertices): axes = [] for i in range(len(vertices)): p1 = vertices[i] p2 = vertices[(i+1)%len(vertices)] # 计算边的向量 edge_x = p2[0] - p1[0] edge_y = p2[1] - p1[1] # 生成垂直于边的法向量 normal = (-edge_y, edge_x) # 归一化法向量(简化投影计算) length = math.hypot(normal[0], normal[1]) if length != 0: normal = (normal[0]/length, normal[1]/length) axes.append(normal) return axes axes = get_axes(vertices1) + get_axes(vertices2) # 检查每个轴上的投影是否重叠 for axis in axes: # 计算两个矩形顶点在当前轴上的投影范围 proj1 = [p[0]*axis[0] + p[1]*axis[1] for p in vertices1] min1, max1 = min(proj1), max(proj1) proj2 = [p[0]*axis[0] + p[1]*axis[1] for p in vertices2] min2, max2 = min(proj2), max(proj2) # 投影无重叠则说明无碰撞 if max1 < min2 or max2 < min1: return False return True # 所有轴投影都重叠,发生碰撞 # 初始化turtle画布 screen = turtle.Screen() screen.title("矩形碰撞检测") screen.setup(width=800, height=600) # 创建两个矩形:随机位置的静止矩形 + 可移动旋转的矩形 static_rect = Rectangle( x=random.randint(-300, 300), y=random.randint(-200, 200), width=80, height=50 ) moving_rect = Rectangle( x=-200, y=0, width=60, height=40 ) # 绘制矩形的工具函数 def draw_rect(rect, color): pen = turtle.Turtle() pen.hideturtle() pen.speed(0) pen.fillcolor(color) pen.penup() vertices = rect.get_vertices() pen.goto(vertices[0]) pen.pendown() pen.begin_fill() for point in vertices[1:]: pen.goto(point) pen.goto(vertices[0]) pen.end_fill() return pen # 初始绘制两个矩形 static_pen = draw_rect(static_rect, "lightblue") moving_pen = draw_rect(moving_rect, "orange") # 更新移动矩形并检测碰撞的函数 def update_moving_rect(): global moving_pen moving_pen.clear() moving_pen = draw_rect(moving_rect, "orange") # 检测碰撞并输出结果到命令窗口 collision = is_colliding(moving_rect, static_rect) print(f"碰撞状态:{'发生碰撞!' if collision else '未碰撞'}") # 绑定键盘控制事件 def move_right(): moving_rect.x += 10; update_moving_rect() def move_left(): moving_rect.x -= 10; update_moving_rect() def move_up(): moving_rect.y += 10; update_moving_rect() def move_down(): moving_rect.y -= 10; update_moving_rect() def rotate_clockwise(): moving_rect.angle += 5; update_moving_rect() def rotate_counter_clockwise(): moving_rect.angle -= 5; update_moving_rect() screen.onkeypress(move_right, "Right") screen.onkeypress(move_left, "Left") screen.onkeypress(move_up, "Up") screen.onkeypress(move_down, "Down") screen.onkeypress(rotate_clockwise, "z") screen.onkeypress(rotate_counter_clockwise, "x") screen.listen() # 启动turtle主循环 turtle.done()
关键细节说明
- 顶点计算:
get_vertices()方法严格适配turtle的坐标系(原点在画布中心,y轴向上),通过三角函数实现旋转后的顶点坐标转换,确保绘制和碰撞检测的坐标一致。 - SAT逻辑优化:咱们只提取了矩形的独特分离轴(每个矩形两条),避免了重复计算,同时对法向量做了归一化,让投影计算更精准。
- 交互反馈:每次移动或旋转后,都会立即重新绘制移动矩形并检测碰撞,结果实时输出到命令窗口,你可以直接在终端看到碰撞状态的变化。
内容的提问来源于stack exchange,提问作者Levent Kırımgeri




