一、主工程调用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;
///
/// MonoBehaviour 基本事件触发器 触发以下事件
/// OnEnable、Start、OnDisable、OnDestroy
///
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();
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();
manager.RegisterMethodDelegate();
manager.RegisterMethodDelegate();
manager.RegisterMethodDelegate();
manager.RegisterMethodDelegate();
manager.RegisterMethodDelegate();
manager.RegisterMethodDelegate();
manager.RegisterMethodDelegate();
manager.RegisterMethodDelegate();
manager.RegisterMethodDelegate();
manager.RegisterMethodDelegate();
manager.RegisterMethodDelegate();
manager.RegisterMethodDelegate();
manager.RegisterMethodDelegate
四、跨域调用性能优化
跨域调用的性能是很差的。性能优化的方式如下:
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的接口可以用代理的方式。
调用时要注意性能问题,可以进行优化。