You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化Python Turtle射线投射项目以解决高FOV下的卡顿问题

问题描述

我用Python的turtle库开发3D游戏射线投射器,但视场角(FOV)超过10时程序严重卡顿。试过tracer、update、speed这些优化方法,效果不好。作为编程新手卡住了,求帮助。

项目代码

import turtle
from math import fabs

window = turtle.Screen()
window.title("Ray Caster")
window.bgcolor("#000000")
window.setup(1000, 500)
window.tracer(0, 0)
window.delay(0)

char = turtle.Turtle()
char.color("#ffff00")
char.penup()
char.shape("triangle")
char.shapesize(stretch_wid=0.4, stretch_len=0.8)

ray = char.clone()
ray.shape("square")
ray.shapesize(stretch_len=0.2, stretch_wid=0.2)
ray.pencolor("#0000ff")
ray.pensize(1)

fov = 60

def single_ray():
    ray.goto(char.position())
    ray.pendown()
    for i in range(1000):
        ray.forward(4)
        if collision(ray, rigwall, 8, 20, 16, 500) or collision(ray, lefwall, 8, 20, 16, 500) or collision(ray, topwall, 8, 1000, 16, 20) or collision(ray, botwall, 8, 1000, 16, 20) or collision(ray, midwall, 8, 500, 16, 20):
            ray.backward(4)
    ray.penup()

def raycast():
    ray.right(fov / 2)
    for i in range(fov):
        single_ray()
        ray.left(1)

def ray_cast():
    ray.clear()
    ray.setheading(char.heading())
    raycast()

def collision(a, b, a_width, b_width, a_height, b_height):
    xcoll = (fabs(a.xcor() - b.xcor()) * 2) < (a_width + b_width)
    ycoll = (fabs(a.ycor() - b.ycor()) * 2) < (a_height + b_height)
    return (xcoll and ycoll)

rigwall = turtle.Turtle()
rigwall.color("#444444")
rigwall.shape("square")
rigwall.shapesize(stretch_wid=25, stretch_len=1)
rigwall.penup()
rigwall.goto(480, 0)

lefwall = rigwall.clone()
lefwall.goto(-490, 0)

topwall = rigwall.clone()
topwall.shapesize(stretch_wid=1, stretch_len=50)
topwall.goto(0, 240)

botwall = topwall.clone()
botwall.goto(0, -230)

midwall = topwall.clone()
midwall.shapesize(stretch_wid=1, stretch_len=25)
midwall.goto(0, 100)

def char_for():
    char.forward(5)
def char_bac():
    char.backward(5)
def char_lef():
    char.left(4.5)
def char_rig():
    char.right(4.5)

window.listen()
window.onkeypress(char_for, "w")
window.onkeypress(char_bac, "s")
window.onkeypress(char_lef, "a")
window.onkeypress(char_rig, "d")

while 1:

    if collision(char, rigwall, 8, 20, 16, 500) or collision(char, lefwall, 8, 20, 16, 500) or collision(char, topwall, 8, 1000, 16, 20) or collision(char, botwall, 8, 1000, 16, 20) or collision(char, midwall, 8, 500, 16, 20):
        char.backward(5)

    ray_cast()

    window.update()
优化方案

卡顿的核心原因是步进式射线检测的效率极低,加上turtle对象的绘图开销。以下是针对性优化:

1. 替换步进检测为射线-矩形相交计算(核心优化)

当前single_ray通过循环1000次移动射线并检测碰撞,FOV=60时仅射线检测就会执行60000次循环,这是最大性能瓶颈。改用数学方法直接计算射线到墙体的距离,无需逐像素移动。

实现射线-矩形相交计算

from math import cos, sin, radians, sqrt

def get_ray_distance(start_x, start_y, angle, walls):
    rad_angle = radians(angle)
    dx = cos(rad_angle)
    dy = sin(rad_angle)
    
    min_dist = float('inf')
    for wall in walls:
        # 转换turtle墙体参数为实际边界坐标
        wx, wy = wall.position()
        # turtle的shapesize单位是20像素,宽是x轴方向,高是y轴方向
        w_width = wall.shapesize()[1] * 20
        w_height = wall.shapesize()[0] * 20
        wall_left = wx - w_width / 2
        wall_right = wx + w_width / 2
        wall_bottom = wy - w_height / 2
        wall_top = wy + w_height / 2
        
        # 计算射线与矩形的交点距离(简化AABB相交算法)
        t_near_x = (wall_left - start_x) / dx if dx != 0 else float('-inf')
        t_far_x = (wall_right - start_x) / dx if dx != 0 else float('inf')
        t_near_y = (wall_bottom - start_y) / dy if dy != 0 else float('-inf')
        t_far_y = (wall_top - start_y) / dy if dy != 0 else float('inf')
        
        # 修正近远交点顺序
        if t_near_x > t_far_x:
            t_near_x, t_far_x = t_far_x, t_near_x
        if t_near_y > t_far_y:
            t_near_y, t_far_y = t_far_y, t_near_y
        
        t_min = max(t_near_x, t_near_y)
        t_max = min(t_far_x, t_far_y)
        
        # 判断是否相交且交点在射线前方
        if t_max >= 0 and t_min <= t_max:
            dist = sqrt((dx * t_min) ** 2 + (dy * t_min) ** 2)
            if dist < min_dist:
                min_dist = dist
    return min_dist if min_dist != float('inf') else 0

重写射线绘制函数

def single_ray(angle):
    ray.goto(char.position())
    ray.setheading(angle)
    dist = get_ray_distance(char.xcor(), char.ycor(), angle, walls_list)
    ray.pendown()
    ray.forward(dist)
    ray.penup()

2. 批量管理墙体,简化碰撞判断

把所有墙体存入列表,避免重复的逻辑判断:

# 在定义完所有墙体后添加
walls_list = [rigwall, lefwall, topwall, botwall, midwall]

修改玩家碰撞检测逻辑:

# 主循环中的碰撞检测改为
collided = False
for wall in walls_list:
    w_width = wall.shapesize()[1] * 20
    w_height = wall.shapesize()[0] * 20
    if collision(char, wall, 8, w_width, 16, w_height):
        char.backward(5)
        collided = True
        break

3. 优化turtle绘图操作

改用tkinter底层的canvas绘制射线,比turtle对象操作更快:

# 替换ray_cast函数
def ray_cast():
    canvas = window.getcanvas()
    canvas.delete("ray")  # 清除之前的射线
    base_angle = char.heading() - fov / 2
    for i in range(fov):
        current_angle = base_angle + i
        dist = get_ray_distance(char.xcor(), char.ycor(), current_angle, walls_list)
        rad = radians(current_angle)
        end_x = char.xcor() + cos(rad) * dist
        end_y = char.ycor() + sin(rad) * dist
        # 用canvas直接绘制射线
        canvas.create_line(char.xcor(), char.ycor(), end_x, end_y, fill="#0000ff", width=1, tags="ray")

4. 替换死循环为定时器,避免阻塞事件

不要用while 1,改用turtle的ontimer维持主循环,保证事件响应流畅:

def main_loop():
    # 玩家移动碰撞检测
    collided = False
    for wall in walls_list:
        w_width = wall.shapesize()[1] * 20
        w_height = wall.shapesize()[0] * 20
        if collision(char, wall, 8, w_width, 16, w_height):
            char.backward(5)
            collided = True
            break
    
    # 射线投射
    ray_cast()
    
    window.update()
    window.ontimer(main_loop, 16)  # 约60帧每秒

# 启动主循环
main_loop()

优化后效果

这些改动将射线检测的时间复杂度从O(FOV1000)降到O(FOV墙体数),FOV设为60甚至更大时,帧率会有明显提升,卡顿问题基本解决。


内容的提问来源于stack exchange,提问作者auzifriend

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 04:39:53