一、主工程调用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_EDITORusing 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的接口能够用代理的形式。
调用时要留神性能问题,能够进行优化。