Unity关闭游戏预览时崩溃的解决方法

问题

在使用Unity 2017.4.16开发时经常遇到关闭游戏预览的时候Unity Crash的情况,经过查询log文件可以找到出问题的原因。要注意的是,每一次运行Unity的时候会重新创建这个文件,所以崩溃之后要立即去查看或者把日志保存下来。

分析

Unity Editor的崩溃日志存放在C:\Users\YOURNAME\AppData\Local\Unity\Editor\Editor.log,崩溃之后可以查看这个日志,我这里崩溃部分的日志是这样的(而且每次崩溃都一样):

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
========== OUTPUTING STACK TRACE ==================

0x00007FFD006892BB (AkSoundEngine) AK::MemoryMgr::Term
0x00007FFD006881FE (AkSoundEngine) AK::MemoryMgr::Free
0x00007FFD005A8F98 (AkSoundEngine) AK::SoundEngine::Term
ERROR: SymGetSymFromAddr64, GetLastError: '试图访问无效的地址。

' (Address: 00007FFD00595277)
0x00007FFD00595277 (AkSoundEngine) (function-name not available)
0x000000005F51B0BD (Mono JIT Code) (wrapper managed-to-native) AkSoundEnginePINVOKE:CSharp_Term ()
0x000000005F51AF9A (Mono JIT Code) [C:\WorkSpace\Client\Assets\Wwise\Deployment\API\Generated\Windows\AkSoundEngine_Windows.cs:2331] AkSoundEngine:Term ()
0x000000005F5190A8 (Mono JIT Code) [C:\WorkSpace\Client\Assets\Wwise\Deployment\Components\AkTerminator.cs:107] AkTerminator:Terminate ()
0x000000005F518925 (Mono JIT Code) [C:\WorkSpace\Client\Assets\Wwise\Deployment\Components\AkTerminator.cs:57] AkTerminator:OnApplicationQuit ()
0x0000000000D87472 (Mono JIT Code) (wrapper runtime-invoke) object:runtime_invoke_void__this__ (object,intptr,intptr,intptr)
0x00007FFCD7A864D3 (mono) [c:\buildslave\mono\build\mono\mini\mini.c:4937] mono_jit_runtime_invoke
0x00007FFCD79D8A2D (mono) [c:\buildslave\mono\build\mono\metadata\object.c:2623] mono_runtime_invoke
0x0000000140A34E2C (Unity) scripting_method_invoke
0x0000000140A287FA (Unity) ScriptingInvocation::Invoke
0x00000001409F3D28 (Unity) MonoBehaviour::HandleNotifications
0x00000001403BF961 (Unity) MessageHandler::HandleMessage
0x00000001403C7634 (Unity) GameObject::SendMessageAny
0x00000001407186D2 (Unity) SendMessageToEveryone
0x0000000140720DBF (Unity) NotifyPlayerQuit
0x00000001411F0FE5 (Unity) PlayerLoopController::ExitPlayMode
0x00000001411F17AD (Unity) PlayerLoopController::SetIsPlaying
0x00000001411F25F6 (Unity) Application::TickTimer
0x00000001414198DF (Unity) MainMessageLoop
0x000000014141B19C (Unity) WinMain
0x0000000141E74068 (Unity) __tmainCRTStartup
0x00007FFD21B47974 (KERNEL32) BaseThreadInitThunk
0x00007FFD2401A271 (ntdll) RtlUserThreadStart

========== END OF STACKTRACE ===========

通过分析和查询资料可以看出崩溃和AkSoundEngine有关系,Wwise是Audiokinetic开发的游戏互动音频引擎,支持Unity和Unreal等游戏引擎,AkSoundEngine就是Wwise中的东西。

在Unity中,运行游戏的时候会创建AkInitializer,退出游戏的时候会销毁。AkInitializer使用的是Lazy模式的初始化,通过设置断点发现编辑器的游戏预览退出的的时候,偶尔会在MonoBehaviour的OnDisable()方法中重新创建的音频管理器对象,也就是说有了两个,但是这种方式又是Audiokinetic允许的。因此,退出游戏的时候可能销毁的不是一开始创建的那个音频管理器对象,就会崩溃。

解决方法

也就是说,我们要保证销毁器销毁的是创建的那个对象,所以要修改AKTerminator.cs这个文件,在里面加上一个判断:

1
2
3
4
5
6
7
8
9
void OnEnable()
{
//The sound engine was not terminated normally. Make this instance the one that will manage termination.
//This happen when Unity resets everything when a script changes.
if (ms_Instance == null && AkSoundEngine.IsInitialized())
{
ms_Instance = this;
}
}

加上OnEnable这个函数之后果然没有再崩溃了。

注意事项

使用这个解决方法可以解决大部分情况下的崩溃,但是如果在游戏运行时修改代码,然后再关闭游戏依然会导致崩溃,似乎是必现。

参考链接:unity crash when exit the application in play-in-editor using AkTerminator.cs