更新
- 2019/04/06 增加正确时机解释
- 2017/12/17 初次发布
环境
- Unity 2017.1.1f1
- macOS 10.11.6
- iOS 11.1
- Android 5.1
问题
在一个有加载卸载资源的 UI 中,发现在某些情况下会导致 Unity 闪退,包括 iOS、Android、macOS 平台下 Unity 编辑器。
确定在一定的操作后必然发生,尝试精简重现步骤,最后只需要固定两步操作即可重现。
macOS 平台下 Unity 编辑器崩溃日志:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
Assertion failed on expression: 'MecanimDataWasBuilt()'
(Filename: /Users/builduser/buildslave/unity/build/Runtime/Animation/AnimationClip.cpp Line: 1742)
Receiving unhandled NULL exception
Obtained 29 stack frames.
#0 0x0000010490db40 in typeinfo for EditorExtension
#1 0x00000101e78084 in mecanim::statemachine::SetStateMachineInInitialState(mecanim::statemachine::StateMachineConstant const&, mecanim::statemachine::StateMachineInput const&, mecanim::statemachine::StateMachineOutput&, mecanim::statemachine::StateMachineMemory&, mecanim::statemachine::StateMachineWorkspace&)
#2 0x00000101de949f in AnimatorControllerPlayable::GenerateGraph()
#3 0x00000101de6bb0 in AnimatorControllerPlayable::SetAnimatorController(RuntimeAnimatorController*)
#4 0x00000101d33bb5 in Animator::CreateInternalControllerPlayable()
#5 0x00000101d22d08 in Animator::CreateObject()
#6 0x00000101d254a1 in Animator::UpdateAvatars(dynamic_array<PlayableOutput*, 8ul> const&, bool, bool, bool)
#7 0x000001007ea46f in DirectorManager::ExecuteProcessCallbacks(DirectorStage)
#8 0x000001007e7dc0 in DirectorManager::InitializeClass()::PreLateUpdateDirectorUpdateAnimationBeginRegistrator::Forward()
#9 0x00000100ab592b in PlayerLoop()
#10 0x00000101834a74 in PlayerLoopController::UpdateScene(bool)
#11 0x0000010183046c in PlayerLoopController::UpdateSceneIfNeeded()
#12 0x0000010182f7a4 in Application::TickTimer()
#13 0x007fffbff9de0f in __NSFireTimer
#14 0x007fffbe513c54 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
#15 0x007fffbe5138df in __CFRunLoopDoTimer
#16 0x007fffbe51343a in __CFRunLoopDoTimers
#17 0x007fffbe50ab81 in __CFRunLoopRun
#18 0x007fffbe50a114 in CFRunLoopRunSpecific
#19 0x007fffbda6aebc in RunCurrentEventLoopInMode
#20 0x007fffbda6acf1 in ReceiveNextEventCommon
#21 0x007fffbda6ab26 in _BlockUntilNextEventMatchingListInModeWithFilter
#22 0x007fffbc003a54 in _DPSNextEvent
#23 0x007fffbc77f7ee in -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:]
#24 0x007fffbbff83db in -[NSApplication run]
#25 0x007fffbbfc2e0e in NSApplicationMain
#26 0x0000010044be37 in EditorMain(int, char const**)
#27 0x0000010044c2b9 in main
#28 0x007fffd3c92235 in start
Launching bug reporter
|
分析
由于崩溃堆栈中出现了 AnimatorControllerPlayable::SetAnimatorController
调用,尝试在崩溃操作前将场景内所有 Animator 组件禁用。
结果发现在崩溃操作后编辑器没有崩溃,仔细查看场景,发现一个模型已经消失不见。
解决
由于资源管理是使用 AssetBundle,所以当资源消失时优先查找是否是由 AssetBundle.Unload(true)
导致,在日志中输出后发现的确如此。
所以最终是由于游戏仍在使用 Animator 及其动画,如果 AssetBundle.Unload(true)
将所有资源卸载会导致资源重建,结果就是之前的崩溃问题了。
解决方法也很简单,在正确的时机调用 AssetBundle.Unload(true)
就可以了。
有评论问到:正确的时机是指什么?
其实很简单,就是资源不再被使用的时候。
注意,卸载的时候要分两步:
- 销毁 Destroy 场景中实例化 Instantiate 出来的 GameObject,确保场景中没有对象引用到资源
- 执行 AssetBundle.Unload(true),使用 AssetBundle 内部的引用追踪将所有从当前 AssetBundle 中加载的资源卸载掉