安装

从零开始 — ILRuntime (ourpalm.github.io)

从Packages/manifest.json里添加信息后,ILRuntime在PackageManager的新的目录里面

My Registries

准备热更工程

首先进入Unity主工程目录(Assets的父目录),在里面新建HotFixProject文件夹

打开Visual Studio,新建一个项目,选择“类库(.Net Framework)”,按照下图设置

配置设置

框架只要选择.Net Framework 4.6.1以上即可,虽然截图中使用的是4.6.2,实际项目中使用的是4.7.2

进入工程后,将默认生成的Class1类重命名为TestClass

添加引用

在“解决方案资源管理器”中右键“引用——添加引用”,点击浏览

这里按照我的Unity编辑器安装目录,定位到几个需要引用的dll,用作参考。

需要引用的dll库 位置
UnityEngine.dll(在Unity2019之后没用了) “D:\Program FIles\Unity Editor\2021.3.22f1c1\Editor\Data\Managed\UnityEngine.dll”
UnityEngine.UI.dll E:\UnityProjects\ResLoadPrg_Android\Library\ScriptAssemblies\UnityEngine.UI.dll
UnityEngine.CoreModule.dll D:\Program FIles\Unity Editor\2021.3.22f1c1\Editor\Data\Managed\UnityEngine\UnityEngine.CoreModule.dll
UnityEngine.UIModule.dll D:\Program FIles\Unity Editor\2021.3.22f1c1\Editor\Data\Managed\UnityEngine\UnityEngine.UIModule.dll
Assembly-CSharp.dll E:\UnityProjects\ResLoadPrg_Android\Library\ScriptAssemblies\Assembly-CSharp.dll
Base.dll E:\UnityProjects\ResLoadPrg_Android\Library\ScriptAssemblies\Base.dll
Resource.dll E:\UnityProjects\ResLoadPrg_Android\Library\ScriptAssemblies\Resource.dll
UIManager.dll E:\UnityProjects\ResLoadPrg_Android\Library\ScriptAssemblies\UIManager.dll

前面几个是Unity工程必备的dll库,后面是单独的项目中生成的dll库和Assembly Definition自己定义的dll库

在Unity2019之后UnityEngine.dll是是一个空库,它里面的功能已经拆分到了各个其他的dll中,我们这里首要引用UnityEngine.CoreModule.dll,UnityEngine这个dll库就不用了

想要定位这些dll库的位置,可以打开Unity的VS工程,在解决方案资源管理器中找到这些引用,它们的属性标记了dll库的位置。注意,自己定义的Assembly Definition的dll库Unity并不能正确标记,它指向一个缓存文件

这几个引用添加完后,记得在Visual Studio设置它们的属性,将“复制本地”设置为“False”

添加引用完成后,我们先在里面随便添加一些代码

1
2
3
4
5
6
7
8
9
10
11
12
13
using System;
using UnityEngine;

namespace HotFix
{
public class TestClass
{
public static void StaticFuncTest()
{
Debug.Log("Test");
}
}
}

设置Unity热更文件

在Unity工程文件夹的Assets——GameData——Data里面新建HotFix文件夹

回到HotFix工程,在Visual Studio种修改工程的属性,再点击“高级”,将“输出——调试信息”改为“可移植”。

点击“生成——生成HotFix”,快捷键Ctrl+B

这样,我们在HotFixProject——HotFix——bin——Debug文件夹内就生成了HotFix.dllHotFix.pdb两个文件,这两个文件就是热更的主要文件,前者包含了主要逻辑,后者用来Debug。

我们要把这两个文件修改后缀后放在GameData文件夹下,这意味着这两个文件需要打成AssetBundle包,而dll后缀的文件会被Unity剔除打包,所以我们需要修改一下后缀。将HotFix.dll改为HotFix.dll.txt,将HotFix.pdb改为HotFix.pdb.txt

我们使用什么样的后缀都没关系,只要保证assetbundle会把它打包进去就好,因为dll库本质上就是一个二进制文件,ILRuntime在解释执行时也是通过读取二进制流来执行的。

点击ResFrame——Resource——ABConfig文件

在其中输入新加的HotFix文件夹路径

1
2
3
hotfix
AB Name : hotfix
Path : Assets/GameData/Data/HotFix

如果此时Assets/GameData/Data/HotFix内有热更代码文件的话,我们打一次包,主工程目录的AssetBundle文件夹内应该能看到名为hotfix的代码ab包

主工程调用热更逻辑

在Scripts——Manager文件夹内新建ILRuntimeManager文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
using UnityEngine;
using ILRuntime.Runtime.Enviorment;
using System.IO;

public class ILRuntimeManager : SingletonPattern<ILRuntimeManager>
{
private const string DLLPATH = "Assets/GameData/Data/HotFix/Hotfix.dll.txt";
#if UNITY_EDITOR
private const string PDBPATH = "Assets/GameData/Data/HotFix/Hotfix.pdb.txt";
#endif
AppDomain m_AppDomain;

public void Init()
{
LoadHotFixAssembly();
}

void LoadHotFixAssembly()
{
//整个工程只有一个ILRuntime的AppDomain,注意AppDomain这个类是ILRuntime的类
m_AppDomain = new AppDomain();
//读取热更资源的dll
TextAsset dllText = ResourceManager.Instance.LoadResource<TextAsset>(DLLPATH);
#if UNITY_EDITOR
//pdb文件,用于调试数据
TextAsset pdbText = ResourceManager.Instance.LoadResource<TextAsset>(PDBPATH);
MemoryStream pdbms = new MemoryStream(pdbText.bytes);
#endif
MemoryStream dllms = new MemoryStream(dllText.bytes);


#if UNITY_EDITOR
m_AppDomain.LoadAssembly(dllms,pdbms,new ILRuntime.Mono.Cecil.Pdb.PdbReaderProvider());
#else
m_AppDomain.LoadAssembly(dllms,null,new ILRuntime.Mono.Cecil.Pdb.PdbReaderProvider());
#endif


InitializeILRuntime();
OnHotFixLoaded();
}
void InitializeILRuntime()
{

}
void OnHotFixLoaded()
{
//第一个简单方法的调用,在ILRuntime底层包装好的直接反射的方式
m_AppDomain.Invoke("HotFix.TestClass", "StaticFuncTest", null, null);
}
}

原视频中使用using语句来加载dllms和pdbms,但是在新版ILRuntime中,不能使用using语句了,要求加载的流不能关闭。

ILRuntime新版的用法在后面新的文章中重写。

在GameStart启动

修改GameStartStartGame方法,添加启动ILRuntimeManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public IEnumerator StartGame(Image image,Text text)
{
image.fillAmount = 0f;

yield return null;
text.text = "加载AB配置......";//+++
AssetBundleManager.Instance.LoadAssetBundleConfig();

image.fillAmount = 0.1f;
yield return null;
text.text = "加载dll... ...";//+++
ILRuntimeManager.Instance.Init();

image.fillAmount = 0.2f;
yield return null;
text.text = "加载配置数据... ...";
LoadConfig();
//...
}

这时启动Unity,就会发现ILRuntime调用了我们热更工程的代码。

dll转换工具

每一次热更工程生成dll时,我们都需要在Unity内生成相应的热更文件,这里我们使用一个工具来转换

在Assets——Editor文件夹内新建TestEditor脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
using UnityEditor;
using System.IO;

public class TestEditor
{
private static string DLLPATH = "E:\\UnityProjects\\ResLoadPrg_Android\\HotFixProject\\HotFix\\bin\\Debug\\Hotfix.dll";
private static string PDBPATH = "E:\\UnityProjects\\ResLoadPrg_Android\\HotFixProject\\HotFix\\bin\\Debug\\Hotfix.pdb";

private static string HotDllPath = "Assets/GameData/Data/HotFix/Hotfix.dll.txt";
private static string HotPDBPath = "Assets/GameData/Data/HotFix/Hotfix.pdb.txt";
[MenuItem("Tools/更新热更代码")]
public static void ChangeDllExten()
{
if (File.Exists(DLLPATH))
{
using(FileStream fs = new FileStream(HotDllPath,FileMode.OpenOrCreate,FileAccess.Write))
{
fs.Seek(0, SeekOrigin.Begin);
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(File.ReadAllBytes(DLLPATH));
bw.Close();
}
}
if(File.Exists(PDBPATH))
{
using (FileStream fs = new FileStream(HotPDBPath, FileMode.OpenOrCreate, FileAccess.Write))
{
fs.Seek(0, SeekOrigin.Begin);
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(File.ReadAllBytes(PDBPATH));
bw.Close();
}
}
}
}