轉載請標明原文地址:https://segmentfault.com/a/11...
一、ILRuntime介紹
問:什么是熱更新?
答:軟件在使用時就能實現更新的方式就叫做熱更新。熱更新無需用戶重新下載安裝或重啟,在使用時即可更新,方便快捷體驗良好。
問:什么是ILRuntime?
答:ILRuntime是一個C#熱更新方案。ILRuntime項目為基於C#的平台(例如Unity)提供了一個純C#實現,快速、方便且可靠的IL運行時,使得能夠在不支持JIT的硬件環境(如iOS)能夠實現代碼的熱更新
問:lua 和 ILRuntime哪個熱更新方案更好?
答:如果你的團隊更熟悉lua,就用lua。如果你的團隊更熟悉C#就用ILRuntime。如果你是主程,你可以選擇自己喜歡的方案,但是要肩負起填坑的責任。
我個人的感覺是:lua在Unity中用起來很難受,不是lua這門語言不好,而是因為在Unity中官方的開發語言是C#。用lua就意味着開發者要會兩種語言,學習和開發成本都高,而且因為C#是強類型、面向對象的語言。lua是弱類型,非面向對象的語言。
lua從編程思想和代碼寫法都和C#有較大差距,這一點在面對越大的項目時感受越明顯,項目小的時候覺得lua還好,項目做大了以后會發現lua帶給你的麻煩會大於便利。
而ILRuntime方案是基於C#的,開發語言統一,編碼更容易。不過他的缺點是實際經過驗證的項目還是太少了,不太成熟,可能有很多坑需要填,不像是經過很多項目驗證的lua,有比較成熟的方案。
二、下載ILRuntime
GitHub:https://github.com/Ourpalm/IL...
Unity Demo:https://github.com/Ourpalm/IL...
國內碼雲:https://gitee.com/zhangyu800/...
中文手冊:http://ourpalm.github.io/ILRu...
先去GitHub上點個贊,支持一下該項目,再去Unity Demo上把項目下載下來,順便也點個贊。
如果國外地址下載慢,可以在國內碼雲上下載Demo。
三、導入ILRuntime
1.解壓縮Unity Demo
打開Unity工程目錄下的ProjectSettings/ProjectVersion.txt 查看工程版本。
為了避免因為不同版本導致的兼容問題,工程版本和Unity版本盡量保持一致,我下載的Demo工程版本是2019.3.6f1, 我盡量用2019.3.6 或稍微高一點兒的版本導入。
2.目錄結構
工程導入完畢,看下目錄結構。
Demo目錄:
在Samples/ILRuntime/1.6.2/Demo/_Scenes/Examples文件夾下
熱更新加載的代碼目錄:
熱更新代碼會從StreamingAssets目錄下加載編譯后的dll
其中mdb和pdb文件都是調試時用的,發布時只需要dll。
運行Hello World Demo:
打開並運行 01_Hello World 場景。可以看到控制台輸出了如下結果。
ILR是如何工作的呢?看下HelloWorld腳本。
using UnityEngine; using System.Collections; using System.IO; using ILRuntime.Runtime.Enviorment; public class HelloWorld : MonoBehaviour { //AppDomain是ILRuntime的入口,最好是在一個單例類中保存,整個游戲全局就一個,這里為了示例方便,每個例子里面都單獨做了一個 //大家在正式項目中請全局只創建一個AppDomain AppDomain appdomain; System.IO.MemoryStream fs; System.IO.MemoryStream p; void Start() { StartCoroutine(LoadHotFixAssembly()); } IEnumerator LoadHotFixAssembly() { //首先實例化ILRuntime的AppDomain,AppDomain是一個應用程序域,每個AppDomain都是一個獨立的沙盒 appdomain = new ILRuntime.Runtime.Enviorment.AppDomain(); //正常項目中應該是自行從其他地方下載dll,或者打包在AssetBundle中讀取,平時開發以及為了演示方便直接從StreammingAssets中讀取, //正式發布的時候需要大家自行從其他地方讀取dll //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //這個DLL文件是直接編譯HotFix_Project.sln生成的,已經在項目中設置好輸出目錄為StreamingAssets,在VS里直接編譯即可生成到對應目錄,無需手動拷貝 //工程目錄在Assets\Samples\ILRuntime\1.6\Demo\HotFix_Project~ //以下加載寫法只為演示,並沒有處理在編輯器切換到Android平台的讀取,需要自行修改 #if UNITY_ANDROID WWW www = new WWW(Application.streamingAssetsPath + "/HotFix_Project.dll"); #else WWW www = new WWW("file:///" + Application.streamingAssetsPath + "/HotFix_Project.dll"); #endif while (!www.isDone) yield return null; if (!string.IsNullOrEmpty(www.error)) UnityEngine.Debug.LogError(www.error); byte[] dll = www.bytes; www.Dispose(); //PDB文件是調試數據庫,如需要在日志中顯示報錯的行號,則必須提供PDB文件,不過由於會額外耗用內存,正式發布時請將PDB去掉,下面LoadAssembly的時候pdb傳null即可 #if UNITY_ANDROID www = new WWW(Application.streamingAssetsPath + "/HotFix_Project.pdb"); #else www = new WWW("file:///" + Application.streamingAssetsPath + "/HotFix_Project.pdb"); #endif while (!www.isDone) yield return null; if (!string.IsNullOrEmpty(www.error)) UnityEngine.Debug.LogError(www.error); byte[] pdb = www.bytes; fs = new MemoryStream(dll); p = new MemoryStream(pdb); try { appdomain.LoadAssembly(fs, p, new ILRuntime.Mono.Cecil.Pdb.PdbReaderProvider()); } catch { Debug.LogError("加載熱更DLL失敗,請確保已經通過VS打開Assets/Samples/ILRuntime/1.6/Demo/HotFix_Project/HotFix_Project.sln編譯過熱更DLL"); } InitializeILRuntime(); OnHotFixLoaded(); } void InitializeILRuntime() { #if DEBUG && (UNITY_EDITOR || UNITY_ANDROID || UNITY_IPHONE) //由於Unity的Profiler接口只允許在主線程使用,為了避免出異常,需要告訴ILRuntime主線程的線程ID才能正確將函數運行耗時報告給Profiler appdomain.UnityMainThreadID = System.Threading.Thread.CurrentThread.ManagedThreadId; #endif //這里做一些ILRuntime的注冊,HelloWorld示例暫時沒有需要注冊的 } void OnHotFixLoaded() { //HelloWorld,第一次方法調用 appdomain.Invoke("HotFix_Project.InstanceClass", "StaticFunTest", null, null); } private void OnDestroy() { if (fs != null) fs.Close(); if (p != null) p.Close(); fs = null; p = null; } void Update() { } }
驚喜!注釋是純中文的,作者一定是中國人!注釋很詳細,看注釋就行了。
其中比較重要的代碼是:
//這個DLL文件是直接編譯HotFix_Project.sln生成的,已經在項目中設置好輸出目錄為StreamingAssets,在VS里直接編譯即可生成到對應目錄,無需手動拷貝 //工程目錄在Assets\Samples\ILRuntime\1.6\Demo\HotFix_Project~ //以下加載寫法只為演示,並沒有處理在編輯器切換到Android平台的讀取,需要自行修改 WWW www = new WWW("file:///" + Application.streamingAssetsPath + "/HotFix_Project.dll"); //HelloWorld,第一次方法調用 appdomain.Invoke("HotFix_Project.InstanceClass", "StaticFunTest", null, null);
打開HotFix_Project:
HotFix_Project 是Demo自帶的C#熱更新代碼工程。
工程目錄在
Assets\Samples\ILRuntime\1.6\Demo\HotFix_Project~文件夾下,用Visual Studio打開該工程。
注意:HotFix_Project~ 后面帶了個波浪號,Unity會自動忽略該目錄,資源不會被導入,代碼不會被編譯,文件不會帶進發布包。
具體規則可以看官方文檔:
https://docs.unity3d.com/Manu...
修改熱更新代碼:
打開HotFix項目下的InstanceClass類 修改StaticFunTest方法中輸出的內容:
public static void StaticFunTest() { UnityEngine.Debug.Log("調用了熱更新類的靜態方法!"); }
生成熱更新代碼dll:
右鍵項目 > 生成
重新運行Hello World Demo:
查看輸出結果,可以看到 我們修改的內容被輸出了。
通過以上步驟,了解了IRuntime的基本運行流程。
1.熱更代碼生成dll
2.Unity加載dll
3.Unity調用dll里的方法
三、創建自己的Hotfix工程
從零開始創建自己的熱更工程,體驗完整配置流程。
1.打開Visual Studio,新建項目。
2.修改項目配置
選擇類庫(.NET Framework)。
修改名稱為 Hotfix
修改路徑為 Unity項目根目錄
3.添加UnityEngine的引用
在Unity中用Visual Studio打開Unity Demo的任意一個腳本,VS會自動關聯需要的Unity類庫引用,在項目的引用中可以看到引用的dll路徑。
在我們自己的Hotfix工程引用中添加該文件。
有兩個主要的路徑
(1)在Unity安裝目錄下的Editor\Data
我的電腦路徑是:D:\SDK\Unity\2019.4.1f1\Editor\Data\Managed\UnityEngine\
把該文件夾內所有dll文件都引入到Hotfix工程里。
(2)在Unity工程根目錄下的Library\ScriptAssemblies\
我的電腦路徑是:
D:\Projects\Unity3D\Unity 2019.3 Projects\ILRuntimeU3D\ILRuntimeDemo\Library\ScriptAssemblies
添加自己需要的dll,我引用了以下兩個dll。
Assembly-CSharp.dll
UnityEngine.UI.dll
注:Unity 2019中已經把類庫拆的很零散了,應該是為了解耦,為了以后的規划。他把類庫拆成兩個部分,內置的類庫放在Unity安裝目錄下了,PackageManager包管理器下載的插件都放在Unity工程目錄下了,UGUI的庫從內置位置移動到PackageManager里了,可能以后要淘汰掉。
4.編寫 Hello World
添加完引用,可以開始寫代碼了。
在我們自己的Hotfix工程中添加一個類 HelloWorld.cs 並輸入以下代碼:
namespace Hotfix { using UnityEngine; // 冰封百度的Blog:https://segmentfault.com/a/1190000023183723 public class HelloWorld { public void Test() { Debug.Log("Hello World"); } } }
5.配置Hotfix.dll輸出路徑
右鍵Hotfix工程, 選擇生成選項卡,在下方的輸出路徑里瀏覽到StreamingAssets文件夾或填寫相對路徑:..\..\Assets\StreamingAssets\
5.生成Hotfix.dll
右鍵Hotfix工程,選擇生成。
生成成功后可以看到如下提示,如果失敗則根據錯誤提示進行處理。
6.修改Unity Demo中的HelloWorld腳本
主要修改腳本中加載dll的名稱,HotFix_Project改為Hotfix。
並且添加調用熱更方法的代碼。
修改后如下:
using UnityEngine; using System.Collections; using System.IO; using ILRuntime.Runtime.Enviorment; public class HelloWorld : MonoBehaviour { //AppDomain是ILRuntime的入口,最好是在一個單例類中保存,整個游戲全局就一個,這里為了示例方便,每個例子里面都單獨做了一個 //大家在正式項目中請全局只創建一個AppDomain AppDomain appdomain; System.IO.MemoryStream fs; System.IO.MemoryStream p; void Start() { StartCoroutine(LoadHotFixAssembly()); } IEnumerator LoadHotFixAssembly() { //首先實例化ILRuntime的AppDomain,AppDomain是一個應用程序域,每個AppDomain都是一個獨立的沙盒 appdomain = new ILRuntime.Runtime.Enviorment.AppDomain(); //正常項目中應該是自行從其他地方下載dll,或者打包在AssetBundle中讀取,平時開發以及為了演示方便直接從StreammingAssets中讀取, //正式發布的時候需要大家自行從其他地方讀取dll //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //這個DLL文件是直接編譯HotFix_Project.sln生成的,已經在項目中設置好輸出目錄為StreamingAssets,在VS里直接編譯即可生成到對應目錄,無需手動拷貝 //工程目錄在Assets\Samples\ILRuntime\1.6\Demo\HotFix_Project~ //以下加載寫法只為演示,並沒有處理在編輯器切換到Android平台的讀取,需要自行修改 #if UNITY_ANDROID WWW www = new WWW(Application.streamingAssetsPath + "/Hotfix.dll"); #else WWW www = new WWW("file:///" + Application.streamingAssetsPath + "/Hotfix.dll"); #endif while (!www.isDone) yield return null; if (!string.IsNullOrEmpty(www.error)) UnityEngine.Debug.LogError(www.error); byte[] dll = www.bytes; www.Dispose(); //PDB文件是調試數據庫,如需要在日志中顯示報錯的行號,則必須提供PDB文件,不過由於會額外耗用內存,正式發布時請將PDB去掉,下面LoadAssembly的時候pdb傳null即可 #if UNITY_ANDROID www = new WWW(Application.streamingAssetsPath + "/Hotfix.pdb"); #else www = new WWW("file:///" + Application.streamingAssetsPath + "/Hotfix.pdb"); #endif while (!www.isDone) yield return null; if (!string.IsNullOrEmpty(www.error)) UnityEngine.Debug.LogError(www.error); byte[] pdb = www.bytes; fs = new MemoryStream(dll); p = new MemoryStream(pdb); try { appdomain.LoadAssembly(fs, p, new ILRuntime.Mono.Cecil.Pdb.PdbReaderProvider()); } catch { Debug.LogError("加載熱更DLL失敗,請確保已經通過VS打開Assets/Samples/ILRuntime/1.6/Demo/HotFix_Project/HotFix_Project.sln編譯過熱更DLL"); } InitializeILRuntime(); OnHotFixLoaded(); } void InitializeILRuntime() { #if DEBUG && (UNITY_EDITOR || UNITY_ANDROID || UNITY_IPHONE) //由於Unity的Profiler接口只允許在主線程使用,為了避免出異常,需要告訴ILRuntime主線程的線程ID才能正確將函數運行耗時報告給Profiler appdomain.UnityMainThreadID = System.Threading.Thread.CurrentThread.ManagedThreadId; #endif //這里做一些ILRuntime的注冊,HelloWorld示例暫時沒有需要注冊的 } void OnHotFixLoaded() { //HelloWorld,第一次方法調用 //appdomain.Invoke("HotFix_Project.InstanceClass", "StaticFunTest", null, null); // 實例化Hotfix中的類 object obj = appdomain.Instantiate("Hotfix.HelloWorld"); appdomain.Invoke("Hotfix.HelloWorld", "Test", obj); } private void OnDestroy() { if (fs != null) fs.Close(); if (p != null) p.Close(); fs = null; p = null; } }
運行Unity,可以看到輸出了Hello World
至此,ILRuntime Hello World部分已經全部完成了。
總結:
ILRuntime使用時,除了Hotfix工程的配置稍微麻煩一些外,其他部分都不難,Demo做的很完善,基本是開箱即用。具體使用細節建議仔細閱讀官方文檔:
http://ourpalm.github.io/ILRu...