[FA] 自定义Animator更新进度的实现与测试
2023-04-25 本文已影响0人
_Walker__
记录环境
- Unity 2021.3.21f1
- Timeline 1.6.4
问题背景
为了解决[RS] Timeline踩坑(3):多轨道播放不同步中【案例1:执行时长差异】的问题,我想了一个处理方案:
我们自己在脚本里更新Animator,禁用Unity默认的行为。
保证Animator、TML都是以30fps的固定时间步执行。
// 停止Animator自己身的更新
animator.playableGraph.SetTimeUpdateMode(DirectorUpdateMode.Manual);
private const float Step = 1 / 30f;
private float _total = 0;
void Update()
{
_total += Time.deltaTime;
while (_total >= Step)
{
_total -= Step;
foreach (Animator animator in _animators)
{
animator.Update(Step);
}
}
}
经测试,用上面的代码可以达到目的,但是有严重的性能问题。我在红米6上进行测试,同时更新30个Animator,每帧主线程耗时能高达30ms,这是完全无法接受的。(耗时参考下图)
从Profiler的性能对比来看,自定义的更新比Unity内部更新,耗时多出了2倍,性能差距非常大。但是在观察Profiler的Timeline模式时,子线程(Job)做的事情耗时并没有很高。我们推测性能差距主要不在于多线程,而是执行次数。
- 固定30fps执行频率,会在一个Unity帧里执行多次动画模拟,耗时自然要多几倍
- 自己的脚本是循环每个Animator逐个执行它们的Update,Unity内部可能有类似批处理的方式
带着这个推测,我们确实也找到了Unity给出的解决方案[1],下面是核心实现。
用AnimatorControllerPlayable将多个Animator连接到一个PlayableGraph上,然你通过这一个PlayableGraph批量更新所有的Animator
private const float Step = 1 / 30f;
private float _total = 0;
private Animator[] _animators;
private PlayableGraph _globalAniGraph;
void Awake()
{
_globalAniGraph = PlayableGraph.Create("GlobalAnimatorGraph");
_globalAniGraph.SetTimeUpdateMode(DirectorUpdateMode.Manual);
_animators = GetComponentsInChildren<Animator>();
for (int i = 0; i < _graphs.Length; ++i)
{
Animator animator = _animators[i];
var playable = AnimatorControllerPlayable.Create(_globalAniGraph, animator.runtimeAnimatorController);
var output = AnimationPlayableOutput.Create(_globalAniGraph, animator.name + "Output", animator);
output.SetSourcePlayable(playable);
}
}
void Update()
{
_total += Time.deltaTime;
while (_total >= Step)
{
_total -= Step;
_globalAniGraph.Evaluate(Step);
}
}
性能对比图
上图是我做的几种性能测试,都是在红米6上,同时执行30个Animator。从左到右依次是:
- Unity默认的Animator自动更新
- 以30fps的固定帧率在脚本里,逐个调用Animator.Update(最上面代码的做法)
- 以30fps的固定帧率,用一个PlayableGraph更新所有Animator(第2段代码的做法)
- 用一个PlayableGraph更新所有Animator,每个Unity帧只执行一次
从结果看,做法4的性能跟原生(做法1)的几乎一致。做法3与2相比也有比较大的提升。为了解决不同步问题,需要的做法是3,但由于一帧内执行的Evaluate次数多,它仍有很高的性能消耗。所以,暂时不准备落实到项目里。
参考文章:
[1] Prevent Animator from auto updating?
[2] PlayableGraph Evaluate performance/best practices