当游戏场景偏大时,由于目标平台资源一般比较有限,通常我们不会把该场景的所有资源一次性展开到内存里。常见做法是,把大大小小的各种资源用某种空间分割的数据结构组织起来,当玩家在场景中移动的时候,可以有效地获取即将出现的资源列表,触发对应的异步加载,并及时释放玩家离开区域的资源。这样的话,无论场景规模多大,同一时刻出现在内存中的数据量总是相对可控的。


为了缓解场景的资源压力,昨天实现了一个基于四叉树的动态资源管理,代码在这里。顺便把设计和实现时的考虑简单地记录一下,以节省沟通的成本。


四叉树是常用的空间分割数据结构。跟 BSP 和 Octree 相比,它非常清晰和规律,在平面上展开时,观察和调试又足够简单。使用四叉树实现的空间管理,可以较好地兼顾开发效率和运行效率。在 开放世界游戏中的大地图背后有哪些实现技术? 一文中我曾提到 “当尺度大到一定规模之后,地形通常退化为相对扁平的2D空间”,在实际的 3D 游戏项目里,水平方向上的场景复杂度一般也会远大于垂直方向上的,因此四叉树比八叉树往往更适合实际项目的需要。

qt1

这是一个测试用的模拟场景,内含 5000 个形状和变换各异的测试模型。左边的 Move 按钮和 "Always Move" 复选框用于模拟玩家的移动, "Debug Lines" 用于画出场景中调试线条。

qt2

这是动态加载开启后的负载情况。可以见到,在玩家(白色柱子)向目标(紫红柱子) 移动的过程中,任意时刻,只有玩家所在区域附近的物件在内存中持有。这张图上还可以看到,玩家远离的方向比面向的方向物件多,这是 swap-out 比 swap-in 的判定距离远一些的缘故,这样主要是为了避免了玩家来回移动时的内存颠簸,也就是对象的反复加载 (降低了 IO 总量)。

qt3

这是在 Scene View 下开启了 Debug Lines 选项后看到的调试视角。可以看到,场景被切分成了均匀四叉树,每一个小的 cell 都是四叉树的一个叶节点 (UQtLeaf) ,其中:

  • 灰色 cells 是非活跃叶节点,这些叶节点上的所有物件都被释放,不占用内存
  • 白色 cells 是玩家当前持有的所有叶节点 (对应代码中的 _holdingLeaves)
  • 绿色 cells 是正在被交换进来 (swap-in) 的叶节点 (这些节点上的所有物件异步加载完成后,这个 cell 会变为白色)
  • 红色 cells 是正在被交换出去 (swap-out) 的叶节点 (这些节点上的所有物件销毁后,这个 cell 会变为灰色)

上面三张图基本上已经把功能说得差不多了,接下来我们简单过一下代码,略作补充。

code1

UQtConfig 这个类内含一些参数,用于按需配置和控制 UQuadtree 内部的一些行为。请注意,对这些参数的调整会直接影响性能表现。目前页交换的触发 (SwapTriggerInterval) 是 0.5s 一次,页状态更新 (SwapProcessInterval) 是 0.2s 一次,这些是为了在保持较低的 CPU 开销下,能够有较高的反应速度。

code2

IQtUserData 是每个叶节点上挂载的用户对象所需实现的接口,使用接口而不是独立委托,可以更明确和清晰地定义 UQuadtree 与用户数据之间的约定。一个典型的用户实现 QtTypicalUserData 应当至少包括一个资源路径字符串 (ResourcePath) 和一个 GameObject——当对应的叶节点被交换进内存时,由 ResourcePath 发起异步请求,载入 GameObject;当对应的叶节点被交换出去时, GameObject 被释放,ResourcePath 被保留用于下一次的载入。


代码及对应的测试工程在 Unity-5.0.1f1 下编译和运行通过,还没有来得及做针对 4.6 的向后移植工作,先这样吧。

Gu Lu
2015-07-11

Comments
Write a Comment
  • Long_Wtf reply

    从git和网盘下载的项目,均无法正常运行,感觉代码有确实,gameObject也有missing。请问是故意处理了。还是我这边打开有问题

  • Bshsf reply

    大兄弟能不能 给个新的工程? 2017 打开啥预制体都不见了

    • lyt reply

      @Bshsf 自己建一个cube当做prefab放在ProtoTypes下面就可以了,只是一个示例而已,MoveTarget同理

Tags

随笔   游戏开发   Bitcoin   BSV   Programming   C/C++   优化   Unity   C++   知乎   中国文化   Unity3D   游戏设计   区块链   比特币   小聪游戏   引擎设计   软件开发   idtech   SatoPlay   系统架构   Production   Bitcoin SV   加密货币   项目管理   游戏评论   资源管理   效率   网络   Visual Studio   Blockchain   Lua   Blockchain Computing   GDC   VR   PerfAssist   EMC   资源流水线   方法论   模板编程   Oculus   渲染   道德经   BitcoinSV   Unity MemoryProfiler   字体   Productivity   图形   SPV   Ethereum   nanomsg   摘录   同步技术   cppcon   C++模板   DOOM3   Surface Pro 3   Engine Evaluation   CRT   Bitcoin Cash   cppcon14   跨语言可变参数列表   团队协作   货币   开放世界   权利   slua   遮挡剔除   完美转发   协作式调度   Money   类型推导   Memory Debugging   个人成长   小故事   产品   错误处理   MOD   BCH   读书笔记   经济学   信息过载   行业报告   Modern C++   ETH   网络编程   协程   Premake   万物理论   测试   中间件   Game Engine   Michael Abrash   区块链游戏   CI   命令行解析   Science   goroutine   ndk   自动化   Scripting   Debugging   数据上链   技术评估   Unity GC   C++11   学习方法   Dice   Telemetry   笔记   golang   Methodology   图形编程   多线程   工程改进   Unity Coroutine   新手引导   Deployment   Visual Assist   文化   BTC   量子计算   域名   虚拟现实   系统重构   暴雪   exp   历史   Unity Profiler  

知识共享许可协议
本作品由Gu Lu创作,采用知识共享Attribution-NonCommercial-NoDerivatives 4.0 国际许可协议进行许可。