(轉)Unity C#熱更新方案 ILRuntime學習筆記(一) Hello World

轉載請標明原文地址: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。

ILRuntimeDemo.png

三、導入ILRuntime

1.解壓縮Unity Demo
打開Unity工程目錄下的ProjectSettings/ProjectVersion.txt 查看工程版本。

爲了避免因爲不同版本導致的兼容問題,工程版本和Unity版本儘量保持一致,我下載的Demo工程版本是2019.3.6f1, 我儘量用2019.3.6 或稍微高一點兒的版本導入。

ProjectVersion.png

2.目錄結構

工程導入完畢,看下目錄結構。

Demo目錄:
在Samples/ILRuntime/1.6.2/Demo/_Scenes/Examples文件夾下

目錄結構.png

熱更新加載的代碼目錄:
熱更新代碼會從StreamingAssets目錄下加載編譯後的dll
其中mdb和pdb文件都是調試時用的,發佈時只需要dll。

StreamingAssets文件夾2.png

運行Hello World Demo:

打開並運行 01_Hello World 場景。可以看到控制檯輸出瞭如下結果。
01_Hello World.png

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:
右鍵項目 > 生成
右鍵項目 生成.png

重新運行Hello World Demo:
查看輸出結果,可以看到 我們修改的內容被輸出了。

Log.png

通過以上步驟,瞭解了IRuntime的基本運行流程。
1.熱更代碼生成dll
2.Unity加載dll
3.Unity調用dll裏的方法

三、創建自己的Hotfix工程

從零開始創建自己的熱更工程,體驗完整配置流程。
1.打開Visual Studio,新建項目。
新建 項目.png

2.修改項目配置
選擇類庫(.NET Framework)。
修改名稱爲 Hotfix
Hotfix 項目設置.png

修改路徑爲 Unity項目根目錄
Hotfix 文件夾.png

3.添加UnityEngine的引用
在Unity中用Visual Studio打開Unity Demo的任意一個腳本,VS會自動關聯需要的Unity類庫引用,在項目的引用中可以看到引用的dll路徑。
Hotfix 引用路徑.png

在我們自己的Hotfix工程引用中添加該文件。

有兩個主要的路徑

(1)在Unity安裝目錄下的Editor\Data
我的電腦路徑是:D:\SDK\Unity\2019.4.1f1\Editor\Data\Managed\UnityEngine\
把該文件夾內所有dll文件都引入到Hotfix工程裏。
拷貝dll.png

(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\
Hotfix 輸出路徑.png

5.生成Hotfix.dll
右鍵Hotfix工程,選擇生成。
生成成功後可以看到如下提示,如果失敗則根據錯誤提示進行處理。
Hotfix 生成成功.png

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
Hotfix Hello World 輸出.png

至此,ILRuntime Hello World部分已經全部完成了。

總結:
ILRuntime使用時,除了Hotfix工程的配置稍微麻煩一些外,其他部分都不難,Demo做的很完善,基本是開箱即用。具體使用細節建議仔細閱讀官方文檔:
http://ourpalm.github.io/ILRu...

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章