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

Ursina开发Minecraft克隆版 覆水放置方块触发AssertionError报错

报错根因

你触发的AssertionError: !is_empty() at line 1045 of panda/src/pgraph/nodePath.cxx是典型的遍历列表时同步修改列表结构+访问已销毁实体属性导致的错误:

  • 你正向遍历voxels列表的过程中直接删除列表条目、销毁方块实体,会导致列表索引错位,后续循环会读取到已经被移除出列表、已经调用destroy()销毁的实体
  • 被销毁的Ursina实体本质是Panda3D的空NodePath,此时访问它的position属性就会触发底层的断言报错
  • 额外隐患:全列表遍历比对坐标的效率极低,且浮点数坐标未做取整处理时,很容易出现视觉上坐标重合但程序判断不相等的匹配错误
修复步骤
  • 禁止在遍历原voxels列表的循环内直接执行实体销毁、列表删除操作,先把所有待删除的目标收集到临时列表,遍历完成后再统一做销毁和数据清理
  • 新增实体存活校验:访问任何方块实体属性前,先判断voxel.nodePath.is_empty()是否为False,跳过已销毁实体做兜底
  • 新增坐标哈希索引:用字典存储坐标到方块实体的映射,把重合检测的时间复杂度从O(n)降到O(1),从根源上避免遍历删数据的索引错位问题
  • 坐标比对前对xyz值做整数取整,规避浮点数精度误差导致的匹配失败
核心修复代码
# 全局方块存储:保留原有列表做遍历用,新增坐标字典做快速查询
voxels = []
voxel_index = {}  # 键为(x,y,z)整数元组,值为对应的方块实体

# 新增方块时同步更新两个存储结构
def spawn_voxel(pos, is_water=False):
    block = Voxel(
        position=pos,
        texture=water_texture if is_water else block_texture,
        collider=None if is_water else 'box'  # 水方块关碰撞,其余开碰撞
    )
    block.is_water = is_water
    voxels.append(block)
    pos_key = (round(pos.x), round(pos.y), round(pos.z))
    voxel_index[pos_key] = block
    return block

# 替换原有的重合水方块删除逻辑
def clear_overlap_water():
    wait_to_delete = []
    # 第一步:仅遍历收集待删除目标,不做任何修改操作
    for block in voxels:
        # 跳过已销毁的无效实体
        if block.nodePath.is_empty():
            continue
        current_pos = (round(block.x), round(block.y), round(block.z))
        # 当前位置是普通方块时,检查同位置是否存在水方块
        if not block.is_water and current_pos in voxel_index:
            target_block = voxel_index[current_pos]
            if target_block.is_water and not target_block.nodePath.is_empty():
                wait_to_delete.append( (current_pos, target_block) )
    
    # 第二步:统一销毁实体、清理存储数据
    for pos_key, target in wait_to_delete:
        destroy(target)
        del voxel_index[pos_key]
        voxels.remove(target)

# 删除普通方块时也同步更新两个存储结构,避免脏数据
def destroy_block(block):
    if block.nodePath.is_empty():
        return
    pos_key = (round(block.x), round(block.y), round(block.z))
    destroy(block)
    if pos_key in voxel_index:
        del voxel_index[pos_key]
    if block in voxels:
        voxels.remove(block)

提示:所有涉及方块坐标增删查的操作,都要同步更新voxels列表和voxel_index字典,避免两个存储结构数据不一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 21:54:29