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

Python Arcade大量Sprite与网格碰撞检测的性能优化求助

50×50网格小球碰撞检测性能优化方案

我用Python开发了一款模拟程序,核心是构建50×50的彩色单元格网格,小球在网格内生成并移动,直至碰撞到不同颜色的单元格——碰撞发生时单元格改变颜色,小球随之消失。目前基础功能已实现,但存在严重性能瓶颈:小球数量低于40个时能维持60FPS,若支持上千个小球运行,帧率会显著下降。

原核心代码片段

GRID_SIZE = 50
CELL_SIZE = 10
self.balls = arcade.SpriteList()
self.cells = arcade.SpriteList(use_spatial_hash=True, spatial_hash_cell_size=CELL_SIZE)

def on_update(self, delta_time):
    self.balls.update()

class Cell(arcade.SpriteSolidColor):
    def __init__(self, x, y, color, grid_app):
        super().__init__(CELL_SIZE, CELL_SIZE, arcade.color.WHITE)
        self.gridApp = grid_app
        self.center_x = x
        self.center_y = y
        self.update_color(color)

    def update_color(self, color):
        self.color = color


class Ball(arcade.Sprite):
    def __init__(self, x, y, velocity, angle, spriteConf, grid_app, scale=1):
        super().__init__(SPRITE_CONF[spriteConf]["imagePath"], scale=scale)
        self.center_x = x
        self.center_y = y
        self.my_color=SPRITE_CONF[spriteConf]["color"]
        self.velocity = velocity
        self.my_angle = angle
        self.gridApp = grid_app

    def update(self):
        self.center_x += self.velocity * REFRESH_RATE * math.cos(math.radians(self.my_angle))
        self.center_y += self.velocity * REFRESH_RATE * math.sin(math.radians(self.my_angle))

        # 边界碰撞检测
        if self.center_x < 0 or self.center_x > WINDOW_WIDTH:
            self.my_angle = 180 - self.my_angle
        if self.center_y < 0 or self.center_y > WINDOW_HEIGHT:
            self.my_angle = 360 - self.my_angle

        # 性能瓶颈区域
        collidedCells = arcade.check_for_collision_with_lists(
            self,
            [self.gridApp.cells]
        )
        hasToBeDelete = False
        for cell in collidedCells:
            if cell.color != self.my_color:
                hasToBeDelete = True
                cell.update_color(self.my_color)
        if hasToBeDelete:
            self.gridApp.removeBall(self)

性能问题出现前的运行状态:
性能问题出现前的游戏状态

针对性优化方案

1. 用网格坐标计算替代Sprite碰撞检测

直接通过小球坐标计算所在单元格,彻底避免遍历SpriteList的开销:

  • 初始化时将Cell对象存入二维数组self.grid_cells(按行列索引存储)
  • 小球update时,通过坐标计算当前所在的网格行列:
    col = int(self.center_x // CELL_SIZE)
    row = int(self.center_y // CELL_SIZE)
    # 确保行列在网格范围内
    if 0 <= col < GRID_SIZE and 0 <= row < GRID_SIZE:
        target_cell = self.gridApp.grid_cells[row][col]
        if target_cell.color != self.my_color:
            target_cell.update_color(self.my_color)
            self.gridApp.removeBall(self)
    
    此方法将每个小球的碰撞检测复杂度从O(N)降到O(1),是提升性能的核心。

2. 预计算方向向量,减少三角函数调用

原代码每次update都重复计算三角函数,可提前将角度转换为方向向量:

  • 在Ball初始化时:
    self.dx = math.cos(math.radians(self.my_angle))
    self.dy = math.sin(math.radians(self.my_angle))
    
  • 更新移动逻辑:
    self.center_x += self.velocity * REFRESH_RATE * self.dx
    self.center_y += self.velocity * REFRESH_RATE * self.dy
    
  • 边界碰撞时直接反转方向向量,无需重新计算角度:
    if self.center_x < 0 or self.center_x > WINDOW_WIDTH:
        self.dx = -self.dx
    if self.center_y < 0 or self.center_y > WINDOW_HEIGHT:
        self.dy = -self.dy
    

3. 优化Cell存储结构

将原SpriteList存储的cells替换为二维数组,初始化时按行列填充:

# 主类初始化时
self.grid_cells = []
for row in range(GRID_SIZE):
    row_cells = []
    for col in range(GRID_SIZE):
        x = col * CELL_SIZE + CELL_SIZE // 2
        y = row * CELL_SIZE + CELL_SIZE // 2
        cell = Cell(x, y, initial_color, self)
        row_cells.append(cell)
        self.cells.append(cell)  # 保留SpriteList用于渲染
    self.grid_cells.append(row_cells)

二维数组的直接索引比SpriteList的空间哈希查找更快,尤其在大量小球并发访问时。

4. 降低碰撞检测频率(可选)

如果小球移动速度较慢,可每N帧执行一次碰撞检测,减少计算量:

  • 在Ball类中添加计数器:self.check_counter = 0
  • update时:
    self.check_counter += 1
    if self.check_counter % 2 == 0:  # 每2帧检查一次
        # 执行网格坐标计算和碰撞逻辑
        self.check_counter = 0
    

5. 批量处理小球更新(进阶优化)

用numpy批量存储小球的坐标、方向向量等数据,通过向量运算批量计算移动,再同步回Sprite对象。此方法可大幅减少Python循环的开销,适合上千个小球的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 08:17:43