1. 程式人生 > >使用 Mono.Cecil 輔助 Unity3D 手遊進行性能測試(續)

使用 Mono.Cecil 輔助 Unity3D 手遊進行性能測試(續)

uil tar ron 重命名 def ace 重復 library com

本文嚴禁轉載。 之前的方法及其局限 問題背景和最初的嘗試見這裏。最開始的想法比較簡單,只想著利用 PostprocessBuild 這個事件,來對已經準備好的本地工程文件(iOS 或 Android)中的 .NET 程序集進行註入。但是,這樣做限制很多。 首先,無法對 IL2CPP 作為 Scripting Backend 的情況進行註入。因為觸發這個事件時,本地工程文件中沒有 .NET 程序集,只有 C++ 代碼,無法用 Cecil 進行註入。 第二,Android 平臺,用 Mono2x 作為 Scripting Backend 的情況下,也需要打包為 Android Studio Project 才能使用。對於直接打包成 apk 的情況,無法簡單的進行註入(除非使用解包、註入、重新簽名打包的方法,比較麻煩)。 第三,iOS 平臺,即使用 Mono2x 作為 Scripting Backend,也無法成功。這是因為在 iOS 平臺打包需要先進行一個叫 AOT Cross Compiling 的步驟,對所有的程序集生成對應的 .dll.s 文件。這些文件包含的信息會在運行時被校驗,如果我篡改了程序集,而沒有理會 .dll.s 文件,在運行時會報錯。錯誤信息類似 A script behaviour (probably XXX?) has a different seralization layout when loading. (Read ** bytes but expected ** bytes)
Did you #ifdef UNITY_EDITOR a section of your serialized properties in any of your scripts? 其中 XXX 是 .NET 腳本名稱,兩組星號表示兩個不同值。這錯誤最終導致腳本加載失敗,無法運行遊戲。與錯誤信息描述不同,我並沒有在出問題的腳本上寫任何條件編譯的代碼。要想解決這個問題,估計需要篡改 .dll.s 文件才可以,仍然是很不經濟的。 篡改編譯器的方法 接下來一個辦法,就是對 Unity 的 C# 編譯器 mcs.exe 進行篡改。我沒有深入實驗,因為幾個簡單的實驗就耗費了一天多的時間。我主要嘗試了兩種方法,當然,都沒成功。 方法一,將原 mcs.exe 重命名(如 mcs1.exe),而後自己寫一個 .NET 控制臺應用程序,占據原來 mcs.exe 的位置,在其中用 System.Diagnostic.Process 類來啟動 mcs1.exe。這個過程中,我對 Process 對象的一些配置,如環境變量(EnvironmentVariables 屬性)、輸入輸出重定向(RedirectStandardXXX 屬性)進行了多種排列組合,仍無法正確調用 mcs1.exe,就更不要說調用之後的事情了。 方法二,直接在 mcs.exe 中註入代碼。因為 mcs.exe 也是一個 .NET 應用程序,並且看上去未經混淆,所以直接註入是可行的。即,「把向遊戲程序集中註入代碼的代碼,註入到編譯器中。」這樣做主要的問題,是 mcs.exe 的輸出目錄是臨時文件夾,無法保證其中有我們依賴的(如註入後寫入程序集時,需要用 Mono.Cecil 的 DefaultAssemblyResolver 進行解析的)程序集。
通過PostprocessScene回調事件來進行註入 Unity 雖然沒有在執行 mcs.exe 和後續步驟(IL2CPP、Android 打包 apk、iOS 上的 AOT 交叉編譯等)之間提供回調,但是回調事件 OnPostprocessScene 目前是確保在它們之間至少觸發一次的。多虧 https://github.com/rayosu/UnityDllInjector 提醒了我。在這個事件回調中處理 DLL,理論上在任何平臺、任何 Scripting Backend 上都可以有效註入。實現過程中有幾個要點需要註意:
  • 事件 OnPostprocessScene 對應 Build Settings 中指定打包的場景個數,所以它可能執行多次,故而需要防止重復。除了上述 UnityDllInjector 中提供的方法,還可以直接把註入標記寫入你的目標程序集。但值得註意的是,新增一個用於標記的空類在 iOS + Mono2x 下又是不好用的,猜測還和 AOT 交叉編譯有關。保險的做法之一,是在遊戲代碼中保留幾個 bool 常量,值為 false,註入前檢查相應的值,如果為 true 則跳過,否則註入。註入完成後,將相應的 bool 常量篡改為 true 即可。
  • 遊戲腳本對應的程序集,在註入時一定處於和 Assets 同級的 Library 下的 ScriptAssemblies 文件夾下,但要註意你依賴的 Unity 程序集。我使用 UnityDllInjector 提供的方法,依然不能保證獲取到需要的程序集。最終我采用的方法是,使用 EditorApplication.applicationContentsPath 獲取 Unity 安裝目錄,在其中 Data/Managed 目錄裏尋找必要的程序集。
目前我測試了 Android + Mono/IL2CPP 和 iOS + IL2CPP,都沒有問題。iOS + Mono2x 可能由於我們項目本身的一些問題,在 Xcode 鏈接階段有一些問題。

使用 Mono.Cecil 輔助 Unity3D 手遊進行性能測試(續)