一院水、ILRuntime的基本介紹
ILRuntime是一個跨平臺CLR實現(xiàn),它可以在多個平臺上運行C#代碼癌幕,包括Android、iOS昧穿、Windows勺远、Linux等等。ILRuntime的實現(xiàn)方式是將C#代碼編譯成IL代碼时鸵,然后在運行時通過JIT或AOT的方式將IL代碼轉(zhuǎn)換為機(jī)器代碼胶逢,從而實現(xiàn)跨平臺的效果。ILRuntime的主要功能包括熱更新饰潜、動態(tài)加載初坠、代碼加密等等。
二彭雾、Unity與ILRuntime的集成
Unity與ILRuntime的集成主要包括以下幾個步驟:
1碟刺、添加ILRuntime的插件
首先需要將ILRuntime的插件添加到Unity項目中,這可以通過Unity Asset Store中搜索“ILRuntime”來獲取薯酝。
2半沽、修改Unity項目的腳本編譯方式
默認(rèn)情況下爽柒,Unity項目中的腳本會被編譯為Mono或IL2CPP,但是為了使用ILRuntime者填,需要將腳本編譯為.NET?Standard 2.0浩村。這可以通過修改Unity項目的Player Settings來實現(xiàn),在Player Settings中選擇“Other Settings”并將“Scripting Runtime Version”設(shè)置為.NET?4.x Equivalent占哟,同時將“Api Compatibility Level”設(shè)置為.NET?Standard 2.0心墅。
3、創(chuàng)建ILRuntime的AppDomain
ILRuntime的AppDomain是一個獨立的運行環(huán)境榨乎,它可以加載和運行IL代碼怎燥。在Unity項目中摹迷,需要在啟動時創(chuàng)建一個ILRuntime的AppDomain缸榛,并將IL代碼加載到AppDomain中络拌。這可以通過在Unity項目中創(chuàng)建一個啟動腳本來實現(xiàn)懈费。在啟動腳本中秸架,需要使用ILRuntime的API加載和運行IL代碼阔籽,例如:
using ILRuntime.Runtime.Enviorment;
public class Startup : MonoBehaviour
{
? ? private AppDomain appDomain;
? ? private void Start()
? ? {
? ? ? ? appDomain = new AppDomain();
? ? ? ? appDomain.LoadAssembly("Assembly-CSharp");
? ? ? ? appDomain.Invoke("Assembly-CSharp.Main", "Start", null);
? ? }
? ? private void Update()
? ? {
? ? ? ? appDomain.Invoke("Assembly-CSharp.Main", "Update", null);
? ? }
}
在上面的代碼中甫贯,創(chuàng)建了一個ILRuntime的AppDomain涛漂,并將“Assembly-CSharp”程序集加載到AppDomain中篇梭。然后在Start和Update方法中氢橙,通過AppDomain的Invoke方法調(diào)用“Assembly-CSharp.Main”類的“Start”和“Update”方法。
三恬偷、Unity與ILRuntime的交互
Unity與ILRuntime之間的交互主要通過以下幾種方式實現(xiàn):
1悍手、使用ILRuntime的API調(diào)用Unity的API
ILRuntime提供了一些API來調(diào)用Unity的API,例如:
using UnityEngine;
using ILRuntime.Runtime.Enviorment;
public class Main : MonoBehaviour
{
? ? private AppDomain appDomain;
? ? private void Start()
? ? {
? ? ? ? appDomain = new AppDomain();
? ? ? ? appDomain.LoadAssembly("Assembly-CSharp");
? ? ? ? appDomain.Invoke("Assembly-CSharp.Main", "Start", null);
? ? ? ? var go = appDomain.Instantiate("Assembly-CSharp.TestGameObject");
? ? ? ? var transform = go.GetComponent<Transform>();
? ? ? ? transform.position = new Vector3(1, 2, 3);
? ? }
? ? private void Update()
? ? {
? ? ? ? appDomain.Invoke("Assembly-CSharp.Main", "Update", null);
? ? }
}
在上面的代碼中袍患,使用ILRuntime的API創(chuàng)建了一個GameObject坦康,并獲取了它的Transform組件,然后設(shè)置了Transform的位置诡延。
2滞欠、使用Unity的API調(diào)用ILRuntime的API
Unity可以通過反射機(jī)制調(diào)用ILRuntime中的API,例如:
using UnityEngine;
using System.Reflection;
using ILRuntime.Runtime.Enviorment;
public class Main : MonoBehaviour
{
? ? private AppDomain appDomain;
? ? private void Start()
? ? {
? ? ? ? appDomain = new AppDomain();
? ? ? ? appDomain.LoadAssembly("Assembly-CSharp");
? ? ? ? appDomain.Invoke("Assembly-CSharp.Main", "Start", null);
? ? ? ? var type = appDomain.LoadedTypes["Assembly-CSharp.TestClass"];
? ? ? ? var method = type.GetMethod("TestMethod");
? ? ? ? method.Invoke(null, null);
? ? }
? ? private void Update()
? ? {
? ? ? ? appDomain.Invoke("Assembly-CSharp.Main", "Update", null);
? ? }
}
在上面的代碼中肆良,使用Unity的反射機(jī)制獲取了ILRuntime中的“Assembly-CSharp.TestClass”類型和“TestMethod”方法筛璧,并調(diào)用了它。
3惹恃、使用委托實現(xiàn)Unity和ILRuntime的互相調(diào)用
ILRuntime提供了一個DelegateBridge類夭谤,它可以將Unity和ILRuntime中的委托類型互相轉(zhuǎn)換。例如:
using UnityEngine;
using System;
using ILRuntime.Runtime.Enviorment;
public class Main : MonoBehaviour
{
? ? private AppDomain appDomain;
? ? private void Start()
? ? {
? ? ? ? appDomain = new AppDomain();
? ? ? ? appDomain.LoadAssembly("Assembly-CSharp");
? ? ? ? appDomain.Invoke("Assembly-CSharp.Main", "Start", null);
? ? ? ? var type = appDomain.LoadedTypes["Assembly-CSharp.TestClass"];
? ? ? ? var method = type.GetMethod("TestMethod");
? ? ? ? var action = DelegateBridge.CreateDelegate(typeof(Action), method) as Action;
? ? ? ? action();
? ? }
? ? private void Update()
? ? {
? ? ? ? appDomain.Invoke("Assembly-CSharp.Main", "Update", null);
? ? }
}
在上面的代碼中巫糙,使用DelegateBridge類將ILRuntime中的“Assembly-CSharp.TestClass.TestMethod”方法轉(zhuǎn)換為Unity的Action委托類型朗儒,并調(diào)用了它。
四、ILRuntime的熱更新
ILRuntime的熱更新是指在運行時替換IL代碼采蚀,從而實現(xiàn)應(yīng)用程序的更新疲牵。ILRuntime的熱更新主要包括以下幾個步驟:
1、在ILRuntime的AppDomain中替換IL代碼
ILRuntime的AppDomain可以在運行時加載和卸載IL代碼榆鼠,因此可以在運行時替換IL代碼纲爸。例如:
using UnityEngine;
using ILRuntime.Runtime.Enviorment;
public class Main : MonoBehaviour
{
? ? private AppDomain appDomain;
? ? private void Start()
? ? {
? ? ? ? appDomain = new AppDomain();
? ? ? ? appDomain.LoadAssembly("Assembly-CSharp");
? ? ? ? appDomain.Invoke("Assembly-CSharp.Main", "Start", null);
? ? }
? ? private void Update()
? ? {
? ? ? ? appDomain.Invoke("Assembly-CSharp.Main", "Update", null);
? ? ? ? // 熱更新
? ? ? ? var newIL = File.ReadAllBytes("Assembly-CSharp.dll");
? ? ? ? appDomain.LoadAssembly(newIL);
? ? ? ? appDomain.Invoke("Assembly-CSharp.Main", "OnHotUpdate", null);
? ? }
}
在上面的代碼中,使用ILRuntime的API加載了“Assembly-CSharp”程序集妆够,并在Update方法中熱更新了IL代碼识啦。
2、使用Unity的AssetBundle實現(xiàn)熱更新
ILRuntime的熱更新也可以使用Unity的AssetBundle來實現(xiàn)神妹,這可以通過將IL代碼打包為AssetBundle颓哮,并在運行時下載和加載AssetBundle來實現(xiàn)。例如:
using UnityEngine;
using System.Collections;
using ILRuntime.Runtime.Enviorment;
public class Main : MonoBehaviour
{
? ? private AppDomain appDomain;
? ? private void Start()
? ? {
? ? ? ? StartCoroutine(DownloadAndLoadAssetBundle());
? ? }
? ? private void Update()
? ? {
? ? ? ? appDomain.Invoke("Assembly-CSharp.Main", "Update", null);
? ? }
? ? private IEnumerator DownloadAndLoadAssetBundle()
? ? {
? ? ? ? var request = UnityWebRequestAssetBundle.GetAssetBundle("http://localhost/Assembly-CSharp.assetbundle");
? ? ? ? yield return request.SendWebRequest();
? ? ? ? var assetBundle = DownloadHandlerAssetBundle.GetContent(request);
? ? ? ? var il = assetBundle.LoadAsset<TextAsset>("Assembly-CSharp.dll");
? ? ? ? appDomain = new AppDomain();
? ? ? ? appDomain.LoadAssembly(il.bytes);
? ? ? ? appDomain.Invoke("Assembly-CSharp.Main", "Start", null);
? ? }
}
在上面的代碼中鸵荠,使用Unity的WebRequest下載了“Assembly-CSharp.assetbundle”文件冕茅,并使用AssetBundle加載了“Assembly-CSharp.dll”文件,并將IL代碼加載到ILRuntime的AppDomain中蛹找。