Unity物理系统与动画系统的优化

本次来看看Unity编辑器创建资源优化之物理系统的选择和使用,碰撞检测与物理碰撞混用乱用等问题,以及动画系统的优化,本次主要这两点内容。Unity 的内置物理系统 (Nvidia PhysX) 在移动设备上开销较大,应该尽量简化碰撞体,网格碰撞体开销较大,或者一开始就预先烘焙碰撞网格,使用物理方法移动刚体,根据目标帧率修改固定时间间隔等都是优化方案,下面看看具体的优化项有哪些吧。

物理系统优化

Unity中的物理解决方案

  • Box2D:C++实现的高效、开源的物理解决方案,是Unity中的默认2D解决方案

  • Nvidia PhysX:是Unity中的默认3D解决方案,非确定性物理模拟库,在状态同步中无法做逻辑处理,帧同步中只能在客户端上做物理表现渲染

  • Unity Physics:确定性物理模拟库,性能不如 PhysX(无法硬件加速),但是可以在单帧内完成多次物理模拟预测,多核心CPU上性能表现不错

  • Havok Physics for Unity:Unity基于Havok物理库推出的物理解决方案, 数据与Unity Physics通用,但是使用Havok Physics for Unity需要得到微软的授权

大部分情况默认即可,无需修改,暂时先关注下面这些设置:

Unity中的物理组件Collider部分的优化

Trigger与Collider

  • Trigger对象的碰撞会被物理引擎所忽略,通过OnTriggerEnter/Stay/Exit函数回调

  • Collider对象由物理引擎触发碰撞,通过OnCollisionEnter/Stay/Exit函数回调

  • Trigger对象不需要RigidBody组件,Collider对象必须至少有一个Collider对象有RigidBody组件

  • Trigger对象更高效,所以如果是仅仅只是碰撞检测,而非物理模拟的时候,最好去掉Collider,只保留Trigger即可

Unity中的物理组件Collider部分的优化

  • 尽量少使用MeshCollider,可以用简单Collider代替,即使用多个简单Collider组合代替也要比复杂的MeshCollider来的高效

  • MeshCollider是基于三角形面的碰撞

  • MeshCollider生成的碰撞体网格占用内存也较高

  • MeshCollider即使要用也要尽量保障其是静态物体

  • 可以通过PlayerSetting选项中勾选Prebake Collision Meshes选项来在构建应用时预先Bake出碰撞网格

  • 尽量少使用MeshCollider可以用简单Collider代替,即使用多个简单Collider组合代替也要比复杂的MeshCollider来的高效

  • MeshCollider是基于三角形面的碰撞

  • MeshCollider生成的碰撞体网格占用内存也较高

  • MeshCollider即使要用也要尽量保障其是静态物体

  • 可以通过PlayerSetting选项中勾选Prebake Collision Meshes选项来在构建应用时预先Bake出碰撞网格

Unity中的物理组件RigidBody部分的优化

Kinematic与RigidBody

  • Kinematic对象不受物理引擎中力的影响,但可以对其他RigidBody施加物理影响。

  • RigidBody完全由物理引擎模拟来控制,场景中RigidBody数量越多,物理计算负载越高

  • 勾选了Kinematic选项的RigidBody对象会被认为是Kinematic的,不会增加场景中的RigidBody个数

  • 尽量应保证场景中的RigidBody对象越少越好

Unity中的RayCast与Overlap部分的优化

  • Unity物理中RayCast与Overlap都有NoAlloc版本的函数,在代码中调用时尽量用NoAlloc版本,这样可以避免不必要的GC开销
  • 尽量调用RayCast与Overlap时要指定对象图层进行对象过滤,并且RayCast要还可以指定距离来减少一些太远的对象查询
  • 此外如果是大量的RayCast操作还可以通过RaycastCommand的方式批量处理,充分利用JobSystem来分摊到多核多线程计算。

动画系统的优化

Unity动画系统主要包含三类:1、Animation 2、Animator 2、Playable API

Legacy动画系统是Unity4之前的动画系统,Unity4以后引入了Mecanim,Mecanim对Animation 、Animator、Playable API这三类资源都会用到,其核心是Playable API:

Animation的一些细节

  • 播放单个AnimationClip速度,Animation系统更快,因为老系统是直接采样曲线并直接写入对象Transform (Animation系统更快仅仅只是针对单个AnimationClip)

  • 针对动画的缩放曲线比位移、旋转曲线开销更大

  • 常数曲线不会每帧写入场景,更高效,常数曲线比例越高性能越好

Animator的一些细节

  • 不要使用字符串来查询Animator

  • 使用曲线标记来处理动画事件

  • 使用Target Marching函数来协助处理动画

  • 将Animator的CullingMode设置成Based On Renderers来优化动画,并禁用SkinMesh Renderer的Update When Offscreen属性来让角色不可见时动画不更新

Animator VS Animation

  • Animation可以将任何对象属性制作成Animation Clip, Animator是将Animaiton Clip组织到状态机流程图中使用

  • Animation与Animator播放动画时的效率是有个临界点的,这个临界点是根据动画曲线条数来的,当动画曲线条数小于这个临界点时Animation快,当动画曲线条数大于这个临界点时Animator快

  • 当Cpu核数较少时,Animation播放动画有优势,当Cpu核数较多时,Animator表现会更好

  • Animator Controller Graph中的所有动画节点的Animation Clip都会载入到内存中,当有海量动画状态机节点时,内存开销较大

Playable API VS Animator

Playable API是一套以树形结构来组织数据源,并允许用户通过脚本来创建和播放自定义行为,支持与动画系统,音频系统等其他系统交互(几乎全部带时间轴的资源),Playable API是一套通用的接口;

PlayableGraph Visualizer作为Playable API的可视化插件,可以访问其地址:https://github.com/Unity-Technologies/graph-visualizer

PlayableGraph 使用可以参考:https://docs.unity3d.com/cn/current/Manual/Playables-Graph.html

Playable API优点

  • 支持动态动画混合,可为场景中的对象提供自己的动画,并可以动态添加到PlayableGraph当中使用

  • 允许创建播放单个动画,而并不会产生创建和管理AnimatorController资源所涉及的开销,可更加灵活的控制PlayableGraph的数据流,可以插入自定义的AimationJob。

  • 可以控制动画文件加载策略,按需加载、异步加载等

  • 允许用户动态创建混合图,并直接逐帧控制混合权重(甚至可以混合AniationClip与AnimatorController动画)

  • 可以运行时动态创建,根据条件添加可播放节点。而不需要提前提供一套PlayableGraph运行时启动和禁用节点,可以做到自由度更高的override机制

  • 可加载自定义配置数据,更加方便的和其他游戏系统整合

Playable API缺点

  • 没有直接使用Animator直观

  • 混合模式没有现成的,需要自己手动实现

  • 需要开发更多的配套工具,而且无论对美术和程序员都有一定学习成本

解决方案选择

  • 一些简单、少量曲线动画可以使用Animation或动画区间库如Dotween\iTween等完成,如UI动画,Transform动画等。

  • 角色骨骼蒙皮动画如果骨骼较少,Animation Clip资源不多,对动画混合表现要求不高的项目可以采用Legacy Animation。注意控制总体曲线数量

  • 一些角色动画要求与逻辑有较高的交互、并且动画资源不多的项目可以直接用Animator Graph完成

  • 一些动作游戏,对动画混合要求较高、有一些高级动画效果要求、动画资源量庞大的项目,建议采用Animator+Playable API扩展Timeline的方式完成。

参考资料

主要内容来自Bilibili的Metaverse大衍神君的 2022新年计划《Unity性能优化》 ,其中的大部分笔记摘抄自其PPT作为以后参考。