一、主工程调用 Hotfix 代码
假如 Hotfix 工程里有一个 Test 类,该如何调用该类的办法呢?
namespace Hotfix {
public class Test {
// 实例办法
public string GetName() {return "test";}
// 静态方法
public static float Sum(float a, float b) {return a + b;}
}
}
1. 调用静态方法
// 获取类型
IType type = appdomain.LoadedTypes["Hotfix.Test"];
// 获取办法
IMethod method = type.GetMethod("Sum", 2);
// 调用办法
object returnValue = appdomain.Invoke(method, null, 1, 2);
// 输入返回值
print("静态方法返回值:" + returnValue);
2. 调用实例办法
// 获取类型
IType type = appdomain.LoadedTypes["Hotfix.Test"];
// 创立实例
object instance = (type as ILType).Instantiate();
// 获取办法
IMethod method = type.GetMethod("GetName", 0);
// 调用办法
object returnValue = appdomain.Invoke(method, instance);
// 输入返回值
print("静态方法返回值:" + returnValue);
二、Hotfix 调用主工程代码
Hotfix 调用主工程代码间接调用即可,无需特地步骤。
namespace Hotfix {
using UnityEngine;
public class Test {
// 调用主工程办法
private void CallUnity() {Debug.Log(Application.streamingAssetsPath);
}
}
}
三、Hotfix 响应 MonoBehaviour 事件
Hotfix 响应 MonoBehaviour 中的事件,能够用代理办法实现。
1. 在 Unity 中新建一个 Mono 脚本,实现本人须要的接口,在这些接口被调用时,调用代理事件
namespace GameUtils.Triggers {
using System;
using UnityEngine;
/// <summary>
/// <para>MonoBehaviour 根本事件触发器 触发以下事件 </para>
/// OnEnable、Start、OnDisable、OnDestroy
/// </summary>
public class MonoBehaviourEventTrigger : MonoBehaviour {
public Action onEnable;
public Action start;
public Action update
public Action onDisable;
public Action onDestroy;
private void OnEnable() {if (onEnable != null) onEnable.Invoke();}
private void Start() {if (start != null) start.Invoke();}
private void OnDisable() {if (onDisable != null) onDisable.Invoke();}
private void Update() {if (update != null) update.Invoke();}
private void OnDestroy() {if (onDestroy != null) onDestroy.Invoke();}
}
}
2. 在 Hotfix 工程中这样调用:
namespace Hotfix {
using UnityEngine;
using GameUtils.Triggers;
public class Test {private void MonoTest() {GameObject obj = new GameObject("Test");
MonoBehaviourEventTrigger monoTrigger = obj.AddComponent<MonoBehaviourEventTrigger>();
monoTrigger.start = Start;
monoTrigger.update = Update;
monoTrigger.onDestroy = OnDestroy;
}
private void Start() {Debug.Log("Start");
}
private void Update() {
// 鼠标按下时,做射线碰撞检测
if (Input.GetMouseButtonDown(0)) {Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 1000)) {Debug.Log(hit.point);
}
}
}
private void OnDestroy() {Debug.Log("OnDestroy");
}
}
}
注:因为 Mono 接口是主工程中的,不能热更,所以要提前写好所有接口以供热更代码调用。Mono 中的接口泛滥,如果全副用一个类实现的话太过冗余,我这里做了一下优化,把不同性能的接口分组到不同的脚本中,由一个对立的 Mono 依据调用状况动静增加,在 set 里实现调用时的动静增加脚本性能,应用时还是一样不便,代码太长就不贴了。
3. 带参数的 Action
留神,带参数的 Action 在跨域调用前要在主工程里注册参数。
否则调用会报错。
private void RegistDelegate() {
DelegateManager manager = appdomain.DelegateManager;
manager.RegisterMethodDelegate<bool>();
manager.RegisterMethodDelegate<byte>();
manager.RegisterMethodDelegate<sbyte>();
manager.RegisterMethodDelegate<char>();
manager.RegisterMethodDelegate<short>();
manager.RegisterMethodDelegate<ushort>();
manager.RegisterMethodDelegate<int>();
manager.RegisterMethodDelegate<uint>();
manager.RegisterMethodDelegate<long>();
manager.RegisterMethodDelegate<ulong>();
manager.RegisterMethodDelegate<float>();
manager.RegisterMethodDelegate<double>();
manager.RegisterMethodDelegate<string>();
manager.RegisterMethodDelegate<object>();
manager.RegisterMethodDelegate<Collider>();
manager.RegisterMethodDelegate<Collision>();
manager.RegisterMethodDelegate<BaseEventData>();
manager.RegisterMethodDelegate<PointerEventData>();
manager.RegisterMethodDelegate<Object>();
manager.RegisterMethodDelegate<GameObject>();
appdomain.DelegateManager.RegisterDelegateConvertor<UnityEngine.Events.UnityAction>((action) => {return new UnityEngine.Events.UnityAction(() => {((System.Action)action)();});
});
}
四、跨域调用性能优化
跨域调用的性能是很差的。性能优化的形式如下:
1. 主工程调用 Hotfix 时,多用 type.GetMethod() 去调用。
// 1. 用 string 调用办法:appdomain.Invoke("Hotfix.Test", "Sum", null, null);
// 2. 用 Method 调用办法性能更好
IType type = appdomain.LoadedTypes["Hotfix.Test"];
IMethod method = type.GetMethod("Sum", 2);
appdomain.Invoke(method, instance, 1, 2);
2.Hotfix 调用主工程代码时,能够用 CLRBinding 优化。
官网提供了一个工具,能够实现 Hotfix 调用的优化,该工具会主动剖析热更 dll 中调用的办法,主动生成 CLRBinding 类。
应用形式是: 在 unity 顶部菜单中选择 ILRuntime > Generate CLR Binding Code by Analysis
当然,应用前要配置你热更 dll 的门路和生成文件的门路。
在 Unity 查找 ILRuntimeCLRBinding 这个类,批改其中门路。
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
using System;
using System.Text;
using System.Collections.Generic;
using ILRuntimeDemo;
[System.Reflection.Obfuscation(Exclude = true)]
public class ILRuntimeCLRBinding
{[MenuItem("ILRuntime/Generate CLR Binding Code by Analysis")]
static void GenerateCLRBindingByAnalysis()
{
// 用新的剖析热更 dll 调用援用来生成绑定代码
ILRuntime.Runtime.Enviorment.AppDomain domain = new ILRuntime.Runtime.Enviorment.AppDomain();
using (System.IO.FileStream fs = new System.IO.FileStream("Assets/StreamingAssets/Hotfix.dll", System.IO.FileMode.Open, System.IO.FileAccess.Read))
{domain.LoadAssembly(fs);
//Crossbind Adapter is needed to generate the correct binding code
InitILRuntime(domain);
ILRuntime.Runtime.CLRBinding.BindingCodeGenerator.GenerateBindingCode(domain, "Assets/Game/ILRuntime/Generated");
}
AssetDatabase.Refresh();}
static void InitILRuntime(ILRuntime.Runtime.Enviorment.AppDomain domain)
{
// 这里须要注册所有热更 DLL 中用到的跨域继承 Adapter,否则无奈正确抓取援用
domain.RegisterCrossBindingAdaptor(new MonoBehaviourAdapter());
domain.RegisterCrossBindingAdaptor(new CoroutineAdapter());
domain.RegisterCrossBindingAdaptor(new TestClassBaseAdapter());
domain.RegisterValueTypeBinder(typeof(Vector3), new Vector3Binder());
}
}
#endif
总结:
主工程调用 Hotfix 代码时比拟麻烦,要用相似反射的模式。
Hotfix 调用主工程代码很容易,失常怎么写就怎么写。
Hotfix 调用 MonoBehaviour 的接口能够用代理的形式。
调用时要留神性能问题,能够进行优化。