Unity-C热更新方案-ILRuntime学习笔记一-Hello-World

95次阅读

共计 9039 个字符,预计需要花费 23 分钟才能阅读完成。

转载请表明原文地址:https://segmentfault.com/a/11…

一、什么是 ILRuntime

问:什么是 ILRuntime?
答:一个 C# 热更新计划。

问:什么是热更新?
答:在不从新下载安装利用包的状况下更新利用内容的形式就叫做热更新。

问:为什么要热更新?
答:为了更好的用户体验。

问:那热更新和非热更新有什么不同吗?
答:用户应用更不便,开发更麻烦。

问:用户不便在哪?开发麻烦在哪?
答:用户不便在当利用更新时,关上利用走个进度条就能用,不必从新下载安装,省心、省时、省流量。
开发麻烦在开发后期的框架搭建,开发中的细节,发开后的测试都减少了肯定难度。

问:产生这些麻烦的起因是什么?
答:次要起因在苹果平台,用 Unity 做挪动端利用,通常兼容两个常见平台: 安卓和苹果。苹果为了用户的平安,对开发者的利用做了限度,不容许开发者用惯例形式更新利用的代码(JIT)。简略来说,就是其余平台都没太大问题,能够很容易的热更新,而苹果的限度让咱们必须用其余办法做热更新。当初支流的热更新计划都是因为能够绕过苹果的限度才流行起来的。就像是你出国吃饭的时候饭店不容许你用筷子,因为他们是西餐厅,只提供了刀叉,但餐具费很贵。你既用不惯刀叉又不想花冤枉钱,然而没方法,你在国外,你只能入乡随俗。好在饭店没明确规定不能够自带餐具,于是你带了一种不是筷子但能吃饭的餐具,饭店也就睁一只眼闭一只眼了。这饭店就相当于苹果,餐具就相当于热更新计划。

问:热更新这么麻烦啊,那怎么解决这个问题呢?
答:想要从基本解决这个问题,要么别去苹果饭店吃饭 (那就没这个问题了), 要么苹果饭店解除限制(不太可能),要么苹果饭店转行了(huang le)。
因为从根本上难以解决该问题,所以当初都是折中的方法,其实是没方法的方法,你要去苹果饭店吃饭就要恪守苹果饭店的规矩。
有些餐具在吃饭时遇到的问题比拟少,吃饭比较顺利。有些餐具在吃饭时遇到的问题比拟多,吃饭不太顺利。于是问题较少的就逐步风行开来,问题较多的就逐步败落,当初支流的热更新计划是 Lua,ILRuntime 应该算是非主流,因为他比拟小众,没流行起来。

问:那为什么不必支流的 lua, 要用非主流的 ILRuntime?
答:因为 lua 在 Unity 中用起来很好受,不是 lua 这门语言不好,而是因为在 Unity 中官网的开发语言是 C#。用 lua 就意味着开发者要会两种语言,学习和开发成本都高,而且因为 C# 是强类型、面向对象的语言。lua 是弱类型,非面向对象的语言。
lua 从编程思维和代码写法都和 C# 有较大差距,这一点在面对越大的我的项目时感触越显著,我的项目小的时候感觉 lua 还好,我的项目做大了当前会发现 lua 带给你的麻烦会大于便当。
而 ILRuntime 计划是基于 C# 的,开发语言对立,编码更容易。不过他的毛病是理论通过验证的我的项目还是太少了,不太成熟,可能有很多坑须要填,不像是通过很多我的项目验证的 lua,有比拟成熟的计划。

集体倡议:
如果你用 lua 更纯熟,更喜爱 lua,就用 lua,有很多成熟框架能够用。
如果你用 C# 更纯熟,更喜爱 C#,用 lua 感觉很好受,能够尝试学习应用一下 ILRuntime,用纯熟了当前再思考用该计划作为热更新计划。
存在即是正当,lua 风行有其劣势,ILRuntime 呈现也有其情理,lua 是第一抉择,但不是惟一的抉择,ILRuntime 写代码更难受,但后方兴许有不少坑。

二、下载 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…

正文完
 0