20.调试相关

20.调试相关


20.1 知识点

复制之前的主入口调用

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

准备ILRuntime调试相关插件

  1. ILRuntime 2.1.0以下的版本需要前往ILRuntime的Github页面获取插件,地址:https://github.com/Ourpalm/ILRuntime。打开页面后,点击右侧的Releases,往下拉会看到很多2.1.0之前的版本,在对应版本处点开Asset,下载调试插件ILRuntimeDebuggerLauncher.vsix。

  2. ILRuntime 2.1.0及其以上的版本在VS上方的拓展-管理拓展-搜索ILRuntime Debugger后安装。或者在rider中搜索ILRuntime Debugger插件,或者直接去以下网址:https://plugins.jetbrains.com/plugin/19528-ilruntime-debugger

下载安装成功后重启IDE,可以看到调试是有附加到ILruntime。

断点调试的必备工作

步骤概述

  1. 在初始化处注册调试服务:appDomain.DebugService.StartDebugService(端口号)

  2. 通过协同程序,等待调试器链接(否则我们无法对一开始的逻辑进行断点)。判断调试器链接的API为appDomain.DebugService.IsDebuggerAttached

注意:一般只有在开发周期才会这样去处理。如果最终发布了,就不用启动我们的调试服务,也不用去通过协程延迟执行后面的逻辑。

去ILruntime管理器InitILRuntime启动调试服务,注意卸载初始化函数的最后,在初始化ILRuntime主线程的线程ID后

// 启动调试服务
appDomain.DebugService.StartDebugService(56000);

在ILruntimeMain随便添加一行调试代码。

如果想要在ILruntimeMain热更工程断点调试,要必须保证Unity工程或者项目已经启动才能在ILruntimeMain热更工程附加ILruntime进行调试,所以一定要先运行了游戏再去附加调试,但是直接一启动就来不及了。

// 如果想要在ILruntimeMain热更工程断点调试,要必须保证Unity工程或者项目已经启动才能在ILruntimeMain热更工程附加ILruntime进行调试
// 所以一定要先运行了游戏再去附加调试,但是直接一启动就来不及了
Debug.Log(123);

为了解决一启动来不及附加的问题,修改ilruntimeManager,添加协程,让回调在协程中运行,在之前使用appDomain.DebugService.IsDebuggerAttached判断是否附加调试

/// <summary>
/// 去异步加载我们的热更相关的dll和pdb文件
/// </summary>
/// <returns></returns>
IEnumerator LoadHotUpdateInfo(UnityAction callBack)
{

    //...
    //将我们两个文件的内存流用于初始化 appDomain 我们之后就可以通过该对象来执行我们对应的热更代码了
    appDomain.LoadAssembly(dllStream, pdbStream, new PdbReaderProvider());

    InitILRuntime();

    StartCoroutine(WaitDebugger(callBack));
}

IEnumerator WaitDebugger(UnityAction callBack)
{
    //不停判断是否有调试器接入 如果false一直等待
    //直到为true 代表有调试器接入
    while (!appDomain.DebugService.IsDebuggerAttached)
    {
        yield return null;
    }

    ILRuntimeLoadOverDoSomthing();

    //当ILRuntime启动完毕 想要在外部执行的内容
    callBack?.Invoke();
}

但是我们只有在开发模式才这样处理,我们可以添加是否是开发模式标识

private bool isDebug = true;

/// <summary>
/// 初始化ILRuntime相关的内容
/// </summary>
unsafe private void InitILRuntime()
{
    //...

    if (isDebug)
    {
        //启动调试服务
        appDomain.DebugService.StartDebugService(56000);
    }

}

/// <summary>
/// 去异步加载我们的热更相关的dll和pdb文件
/// </summary>
/// <returns></returns>
IEnumerator LoadHotUpdateInfo(UnityAction callBack)
{

    ...
    InitILRuntime();

    if (isDebug)
    {
        StartCoroutine(WaitDebugger(callBack));
    }
    else
    {
        ILRuntimeLoadOverDoSomthing();

        //当ILRuntime启动完毕 想要在外部执行的内容
        callBack?.Invoke();
    }

}

IEnumerator WaitDebugger(UnityAction callBack)
{
    print("等到调试器接入");
    //不停判断是否有调试器接入 如果false一直等待
    //直到为true 代表有调试器接入
    while (!appDomain.DebugService.IsDebuggerAttached)
    {
        yield return null;
    }

    print("调试器接入成功");
    yield return new WaitForSeconds(1f);

    ILRuntimeLoadOverDoSomthing();

    //当ILRuntime启动完毕 想要在外部执行的内容
    callBack?.Invoke();
}

进行断点调试

断点概述

在ILruntimeMain热更工程的 调试页签中 选择 Attach to ILRuntime,即可开始断点调试。

注意:

  1. 附加到ILRuntime后,弹出窗口中填写的内容为IP地址和端口号,意味着我们可以调试各种设备,只要保证IP地址和端口号正确即可。

  2. 如果ILRuntime相关的dll和pdb文件没有加载成功,调试会失败。

isDebug改成true,再运行Unity,在ILruntimeMain热更工程中选择 Attach to ILRuntime,如果附加不上尝试重新生成ILruntimeMain热更工程后重来

总结

  1. 进行ILRuntime调试需要安装插件。

  2. 想要进行断点调试,我们需要注册调试服务。想要及时进断点,那么可以配合协同程序等待调试器链接。

  3. 可以利用IP和端口调试特点来调试移动设备。


20.2 知识点代码

Lesson20_调试相关

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

public class Lesson20_调试相关 : MonoBehaviour
{
    void Start()
    {
        #region 复制之前的主入口调用

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

        #endregion

        #region 知识点一 准备ILRuntime调试相关插件

        //1.ILRuntime 2.1.0以下的版本 需要前往ILRuntime的Github页面获取插件
        //  地址:https://github.com/Ourpalm/ILRuntime
        //  打开页面后,点击右侧的Releases,往下拉会看到很多2.1.0之前的版本,在对应版本处点开Asset,下载调试插件ILRuntimeDebuggerLauncher.vsix


        //2.ILRuntime 2.1.0及其以上的版本 
        //  在VS上方的
        //  拓展-管理拓展-搜索ILRuntime Debugger后安装
        
        // rider搜索ILRuntime Debugger插件 或者直接去以下网址
        // https://plugins.jetbrains.com/plugin/19528-ilruntime-debugger

        //下载安装成功后 重启ide
        //可以看到调试是有附加到ILruntime
        
        #endregion

        #region 知识点二 断点调试的必备工作

        //1.在初始化处 注册调试服务
        //  appDomain.DebugService.StartDebugService(端口号)

        //2.通过协同程序,等待调试器链接(否则我们无法对一开始的逻辑 进行断点)
        //  判断调试器链接的API为 appDomain.DebugService.IsDebuggerAttached

        //注意:一般只有在开发周期 才会这样去处理
        //如果最终发布了 就不用启动我们的调试服务 也不用去通过协程延迟执行后面的逻辑

        #endregion

        #region 知识点三 进行断点调试

        //在ILruntimeMain热更工程的 调试页签中 选择 Attach to ILRuntime
        //即可开始断点调试

        //注意:
        //1.附加到ILRuntime后
        //  弹出窗口中填写的内容为IP地址和端口号,意味着我们可以调试各种设备
        //  只要保证IP地址和端口号正确即可

        //2.如果ILRuntime相关的dll和pdb文件没有加载成功,调试会失败

        #endregion

        #region 总结

        //1.进行ILRuntime调试需要安装插件
        //2.想要进行断点调试,我们需要注册调试服务
        //  想要及时进断点,那么可以配合协同程序等待调试器链接
        //3.可以利用IP和端口调试特点来调试移动设备

        #endregion
    }
}

ILRuntimeManager

using ILRuntime.Mono.Cecil.Pdb;
using ILRuntime.Runtime.Enviorment;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading;
using ILRuntime.CLR.Method;
using ILRuntime.CLR.Utils;
using ILRuntime.Runtime.Intepreter;
using ILRuntime.Runtime.Stack;
using Lesson12_FatherNameSpace;
using Lesson13_FatherNameSpace;
using Lesson13_InheritAllNameSpace;
using Lesson13_InterfaceNameSpace;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;

namespace BaseFramework
{
    public class ILRuntimeManager : BaseSingletonInMonoBehaviour<ILRuntimeManager>
    {
        public AppDomain appDomain;

        //用于存储加载出来的 两个文件的内存流对象
        private MemoryStream dllStream;
        private MemoryStream pdbStream;

        private bool isStart = false;


        private bool isDebug = true;
        
        /// <summary>
        /// 用于启动ILRuntime初始化方法
        /// </summary>
        public void StartILRuntime(UnityAction callBack = null)
        {
            if (!isStart)
            {
                isStart = true;
                appDomain = new AppDomain();
                StartCoroutine(LoadHotUpdateInfo(callBack));
            }
        }

        public void StopILRuntime()
        {
            //1.释放流
            if (dllStream != null)
                dllStream.Close();
            if (pdbStream != null)
                pdbStream.Close();
            dllStream = null;
            pdbStream = null;
            //2.清空appDomain
            appDomain = null;

            isStart = false;
        }

        /// <summary>
        /// 初始化ILRuntime相关的内容
        /// </summary>
        unsafe private void InitILRuntime()
        {
            //其他初始化
            
            //无参无返回值不需要委托注册
            //MyUnityDel1因为转成的是无参无返回值的Action,Action在热更新里可以直接使用,所以不需要进行委托的注册
            //委托转换器注册(要把自定义委托无参无返回值MyUnityDel1委托转换为Action,其他自定义委托可以看语法也转成Action或Func)
            appDomain.DelegateManager.RegisterDelegateConvertor<MyUnityDel1>((act) =>
            {
                return new MyUnityDel1(() =>
                {
                    ((System.Action)act)();
                });
            });

            //有参有返回值委托注册 因为是自己定的规则要自己手动进行委托注册
            appDomain.DelegateManager.RegisterFunctionDelegate<System.Int32, System.Int32, System.Int32>();
            
            //委托转换器注册(要把自定义委托有参有返回值MyUnityDel2委托转换为Func,其他自定义委托可以看语法也转成Action或Func)
            appDomain.DelegateManager.RegisterDelegateConvertor<MyUnityDel2>((act) =>
            {
                return new MyUnityDel2((i, j) =>
                {
                    return ((System.Func<System.Int32, System.Int32, System.Int32>)act)(i, j);
                });
            });

            
            //注册跨域继承的父类适配器
            appDomain.RegisterCrossBindingAdaptor((new Lesson12_FatherAdapter()));
            appDomain.RegisterCrossBindingAdaptor((new Lesson13_FatherAdapter()));
            appDomain.RegisterCrossBindingAdaptor((new Lesson13_InterfaceAdapter()));
            appDomain.RegisterCrossBindingAdaptor((new Lesson13_InheritAllAdapter()));
            
            
            //注册协程跨域适配器
            appDomain.RegisterCrossBindingAdaptor(new CoroutineAdapter());
            //注册异步跨域适配器
            appDomain.RegisterCrossBindingAdaptor(new IAsyncStateMachineClassInheritanceAdaptor());
            
            // 值类型绑定注册
            appDomain.RegisterValueTypeBinder(typeof(Vector3), new Vector3Binder());
            appDomain.RegisterValueTypeBinder(typeof(Vector2), new Vector2Binder());
            appDomain.RegisterValueTypeBinder(typeof(Quaternion), new QuaternionBinder());
            
            
            
            
            //CLR重定向内容 要写到CLR绑定之前
            //得到想要重定向类的Type
            System.Type debugType = typeof(Debug);
            //得到想要重定向方法的方法信息
            MethodInfo methodInfo = debugType.GetMethod("Log", new System.Type[] { typeof(object) });
            //进行CLR重定向
            appDomain.RegisterCLRMethodRedirection(methodInfo, MyLog);
            
            
            //注册Litjson
            LitJson.JsonMapper.RegisterILRuntimeCLRRedirection(appDomain);
            
            //注册CLR绑定信息
            ILRuntime.Runtime.Generated.CLRBindings.Initialize(appDomain);
            
            
            
            //初始化ILRuntime相关信息(目前只需要告诉ILRuntime主线程的线程ID,主要目的是能够在Unity的Profiler剖析器窗口中分析问题)
            appDomain.UnityMainThreadID = Thread.CurrentThread.ManagedThreadId;

            if (isDebug)
            {
                //启动调试服务
                appDomain.DebugService.StartDebugService(56000);
            }

        }

        
        
        
        // MyLog函数用于记录日志,并输出调用栈信息
        // 参数:
        //   __intp: ILRuntime解释器实例
        //   __esp: 栈指针
        //   __mStack: 托管堆栈
        //   __method: 当前方法
        //   isNewObj: 是否为新对象
        unsafe StackObject* MyLog(ILIntepreter __intp, StackObject* __esp, List<object> __mStack, CLRMethod __method, bool isNewObj)
        {
            // 获取当前应用程序域
            ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;
            // 指针的声明
            StackObject* ptr_of_this_method;
            // 为了方便减法运算,计算栈顶指针位置
            StackObject* __ret = ILIntepreter.Minus(__esp, 1);

            // 获取栈顶参数的指针
            ptr_of_this_method = ILIntepreter.Minus(__esp, 1);
            // 将栈顶参数转换为System.Object类型
            System.Object @message = (System.Object)typeof(System.Object).CheckCLRTypes(StackObject.ToObject(ptr_of_this_method, __domain, __mStack), (ILRuntime.CLR.Utils.Extensions.TypeFlags)0);
            // 释放栈顶参数的指针
            __intp.Free(ptr_of_this_method);

            // 获取调用栈信息 是我们自己添加的
            var stackTrace = __domain.DebugService.GetStackTrace(__intp);

            // 输出日志信息和调用栈信息
            UnityEngine.Debug.Log(@message + "\n" + stackTrace);

            return __ret;
        }

        
        
        
        
        
        
        
        
        /// <summary>
        /// 启动完毕并且初始化完毕后 想要执行的热更新的逻辑
        /// </summary>
        private void ILRuntimeLoadOverDoSomthing()
        {
        }

        /// <summary>
        /// 去异步加载我们的热更相关的dll和pdb文件
        /// </summary>
        /// <returns></returns>
        IEnumerator LoadHotUpdateInfo(UnityAction callBack)
        {
            //这部分知识点在 Unity网络开发基础当中有讲解
            //加载本地的DLL文件
#if UNITY_ANDROID
        UnityWebRequest reqDll = UnityWebRequest.Get(Application.streamingAssetsPath + "/HotFix_Project.dll");
#else
            UnityWebRequest reqDll =
                UnityWebRequest.Get("file:///" + Application.streamingAssetsPath + "/HotFix_Project.dll");
#endif
            yield return reqDll.SendWebRequest();
            //如果失败了
            if (reqDll.result != UnityWebRequest.Result.Success)
                print("加载DLL文件失败" + reqDll.responseCode + reqDll.result);
            //读取加载的DLL数据
            byte[] dll = reqDll.downloadHandler.data;
            reqDll.Dispose();

#if UNITY_ANDROID
        UnityWebRequest reqPdb = UnityWebRequest.Get(Application.streamingAssetsPath + "/HotFix_Project.pdb");
#else
            UnityWebRequest reqPdb =
                UnityWebRequest.Get("file:///" + Application.streamingAssetsPath + "/HotFix_Project.pdb");
#endif
            yield return reqPdb.SendWebRequest();
            //如果失败了
            if (reqPdb.result != UnityWebRequest.Result.Success)
                print("加载Pdb文件失败" + reqPdb.responseCode + reqPdb.result);
            //读取加载的DLL数据
            byte[] pdb = reqPdb.downloadHandler.data;
            reqPdb.Dispose();

            //3.将加载的数据以流的形式(文件流或者内存流对象)传递给AppDomain对象中的LoadAssembly方法
            //注意 这里使用流 不要用完就关 一定要等到热更相关内容不使用了 再关闭
            dllStream = new MemoryStream(dll);
            pdbStream = new MemoryStream(pdb);
            //将我们两个文件的内存流用于初始化 appDomain 我们之后就可以通过该对象来执行我们对应的热更代码了
            appDomain.LoadAssembly(dllStream, pdbStream, new PdbReaderProvider());

            InitILRuntime();

            if (isDebug)
            {
                StartCoroutine(WaitDebugger(callBack));
            }
            else
            {
                ILRuntimeLoadOverDoSomthing();

                //当ILRuntime启动完毕 想要在外部执行的内容
                callBack?.Invoke();
            }

        }

        IEnumerator WaitDebugger(UnityAction callBack)
        {
            print("等到调试器接入");
            //不停判断是否有调试器接入 如果false一直等待
            //直到为true 代表有调试器接入
            while (!appDomain.DebugService.IsDebuggerAttached)
            {
                yield return null;
            }
            
            print("调试器接入成功");
            yield return new WaitForSeconds(1f);
            
            ILRuntimeLoadOverDoSomthing();

            //当ILRuntime启动完毕 想要在外部执行的内容
            callBack?.Invoke();
        }
    }
}

ILRuntimeMain

using LitJson;
using System;
using System.Collections;
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>
        /// 把逻辑处理权交给ILruntimeMain热更工程
        /// 它是一个启动函数
        /// </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 ILruntimeMain热更工程中使用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 协同程序和异步函数

            //Lesson17_更多跨域调用_协同程序和异步函数 lesson17 = GameObject.FindObjectOfType<Lesson17_更多跨域调用_协同程序和异步函数>();
            //lesson17.StartCoroutine(Lesson17Courtinue());

            //Lesson17Async();

            #endregion


            #region 序列化库 

            //Lesson19_Test test = new Lesson19_Test();
            //test.testInt = 99;
            //test.testStr = "韬韬韬";
            //test.testList = new List<int>() { 1, 2, 3, 4, 5 };
            //test.testDictionary = new Dictionary<string, int>() { { "1", 99 }, { "2", 88 }, { "3", 77 } };

            ////序列化Json字符串
            //string str = JsonMapper.ToJson(test);
            //Debug.Log(str);


            ////反序列化
            //Lesson19_Test test2 = JsonMapper.ToObject<Lesson19_Test>(str);
            //Debug.Log(test2.testInt);
            //Debug.Log(test2.testStr);

            #endregion

            #region 调试相关

            //如果想要在ILruntimeMain热更工程断点调试,要必须保证Unity工程或者项目已经启动才能在ILruntimeMain热更工程附加ILruntime进行调试
            //所以一定要先运行了游戏在去附加调试,但是直接一启动就来不及了
            Debug.Log(123);

            #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

        #region 协同程序和异步函数

        public static IEnumerator Lesson17Courtinue()
        {
            Debug.Log(0);
            yield return new WaitForSeconds(1f);

            Debug.Log(1);
            yield return new WaitForSeconds(1f);

            Debug.Log(2);
            yield return new WaitForSeconds(1f);

            Debug.Log(3);
            yield return new WaitForSeconds(1f);
        }

        public static async void Lesson17Async()
        {
            Debug.Log("Lesson17Async 1");
            await Task.Delay(1000);

            Debug.Log("Lesson17Async 2");
            await Task.Delay(1000);

            Debug.Log("Lesson17Async 3");
            await Task.Delay(1000);

            Debug.Log("Lesson17Async 4");
            await Task.Delay(1000);
        }

        #endregion 

    }
}

20.3 练习题

想要在ILRuntimeILruntimeMain热更工程中进行断点调试,我们需要进行哪些操作?

  1. 下载安装ILRuntime调试插件
  2. 注册调试服务appDomain.DebugService.StartDebugService(端口号)
  3. 通过协同程序,利用appDomain.DebugService.IsDebuggerAttached判断是否有调试器接入,调试器接入后再执行主逻辑
  • 完成以上3步后,我们便可以在ILruntimeMain热更工程中填写对应ip地址和端口号进行断点调试


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

×

喜欢就点赞,疼爱就打赏