16.ILR工程使用MonoBehaviour

16.更多跨域调用-热更工程中使用MonoBehaviour


16.1 知识点

复制之前的主入口调用

ILRuntimeManager.Instance.StartILRuntime(() =>
{
    // 执行ILRuntime当中的一个静态函数
    // 静态函数中就是热更工程自己处理的逻辑了
    ILRuntimeManager.Instance.appDomain.Invoke("HotFix_Project.ILRuntimeMain", "Start", null, null);
});

ILRuntime不推荐直接使用MonoBehaviour

ILRuntime中支持在热更工程中跨域继承MonoBehaviour

  1. 注册跨域继承适配器。
  2. 对泛型方法AddComponent进行重定向(较为复杂)。

但是,ILRuntime并不推荐通过跨域继承直接使用MonoBehaviour。推荐大家类似Lua中一样使用MonoBehaviour,在主工程中通过委托或事件的形式派发生命周期函数到热更中。

主要原因:
MonoBehaviour是一个很特殊的类,很多底层逻辑是在C++中处理的,比如其中public字段的序列化,在Inspector窗口中显示的功能。如果在热更工程中去写,底层C++逻辑无法获取到热更工程中C#相关的信息。

派发生命周期函数的形式使用MonoBehaviour

思路

在主工程中实现一个脚本,该脚本的主要目的就是派发MonoBehaviour的生命周期函数供热更工程中使用。

Unity工程中声明ILRuntimeMono类,定义各个生命周期的事件

public class ILRuntimeMono : MonoBehaviour
{
    // Awake相当于构造函数,在脚本添加的时候就会自动执行
    // 所以它不能使用这种事件分发的形式去触发它
    // public event Action awakeEvent;
    public event Action startEvent;
    public event Action updateEvent;

    // private void Awake()
    // {
    //     awakeEvent?.Invoke();
    // }
    
    void Start()
    {
        startEvent?.Invoke();
    }
    
    void Update()
    {
        updateEvent?.Invoke();
    }
}

在ILRuntime工程直接添加ILRuntimeMono组件并添加事件函数。注意Awake函数添加委托没意义,因为不会执行。

GameObject obj = new GameObject();
ILRuntimeMono mono = obj.AddComponent<ILRuntimeMono>();

// 由于Awake执行时机的特殊性,Awake会在脚本添加时自动执行,我们可以在添加了对应脚本后直接执行初始化相关逻辑即可
// 无需通过事件或者委托的形式去触发它
Debug.Log("Awake");
// mono.awakeEvent += () =>
// {
//     Debug.Log("Awake");
// };
mono.startEvent += () =>
{
    Debug.Log("Start");
};

mono.updateEvent += () =>
{
    Debug.Log("Update");
};

总结

ILRuntime中虽然支持跨域继承MonoBehaviour,但是作者并不建议大家这样做。我们可以通过类似Lua热更中那样,在主工程中实现用于派发生命周期函数的公共脚本,通过委托或事件达到生命周期函数的调用。


16.2 知识点代码

Lesson16_更多跨域调用_热更工程中使用MonoBehaviour

using System.Collections;
using System.Collections.Generic;
using BaseFramework;
using UnityEngine;

public class Lesson16_更多跨域调用_热更工程中使用MonoBehaviour : MonoBehaviour
{
    void Start()
    {
        #region 复制之前的主入口调用

        ILRuntimeManager.Instance.StartILRuntime(() =>
        {
            //执行ILRuntime当中的一个静态函数
            //静态函数中 就是热更工程自己处理的逻辑了
            ILRuntimeManager.Instance.appDomain.Invoke("HotFix_Project.ILRuntimeMain", "Start", null, null);
        });

        #endregion

        #region 知识点一 ILRuntime不推荐直接使用MonoBehaviour

        //ILRuntime中支持在热更工程中跨域继承MonoBehaviour
        //1.注册跨域继承适配器
        //2.对泛型方法AddComponent进行重定向(较为复杂)

        //但是
        //ILRuntime并不推荐通过跨域继承直接使用MonoBehaviour
        //推荐大家类似Lua中一样使用MonoBehaviour
        //在主工程中通过委托或事件的形式派发生命周期函数 到 热更中

        //主要原因:
        //MonoBehaviour是一个很特殊的类,很多底层逻辑是在C++中处理的
        //比如其中public字段的序列化,在Inspector窗口中显示的功能
        //如果在热更工程中去写,底层C++逻辑无法获取到热更工程中C#相关的信息

        #endregion

        #region 知识点二 派发生命周期函数的形式使用MonoBehaviour

        //在主工程中实现一个脚本
        //该脚本的主要目的就是派发MonoBehaviour的声明周期函数供热更工程中使用

        #endregion

        #region 总结

        //ILRuntime中虽然支持跨域继承MonoBehaviour
        //但是作者并不建议大家这样做
        //我们可以通过类似Lua热更中那样
        //在主工程中实现用于派发生命周期函数的的公共脚本
        //通过委托或事件达到生命周期函数的调用

        #endregion
    }
}

ILRuntimeMono

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ILRuntimeMono : MonoBehaviour
{
    //Awake相当于构造函数 在脚本添加的时候就会自动执行
    //所以它不能使用这种事件分发的形式去触发它
    //public event Action awakeEvent;
    public event Action startEvent;
    public event Action updateEvent;

    //private void Awake()
    //{
    //    awakeEvent?.Invoke();
    //}
    
    void Start()
    {
        startEvent?.Invoke();
    }
    
    void Update()
    {
        updateEvent?.Invoke();
    }
}

ILRuntimeMain

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace HotFix_Project
{

    #region 委托调用

    public delegate void MyILRuntimeDel1();
    public delegate int MyILRuntimeDel2(int i, int j);

    #endregion


    class ILRuntimeMain
    {
        /// <summary>
        /// 把逻辑处理权交给热更工程
        /// 它是一个启动函数
        /// </summary>
        public static void Start()
        {
            #region ILRuntime调用Unity

            ////使用ILRuntime创建的空物体
            //GameObject obj = new GameObject("ILRuntime创建的空物体");
            //obj.transform.position = new Vector3(10, 10, 10);
            ////Debug.Log(obj.transform.position);//(10.00, 10.00, 10.00)

            ////添加物理的dll后 可以正常使用Rigidbody了
            //Rigidbody rigidBody = obj.AddComponent<Rigidbody>();
            //rigidBody.mass = 9999;

            //// 可以直接添加Unity自己写的脚本
            //obj.AddComponent<Lesson10_Test>();

            #endregion



            #region 委托调用



            ////在ILRuntime中 申明 Unity中自定义委托变量 装载 ILRuntime的函数 正常使用
            //MyUnityDel1 fun = Fun1;
            //fun();//IL_Fun1

            //MyUnityDel2 fun2 = Fun2;
            //int result = fun2(5, 6);//IL_Fun2
            //Debug.Log(result);//11




            ////在Unity中 申明 Unity中自定义委托变量 关联ILRuntime工程中的函数

            ////找到我们在Unity的脚本
            //Lesson11_更多跨域调用_委托调用 lesson11 = GameObject.FindObjectOfType<Lesson11_更多跨域调用_委托调用>();

            ////直接设置Unity中自定义委托变量为ILRuntime工程中的函数会报错
            ////报错信息:KeyNotFoundException: Cannot find convertor for global::MyUnityDel1

            ////使用自定义委托
            ////注册MyUnityDel1和MyUnityDel2的委托和委托转换器后才能正常执行
            //lesson11.fun = Fun1;
            //lesson11.fun();//IL_Fun1
            //lesson11.fun2 = Fun2;
            //result = lesson11.fun2(7, 7);//IL_Fun2
            //Debug.Log(result);//14




            ////使用Action或者Func
            ////因为使用的是Action或者Func 不需要注册委托转换器
            //lesson11.funAction = Fun1;
            //lesson11.funAction();//IL_Fun1
            //lesson11.funFunc = Fun2;
            //result = lesson11.funFunc(8, 8);//IL_Fun2
            //Debug.Log(result);//16



            ////在ILRuntime中自定义委托后使用 直接使用
            //MyILRuntimeDel1 funIL = Fun1;
            //funIL();//IL_Fun1
            //MyILRuntimeDel2 funIL2 = Fun2;
            //result = fun2(1, 1);//IL_Fun2
            //Debug.Log(result);//2

            #endregion


            #region 跨域继承Unity中的类

            //Lesson12_Son lesson12_Son = new Lesson12_Son();
            //lesson12_Son.TestFun("哈哈哈");
            //lesson12_Son.TestAbstract(99);
            //lesson12_Son.valuePublic = 100;

            #endregion

            #region 跨域继承Unity中的类的注意事项


            //Lesson13_Son lesson13_Son = new Lesson13_Son();
            //lesson13_Son.TestAbstract(666);
            //lesson13_Son.TestInterface();

            //Lesson13_InheritAllSon lesson13_InheritAll = new Lesson13_InheritAllSon();
            //lesson13_InheritAll.TestAbstract(666);
            //lesson13_InheritAll.TestInterface();

            #endregion

            #region CLR重定向和CLR绑定

            ////得到当前系统时间
            //System.DateTime currentTime = System.DateTime.Now;
            //for (int i = 0; i < 100000; i++)
            //{
            //    Lesson14_更多跨域调用_CLR重定向和CLR绑定.TestFun(i, i);
            //}
            //Debug.Log("十万次循环花费的时间:" + (System.DateTime.Now - currentTime).TotalMilliseconds + "ms");

            #endregion


            #region 值类型绑定
            //System.DateTime currentTime = System.DateTime.Now;
            //Vector3 v1 = new Vector3(123, 54, 567);
            //Vector3 v2 = new Vector3(342, 678, 123);
            //float dot = 0;
            //for (int i = 0; i < 1000000; i++)
            //{
            //    dot += Vector3.Dot(v1, v2);
            //}

            //Vector2 v3 = new Vector2(12, 56);
            //Vector2 v4 = new Vector2(123123, 45345);
            //for (int i = 0; i < 1000000; i++)
            //{
            //    dot += Vector3.Dot(v3, v4);
            //}

            //Debug.Log("值类型计算花费的时间:" + (System.DateTime.Now - currentTime).TotalMilliseconds + "ms");
            #endregion

            #region 热更工程中使用MonoBehaviour

            GameObject obj = new GameObject();
            ILRuntimeMono mono = obj.AddComponent<ILRuntimeMono>();

            //由于Awake执行时机的特殊性  Awake会在脚本添加时自动执行 我们可以在添加了对应脚本后直接执行初始化相关逻辑即可
            //无需通过事件或者委托的形式去触发它
            Debug.Log("Awake");
            //mono.awakeEvent += () =>
            //{
            //    Debug.Log("Awake");
            //};
            mono.startEvent += () =>
            {
                Debug.Log("Start");
            };

            mono.updateEvent += () =>
            {
                Debug.Log("Update");
            };
            #endregion

        }


        #region 委托调用

        public static void Fun1()
        {
            Debug.Log("IL_Fun1");
        }

        public static int Fun2(int a, int b)
        {
            Debug.Log("IL_Fun2");
            return a + b;
        }

        #endregion

    }
}

16.3 练习题

完善对ILRuntimeMono脚本的封装,提供一个添加事件和移除事件的方法,供外部使用,请将完善后的代码作为答案回答





转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏