• Unity ILRuntime 调用方法一览


    Unity C#热更新方案 ILRuntime学习笔记

     

    一、主工程调用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

    CLRBinding.png

    当然,使用前要配置你热更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的接口可以用代理的方式。
    调用时要注意性能问题,可以进行优化。

    转发自:  https://segmentfault.com/a/1190000023290547    感谢冰封百度大佬

  • 相关阅读:
    Swing 添加Esc快捷键退出程序
    DefaultTableCellRenderer 自定义
    项目清理和删除svn信息(转)
    时间转换工具类
    Java Swing 日期控件(转载)
    Eureka原理
    SpringCloud之Eureka注册中心集群篇
    spring boot及spring cloud介绍
    spring cloud 服务注册/发现/提供/调用 demo
    eclipse构建maven+scala+spark工程
  • 原文地址:https://www.cnblogs.com/NGZ3D/p/13738389.html
Copyright © 2020-2023  润新知