转载请表明原文地址: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...