之前簡單的寫了個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文件
Unity部分
我們先把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<UnityEngine.Events.UnityAction>((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部分
需要熱更的部分我們都需要放在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<T> : 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<TimeManager>
{
public override void Start()
{
base.Start();
Debug.Log("TimeManager start");
}
}
}
using UnityEngine;
namespace Hotfix.Manager
{
class UIManager : ManagerBase<UIManager>
{
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<IManager> managerList = new List<IManager>();
public static void Start()
{
Debug.Log("HotfixLaunch Start");
//獲取Hotfix.dll內部定義的類
List<Type> allTypes = new List<Type>();
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掛載到場景中運行即可。