Unity DOTS 学习笔记(一) Nav-Field寻路场

Last modified date

从零开始的ECS学习之路——冒险的黎明

Unity DOTS

Unity dots 1.0发布快一年了。网上不少大佬表示ECS框架没有价值,可能是站在不同角度或者根据自己的喜好和目标来评判的吧。作为一个熟悉C#又不想改引擎的独立游戏开发者来说,dots无疑是提供了一个巨大的可能性空间。例如做一个同屏万人的弹幕射击游戏;又例如做一个Boids集群算法的特效。由于Unity传统框架都是单线程执行的,这些需要利用到多线程并行计算的功能直到有了dots 1.0这个相对稳定的框架以后,从梦想走进了现实。

从三年前Unity ECS框架发布开始,我陆续跟进过几个版本,都很不完善,学个开头什么也做不了,没有完善的渲染管线支持,等等。每次学个把月就放弃了。直到1.0版本发布以后,真的从渲染到编辑器,物理,调试工具全方位的支持到位了。

而且网上也有不少开发者的分享,推荐这两个:

https://www.youtube.com/@TurboMakesGames

https://www.youtube.com/watch?v=H7zAORa3Ux0&t=2642s

当然这段学习期间我也踩了不少坑,总结一下使用心得。

  • 首先要想清楚每个Component模块的数据拆分。拆分的合理可以更加合理的加速内存读写效率。例如活用“IAspect”。
  • 根据实际功能需求来分配每个job是放在主线程还是parallel线程。
  • 有些功能需要上一个job计算完成才执行的,可以用“JobHandle.Complete();”或者用“UpdateInGroup”,“UpdateAfter”之类的标签来决定执行顺序和分组。
  • 有些功能是依赖彼此数据的先后计算顺序的,可以用NativeArray或者NativeHashMap来临时拷贝上一个job的计算结果给下一个job读取。(因为”EntityQuery.ToComponentDataArray”这种方法只能在每帧开始拷贝一次上一帧的计算结果。)

踩的坑很多很多,一时也说不完。由于dots和常用的代码框架差异很大,很多细节也一言难尽。学习时要多看unity官方文档,因为很多组件一直在更新,时不时一个大变化,稍不留神就被坑惨。

用ECS框架做寻路场:

首先场景地形数据是2d格子矩阵,带有障碍物和沟壑,所有的怪物通过寻路场向着玩家前进。我们只要用类似烘焙Nav-Mesh的方法,计算每个节点的寻路信息,并且要根据玩家的移动实时更新寻路场。

寻路计算方式用最常见的A*。参考:https://www.redblobgames.com/pathfinding/a-star/introduction.html

寻路场计算过程:

  • 当玩家走到一个新的格子即刷新Nav-Field。刷新频率不高。
  • 由于A*寻路的计算需要诸格子迭代计算,单帧算完计算量太大,所以把循环迭代改成每一帧执行一次迭代,大大降低每帧的计算量。
  • A*寻路方向太过方正,不自然。在寻路计算完毕后,可以通过读取周围一圈格子的方向,加权平均,获得柔和自然的寻路场。

烘焙好寻路场以后,所有个怪物只要计算当前自己所在格子的移动方向即可。

简化的避障算法:

主流最有效的避障算法是RVO2,但是计算量有点大。参考:https://gamma.cs.unc.edu/RVO2/

这里用了简化的避障计算。参考:https://www.youtube.com/watch?v=pGCVO1FV7PU

  • 对于庞大数量的避障计算,需要用HashMap把场景对象分割成一小块一小块,每个对象只要遍历计算与自己所在格子里的其他对象的碰撞。利用多线程并行计算大大提高性能。
  • 先定义两个NativeMultiHashMap,一个用来存怪物的坐标,一个用来存碰撞后挤出的Velocity。
  • 把怪物按照地形二维坐标划分成若干chunk,用NativeMultiHashMap来储存每个chunk里的怪物的碰撞Velocity。
  • 遍历每个怪物与他所在chunk里的其他怪做碰撞检测:做法就是如果两者距离小于半径之和,则把二者的坐标差当作Velocty存储到NativeMultiHashMap里。
  • 算完所有的碰撞后,新起一个job读取NativeMultiHashMap里的velocity值set给每个怪物。

这种避障算法很不精确,而且很多对象挤在一起时可能会出错,但是性能开销很小。

最后看实机视频效果:

以上角色是序列帧动画的billboard。渲染部分可以通过Hybird renderer从ECS传入材质参数来控制每个instance的表现状态。最终在vivo iQOO8手机上也可以稳定60帧跑10000个对象。当然我的代码能力还有待提高,应该还有不少优化空间。

Share

This site is protected by wp-copyrightpro.com