Ursina引擎中如何禁用远处实体以降低游戏卡顿?
嘿,我完全懂你这种挫败感——明明方块总数算下来才1875个(25×25×3),却已经出现帧率问题,而且还得保留每个方块的独立性(毕竟MC克隆的核心就是可破坏/交互的单个方块),不能随便合并成大网格,确实有点难搞。针对你试过的方法,我给你几个优化思路:
1. 优化距离检测效率,别再遍历所有方块
你之前的全量遍历+高频检测是卡顿的核心原因之一——哪怕每60帧跑一次,遍历1900个方块的计算量也不小。不如参考MC的区块思路,把你的25×25世界拆成小区块(比如5×5的区块,每个区块管理25个方块),这样你只需要先判断哪些区块在玩家的有效范围内,再处理这些区块里的方块,遍历的数量会大幅减少。
举个代码例子:
from ursina import * # 先定义区块类,管理一组方块 class BlockChunk(Entity): def __init__(self, position=(0,0,0), **kwargs): super().__init__(position=position, **kwargs) self.blocks = [] # 存储区块内的所有方块 # 初始化世界时,把方块分配到对应区块 chunks = {} for x in range(25): for z in range(25): chunk_pos = (x//5 *5, 0, z//5 *5) # 区块的起始坐标 if chunk_pos not in chunks: chunks[chunk_pos] = BlockChunk(position=chunk_pos) # 创建方块并加入对应区块 block = Entity(parent=chunks[chunk_pos], position=(x, 0, z), model='cube') chunks[chunk_pos].blocks.append(block) # 然后只在玩家移动时(或者低频检测)更新区块状态 def update(): # 每10帧检测一次,减少频率 if not frame_count % 10: player_pos = player.position # 遍历所有区块,判断是否在玩家10单位范围内 for chunk_pos, chunk in chunks.items(): # 用平方距离代替欧几里得距离,避免开根号,计算更快 distance_sq = (player_pos.x - chunk_pos.x)**2 + (player_pos.z - chunk_pos.z)**2 if distance_sq > 100: # 10的平方 chunk.enabled = False # 禁用整个区块,包括所有子方块 else: chunk.enabled = True
这样一来,你不需要逐个处理每个方块,而是直接控制整个区块的启用/禁用,效率会高很多,而且检测次数也少了。
2. 确保禁用操作真正停止所有资源消耗
你说禁用方块后帧率没提升,可能是因为block.disable()只是隐藏了渲染,但某些方块的组件(比如碰撞检测、自定义update脚本)还在后台运行。试试在禁用时额外关闭这些开销:
# 如果是单个方块处理,除了disable,还要关闭碰撞和自定义脚本 def toggle_block(block, enable): block.enabled = enable block.collision = enable # 禁用碰撞检测,减少物理开销 # 如果你的方块有自定义的update函数,也要手动暂停 if hasattr(block, 'custom_update'): block.custom_update.enabled = enable
Ursina里的enabled=False会禁用实体的所有组件,但如果你给方块加了额外的独立脚本,可能需要单独处理。另外,也可以用block.visible = False只隐藏渲染,但保留碰撞,不过如果不需要交互的话,直接禁用整个实体更彻底。
3. 别只靠相机裁剪面,要结合实体禁用
你设置的camera.clip_plane_far = 10只是让远处的方块不渲染,但这些方块依然存在于场景中,会占用内存、参与物理计算(如果有的话),甚至可能在后台执行脚本。所以裁剪面只能作为辅助,必须配合实体禁用才能真正减少资源占用。
4. 补充:如果还是有卡顿,试试批量渲染优化
虽然你不想合并网格,但可以给远处的区块用简化网格(比如把整个区块合并成一个大立方体),同时保留近处区块的单个方块。这样既保证了近处的交互性,又减少了远处的渲染开销。比如:
# 给区块添加简化网格的逻辑 class BlockChunk(Entity): def __init__(self, position=(0,0,0), **kwargs): super().__init__(position=position, **kwargs) self.blocks = [] self.simplified_mesh = None def create_simplified_mesh(self): # 把区块内的方块合并成一个网格(Ursina里可以用Mesh.combine) self.simplified_mesh = Entity(parent=self, model=Mesh.combine([b.model for b in self.blocks]), color=color.gray) self.simplified_mesh.enabled = False # 然后在检测区块距离时,切换显示简化网格还是单个方块 def update(): if not frame_count % 10: player_pos = player.position for chunk_pos, chunk in chunks.items(): distance_sq = (player_pos.x - chunk_pos.x)**2 + (player_pos.z - chunk_pos.z)**2 if distance_sq > 100 and distance_sq <= 200: # 中等距离显示简化网格 chunk.enabled = False chunk.simplified_mesh.enabled = True elif distance_sq > 200: # 超远距离直接隐藏 chunk.enabled = False chunk.simplified_mesh.enabled = False else: # 近距离显示单个方块 chunk.enabled = True chunk.simplified_mesh.enabled = False
这样既解决了帧率问题,又保留了核心的方块交互功能。
最后再提醒一下:测试的时候打开Ursina的性能面板(按F1),看看是CPU还是GPU瓶颈——如果是CPU占满,那优化检测逻辑和实体禁用就对了;如果是GPU瓶颈,那简化网格或者降低渲染分辨率会更有效。
备注:内容来源于stack exchange,提问作者LimeSpeedCrystal

