之前简单的写了个ILRuntime和Unity互相调用的文章:https://blog.csdn.net/wangjiangrong/article/details/90294366,感觉有蛮多不好的地方,所以想重新搞一搞,弄个简单的ILRuntime和Unity的基本框架。
一些基本的概念在上面的文章讲过了,这边就懒得再说了,前期工作依旧是导入ILRuntime的库到Unity,和一个Hotfix工程。
有兴趣的同学可以看下这个工程,里面有比较完整的代码,也是后续讲到UI相关的文章里面的Demo工程。
补充:官方文档说导入 Mono.Cecil.20,Mono.Cecil.Pdb,ILRuntime三个文件夹到Unity工程,但是在新下载的目录中,根目录下只有ILRuntime和Mono.Cecil两个目录,经测试,我们导入这两个目录,然后删除ILRuntime/ILRuntime.csproj和Mono.Cecil子目录中出了文件夹外的其他文件,同时删除子文件夹内所有会导致Unity编译报错的AssemblyInfo.cs文件
我们先把ILRuntime部分的内容,例如绑定适配器,注册委托等分成多个工具类,方便管理和查看,同时暴露出ILRuntime.Runtime.Enviorment.AppDomain实例,供外部使用。
注册委托的类ILRuntimeDelegateHelp
using System;
namespace Tool
{
public class ILRuntimeDelegateHelp
{
//跨域委托调用,注册委托的适配器
public static void RegisterDelegate(ILRuntime.Runtime.Enviorment.AppDomain appdomain)
{
//Button 点击事件的委托注册
appdomain.DelegateManager.RegisterDelegateConvertor((act) =>
{
return new UnityEngine.Events.UnityAction(() =>
{
((Action)act)();
});
});
}
}
}
绑定适配器的类ILRuntimeAdapterHelp
namespace Tool
{
public class ILRuntimeAdapterHelp
{
//跨域继承绑定适配器
public static void RegisterCrossBindingAdaptor(ILRuntime.Runtime.Enviorment.AppDomain appdomain)
{
//appdomain.RegisterCrossBindingAdaptor(new InterfaceIUIAdaptor());
}
}
}
读取hotfix.dll的类ILRuntimeHelp
using System;
using System.Collections;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
namespace Tool
{
public class ILRuntimeHelp
{
public static ILRuntime.Runtime.Enviorment.AppDomain appdomain;
static MemoryStream m_hotfixDllMemoryStream;
static MemoryStream m_hotfixPdbMemoryStream;
public static IEnumerator LoadILRuntime(Action LoadedFinish)
{
appdomain = new ILRuntime.Runtime.Enviorment.AppDomain();
//把官方文档的WWW用UnityWebRequest替代了
UnityWebRequest webRequest = UnityWebRequest.Get("file:///" + Application.streamingAssetsPath + "/Hotfix.dll");
yield return webRequest.SendWebRequest();
byte[] dll = null;
if (webRequest.isNetworkError)
{
Debug.Log("Download Error:" + webRequest.error);
}
else
{
dll = webRequest.downloadHandler.data;
}
webRequest.Dispose();
webRequest = UnityWebRequest.Get("file:///" + Application.streamingAssetsPath + "/Hotfix.pdb");
yield return webRequest.SendWebRequest();
byte[] pdb = null;
if (webRequest.isNetworkError)
{
Debug.Log("Download Error:" + webRequest.error);
}
else
{
pdb = webRequest.downloadHandler.data;
}
//用下面的会报错:ObjectDisposedException: Cannot access a closed Stream.
/**
using (MemoryStream fs = new MemoryStream(dll))
{
using (MemoryStream p = new MemoryStream(pdb))
{
appdomain.LoadAssembly(fs, p, new Mono.Cecil.Pdb.PdbReaderProvider());
}
}
**/
m_hotfixDllMemoryStream = new MemoryStream(dll);
m_hotfixPdbMemoryStream = new MemoryStream(pdb);
appdomain.LoadAssembly(m_hotfixDllMemoryStream , m_hotfixPdbMemoryStream , new Mono.Cecil.Pdb.PdbReaderProvider());
webRequest.Dispose();
webRequest = null;
ILRuntimeDelegateHelp.RegisterDelegate(appdomain);
ILRuntimeAdapterHelp.RegisterCrossBindingAdaptor(appdomain);
//用于ILRuntime Debug
if (Application.isEditor)
appdomain.DebugService.StartDebugService(56000);
LoadedFinish?.Invoke();
}
public static void Dispose()
{
m_hotfixDllMemoryStream?.Dispose();
m_hotfixPdbMemoryStream?.Dispose();
}
}
}
ILRuntime的类搞好之后,我们需要一个启动类Launch,用来加载调用热更的内容,同时管理热更内容的生命周期。
using System;
using Tool;
using UnityEngine;
public class Launch : MonoBehaviour
{
public static Action OnUpdate { get; set; }
public static Action OnLateUpdate { get; set; }
public static Action OnFixedUpdate { get; set; }
public static Action OnApplicationQuitAction { get; set; }
private void Start()
{
StartCoroutine(ILRuntimeHelp.LoadILRuntime(OnILRuntimeInitialized));
}
void OnILRuntimeInitialized()
{
ILRuntimeHelp.appdomain.Invoke("Hotfix.HotfixLaunch", "Start", null, null);
}
void Update()
{
OnUpdate?.Invoke();
}
void LateUpdate()
{
OnLateUpdate?.Invoke();
}
void FixedUpdate()
{
OnFixedUpdate?.Invoke();
}
void OnApplicationQuit()
{
OnApplicationQuitAction?.Invoke();
ILRuntimeHelp.Dispose();
GC.Collect();
}
}
需要热更的部分我们都需要放在Hotfix工程中,打成dll导入到Unity。我们需要一个主类提供一个接口给Unity调用,类似Main函数,然后在该类中去维护Hotfix部分的生命周期。例如
namespace Hotfix.Manager
{
public interface IManager
{
void Init();
void Start();
void Update();
void LateUpdate();
void FixedUpdate();
void OnApplicationQuit();
}
}
一般一个项目会有多个功能模块,例如UI,对象池,网络等等,这些我们可以都用一个个的管理类来处理,我们先写一个基类
namespace Hotfix.Manager
{
public class ManagerBase : IManager where T : IManager, new()
{
protected static T mInstance;
public static T Instance
{
get
{
if (mInstance == null)
{
mInstance = new T();
}
return mInstance;
}
}
protected ManagerBase()
{
}
public virtual void Init()
{
}
public virtual void Start()
{
}
public virtual void Update()
{
}
public virtual void LateUpdate()
{
}
public virtual void FixedUpdate()
{
}
public virtual void OnApplicationQuit()
{
}
}
}
然后需要的模块继承这个基类,例如
using UnityEngine;
namespace Hotfix.Manager
{
class TimeManager : ManagerBase
{
public override void Start()
{
base.Start();
Debug.Log("TimeManager start");
}
}
}
using UnityEngine;
namespace Hotfix.Manager
{
class UIManager : ManagerBase
{
public override void Start()
{
base.Start();
Debug.Log("UIManager start");
}
}
}
接着我们就需要一个主类,去由unity调用,然后启动我们的这些管理类。
using Hotfix.Manager;
using System;
using System.Collections.Generic;
using System.Linq;
using Tool;
using UnityEngine;
namespace Hotfix
{
class HotfixLaunch
{
static List managerList = new List();
public static void Start()
{
Debug.Log("HotfixLaunch Start");
//获取Hotfix.dll内部定义的类
List allTypes = new List();
var values = ILRuntimeHelp.appdomain.LoadedTypes.Values.ToList();
foreach (var v in values)
{
allTypes.Add(v.ReflectionType);
}
//去重
allTypes = allTypes.Distinct().ToList();
//获取hotfix的管理类,并启动
foreach (var t in allTypes)
{
try
{
if (t != null && t.BaseType != null && t.BaseType.FullName != null)
{
if (t.BaseType.FullName.Contains(".ManagerBase`"))
{
Debug.Log("加载管理器-" + t);
var manager = t.BaseType.GetProperty("Instance").GetValue(null, null) as IManager;
manager.Init();
managerList.Add(manager);
continue;
}
}
}
catch (Exception e)
{
Debug.LogError(e.Message);
}
}
//绑定生命周期方法
Launch.OnUpdate = Update;
Launch.OnLateUpdate = LateUpdate;
Launch.OnFixedUpdate = FixedUpdate;
Launch.OnApplicationQuitAction = ApplicationQuit;
foreach (var manager in managerList)
manager.Start();
}
static void Update()
{
foreach(var manager in managerList)
manager.Update();
}
static void LateUpdate()
{
foreach (var manager in managerList)
manager.LateUpdate();
}
static void FixedUpdate()
{
foreach (var manager in managerList)
manager.FixedUpdate();
}
static void ApplicationQuit()
{
Debug.Log("hotfix ApplicationQuit");
}
}
}
最后导出dll和pdb文件到streamassets目录,将组件Launch挂载到场景中运行即可。