记录环境
- Unity 2021.3.4f1
- Timeline 1.6.4
问题描述
首先说明下,这个报错不是Unity直接抛出的,它就是我们写的轨道代码里,访问了一个已经被Destroy的对象。但是,导致这个报错出现,不是因为我们用法不合理!!!
我们项目在切换情景的时候,会将当前情景的资源“全部卸载”,其中就包含TML及其控制的角色。
我们在主城切换到副本的时候,爆出来这个MissingReferenceException
的问题。检查后发现项目中资源释放的逻辑没有问题,轨道里的代码也符合制作规范。
统一资源释放流程(部分):
1.先停止所有正在播放的TML
2.释放/销毁资源,这里包含TML、角色等(这一步的顺序不重要)
轨道实现的规范(部分):
【轨道还原】在离开Clip、退出或销毁Track等时机时,要尽量还原被控角色的状态
轨道还原是因为我们TML主要用在战斗中做技能表演,每个技能释放完毕后,需要角色还原到自己的出场站位,所以大部分轨道都需要做还原这个处理。
基于TML的设定,我们在做资源释放时,就要保证上面的顺序。因为TML结束的时候,仍然会访问角色的对象,故TML需要先停止。
原因说明
经过进一步打Log测试,我发现TML的停止跟Playable资源的停止并不是同步进行的。
测试得到一个结论:
PlayableDirector.Stop()与PlayerBehaviour.OnPlayableDestroy()是在同一帧执行,但不是同步执行,OnPlayableDestroy会晚一些。
测试的Log流程(下面这些全部在同一个Update帧输出):
-
PlayableDirector.Stop()
【主动停止TML】 -
TimelineCtrl.OnDestroy()
【停止TML后,销毁角色跟TML】 -
Update()
开始【停止TML时,做了个标记让某个脚本的Update在第一行输出Log】 -
Update()
结束【同上,在最后一行输出Log】 -
PlayerBehaviour.OnPlayableDestroy()
【Unity内部在UpdateDirectorUpdateRegistrator::Forward
调过来】 - Frame End【停止TML时,开了个协程在
WaitForEndOfFrame
之后输出Log】
// OnPlayableDestroy的执行堆栈
MoleTimeline.MoleStateMixer:OnPlayableDestroy (UnityEngine.Playables.Playable)
...
0x00007ff7ae5e01d4 (Unity) ScriptingInvocation::Invoke
0x00007ff7ae56f5c0 (Unity) PlayableMethods::InvokePlayableDestroy
0x00007ff7ae562112 (Unity) Playable::DeallocateResources
0x00007ff7ae568862 (Unity) PlayableGraph::DestroyPendingPlayables
0x00007ff7ae568387 (Unity) PlayableGraph::Destroy
0x00007ff7ae56c876 (Unity) DirectorManager::ProcessPlayStateChanges
0x00007ff7ae56a657 (Unity) `DirectorManager::InitializeClass'::`2'::UpdateDirectorUpdateRegistrator::Forward
0x00007ff7ae2a437c (Unity) ExecutePlayerLoop
0x00007ff7ae2a4453 (Unity) ExecutePlayerLoop
0x00007ff7ae2aa099 (Unity) PlayerLoop
推测: Unity内部维护了一个Playable的列表,并在Update里每帧去做清理,PlayableDirector.Stop只做了一个可销毁的标记。
解决方案
暂时没找到比较好的解决方案,只是在每个Track的代码里,自己判断要用的资源是否合法。
网友评论