You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何使用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

火山引擎 最新活动