使用流程
我们可能需要热更工程去继承Unity工程中的类,那么我们就需要为其写一个适配器,具体流程如下:
- 新建Unity工程父类
public abstract class TestClassBase
{
public virtual int Value
{
get
{
return 0;
}
set
{
}
}
public virtual void TestVirtual(string str)
{
Debug.Log("!! TestClassBase.TestVirtual, str = " + str);
}
public abstract void TestAbstract(int gg);
}
2.编写Unity工程如口主类,并实例初始化热更新ILRuntime的AppDomain,同时加载热更新dll和pdb。
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~
#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");
}
#if DEBUG && (UNITY_EDITOR || UNITY_ANDROID || UNITY_IPHONE)
//由于Unity的Profiler接口只允许在主线程使用,为了避免出异常,需要告诉ILRuntime主线程的线程ID才能正确将函数运行耗时报告给Profiler
appdomain.UnityMainThreadID = System.Threading.Thread.CurrentThread.ManagedThreadId;
#endif
OnHotFixLoaded();
}
- 入口类中调用dll继承TestClassBase的类和方法
void OnHotFixLoaded()
{
Debug.Log("首先我们来创建热更里的类实例");
TestClassBase obj;
Debug.Log("现在我们来注册适配器, 该适配器由ILRuntime/Generate Cross Binding Adapter菜单命令自动生成");
//因为跨域继承必须要注册适配器。 如果是热更DLL里面继承热更里面的类型,不需要任何注册。
appdomain.RegisterCrossBindingAdaptor(new TestClassBaseAdapter());
Debug.Log("现在再来尝试创建一个实例");
obj = appdomain.Instantiate<TestClassBase>("HotFix_Project.TestInheritance");
Debug.Log("现在来调用成员方法");
obj.TestAbstract(123);
obj.TestVirtual("Hello");
obj.Value = 233;
Debug.LogFormat("obj.Value={0}", obj.Value);
Debug.Log("现在换个方式创建实例");
obj = appdomain.Invoke("HotFix_Project.TestInheritance", "NewObject", null, null) as TestClassBase;
obj.TestAbstract(456);
obj.TestVirtual("Foobar");
obj.Value = 2333333;
Debug.LogFormat("obj.Value={0}", obj.Value);
}
- 生成适配器
由于跨域继承特殊性太多,自动生成无法实现完全无副作用生成,点击ILRuntime/Generate Cross Binding Adapter菜单命令自动生成的也只是个初始模版,简化大家工作,具体代码需求还是得自己调整
using System;
using ILRuntime.CLR.Method;
using ILRuntime.Runtime.Enviorment;
using ILRuntime.Runtime.Intepreter;
namespace ILRuntimeDemo
{
public class TestClassBaseAdapter : CrossBindingAdaptor
{
//定义访问方法的方法信息
static CrossBindingFunctionInfo<System.Int32> mget_Value_0 = new CrossBindingFunctionInfo<System.Int32>("get_Value");
static CrossBindingMethodInfo<System.Int32> mset_Value_1 = new CrossBindingMethodInfo<System.Int32>("set_Value");
static CrossBindingMethodInfo<System.String> mTestVirtual_2 = new CrossBindingMethodInfo<System.String>("TestVirtual");
static CrossBindingMethodInfo<System.Int32> mTestAbstract_3 = new CrossBindingMethodInfo<System.Int32>("TestAbstract");
public override Type BaseCLRType
{
get
{
return typeof(global::TestClassBase);//这里是你想继承的类型
}
}
public override Type AdaptorType
{
get
{
return typeof(Adapter);
}
}
public override object CreateCLRInstance(ILRuntime.Runtime.Enviorment.AppDomain appdomain, ILTypeInstance instance)
{
return new Adapter(appdomain, instance);
}
public class Adapter : global::TestClassBase, CrossBindingAdaptorType
{
ILTypeInstance instance;
ILRuntime.Runtime.Enviorment.AppDomain appdomain;
//必须要提供一个无参数的构造函数
public Adapter()
{
}
public Adapter(ILRuntime.Runtime.Enviorment.AppDomain appdomain, ILTypeInstance instance)
{
this.appdomain = appdomain;
this.instance = instance;
}
public ILTypeInstance ILInstance { get { return instance; } }
//下面将所有虚函数都重载一遍,并中转到热更内
public override void TestVirtual(System.String str)
{
if (mTestVirtual_2.CheckShouldInvokeBase(this.instance))
base.TestVirtual(str);
else
mTestVirtual_2.Invoke(this.instance, str);
}
public override void TestAbstract(System.Int32 gg)
{
mTestAbstract_3.Invoke(this.instance, gg);
}
public override System.Int32 Value
{
get
{
if (mget_Value_0.CheckShouldInvokeBase(this.instance))
return base.Value;
else
return mget_Value_0.Invoke(this.instance);
}
set
{
if (mset_Value_1.CheckShouldInvokeBase(this.instance))
base.Value = value;
else
mset_Value_1.Invoke(this.instance, value);
}
}
public override string ToString()
{
IMethod m = appdomain.ObjectType.GetMethod("ToString", 0);
m = instance.Type.GetVirtualMethod(m);
if (m == null || m is ILMethod)
{
return instance.ToString();
}
else
return instance.Type.FullName;
}
}
}
}
- 热更工程中的继承实现类
using System;
using System.Collections.Generic;
namespace HotFix_Project
{
//一定要特别注意,:后面只允许有1个Unity主工程的类或者接口,但是可以有随便多少个热更DLL中的接口
public class TestInheritance : TestClassBase
{
public override int Value { get; set; }
public override void TestAbstract(int gg)
{
UnityEngine.Debug.Log("!! TestInheritance.TestAbstract gg =" + gg);
}
public override void TestVirtual(string str)
{
base.TestVirtual(str);
UnityEngine.Debug.Log("!! TestInheritance.TestVirtual str =" + str);
}
public static TestInheritance NewObject()
{
return new HotFix_Project.TestInheritance();
}
}
}