精华内容
下载资源
问答
  • ILRunTime

    2019-10-07 15:27:25
    关于ILRunTime的介绍,详细的可...ILRuntime的优势 同市面上的其他热更方案相比,ILRuntime主要有以下优点: 无缝访问C#工程的现成代码,无需额外抽象脚本API 直接使用VS2015进行开发,ILRuntime的解译引擎支持...

    关于ILRunTime的介绍,详细的可参考 官方中文文档地址:https://ourpalm.github.io/ILRuntime/public/v1/guide/index.html

    ILRuntime的优势

    同市面上的其他热更方案相比,ILRuntime主要有以下优点:

    • 无缝访问C#工程的现成代码,无需额外抽象脚本API
    • 直接使用VS2015进行开发,ILRuntime的解译引擎支持.Net 4.6编译的DLL
    • 执行效率是L#的10-20倍
    • 选择性的CLR绑定使跨域调用更快速,绑定后跨域调用的性能能达到slua的2倍左右(从脚本调用GameObject之类的接口)
    • 支持跨域继承
    • 完整的泛型支持
    • 拥有Visual Studio的调试插件,可以实现真机源码级调试。支持Visual Studio 2015 Update3 以及Visual Studio 2017

    ILRunTime资源

    官方Github地址:https://github.com/Ourpalm/ILRuntime

    Unity Demo 地址:https://github.com/Ourpalm/ILRuntimeU3D/

    对项目中运用UnityDemo就可以

     

     

    在刚开始使用的时候,会有部分的坑,解决办法在这个大佬的文章中有介绍

    https://blog.csdn.net/u010294054/article/details/90724454

    首先将需要导入的资源,导入到项目中,并配置环境

     

     

     

    ILRunTime   HelloWorld  实例应用

    示例一:

    创建如图文件夹,

    creat -----assembly definition

    Assembly Definition 自己可以完成程序集的定义,从而将一个大型的或脚本过多的项目很好的规范起来

    创建脚本

    注意:  脚本的必须定义命名空间,脚本不能继承自 MonoBehavior

     

    找到生成的 .dll和.pdb文件

    将其放入文件夹里边(自己创建的文件夹)

    然后在IlManager.cs中实现热更新的调用

    public class HelloWorld : MonoBehaviour
    {
        //AppDomain是ILRuntime的入口,最好是在一个单例类中保存,整个游戏全局就一个,这里为了示例方便,每个例子里面都单独做了一个
        //大家在正式项目中请全局只创建一个AppDomain
        AppDomain appdomain;
    
        void Start()
        {
            StartCoroutine(LoadHotFixAssembly());
        }
    
        IEnumerator LoadHotFixAssembly()
        {
            //首先实例化ILRuntime的AppDomain,AppDomain是一个应用程序域,每个AppDomain都是一个独立的沙盒
            appdomain = new ILRuntime.Runtime.Enviorment.AppDomain();
            //正常项目中应该是自行从其他地方下载dll,或者打包在AssetBundle中读取,平时开发以及为了演示方便直接从StreammingAssets中读取,
            //正式发布的时候需要大家自行从其他地方读取dll
    
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            //这个DLL文件是直接编译HotFix_Project.sln生成的,已经在项目中设置好输出目录为StreamingAssets,在VS里直接编译即可生成到对应目录,无需手动拷贝
    #if UNITY_ANDROID
           // WWW www = new WWW(Application.streamingAssetsPath + "/HotFix_Project.dll");
    #else
            WWW www = new WWW(@"E:\\UnityMF\Unity热更新\AssetBundleProject\IlRunTimeTest\HorFix\IL.dll");
    #endif
            while (!www.isDone)
                yield return null;
            if (!string.IsNullOrEmpty(www.error))
                UnityEngine.Debug.LogError(www.error);
            byte[] dll = www.bytes;
            www.Dispose();
    
            //PDB文件是调试数据库,如需要在日志中显示报错的行号,则必须提供PDB文件,不过由于会额外耗用内存,正式发布时请将PDB去掉,下面LoadAssembly的时候pdb传null即可
    #if UNITY_ANDROID
            www = new WWW(Application.streamingAssetsPath + "/HotFix_Project.pdb");
    #else
            www = new WWW(@"E:\\UnityMF\Unity热更新\AssetBundleProject\IlRunTimeTest\HorFix\IL.pdb");
    #endif
            while (!www.isDone)
                yield return null;
            if (!string.IsNullOrEmpty(www.error))
                UnityEngine.Debug.LogError(www.error);
            byte[] pdb = www.bytes;
            using (System.IO.MemoryStream fs = new MemoryStream(dll))
            {
                using (System.IO.MemoryStream p = new MemoryStream(pdb))
                {
                    appdomain.LoadAssembly(fs, p, new Mono.Cecil.Pdb.PdbReaderProvider());
                }
            }
    
            InitializeILRuntime();
            OnHotFixLoaded();
        }
    
        void InitializeILRuntime()
        {
            //这里做一些ILRuntime的注册,HelloWorld示例暂时没有需要注册的
        }
    
        void OnHotFixLoaded()
        {
            //HelloWorld,第一次方法调用
            appdomain.Invoke("IL.Test","Init",null,null);
    
        }

     

     

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • ILRuntime

    2021-07-15 19:51:58
    http://ourpalm.github.io/ILRuntime/public/v1/guide/index.html
    展开全文
  • ILRuntime 1.4

    2018-11-23 16:47:04
    ILRuntime的最新版本,可用于Unity3D等C#环境下的热更新。
  • ILRuntime 1.6

    2020-06-11 14:25:49
    直接使用VS2015进行开发,ILRuntime的解译引擎支持.Net 4.6编译的DLL 执行效率是L#的10-20倍 选择性的CLR绑定使跨域调用更快速,绑定后跨域调用的性能能达到slua的2倍左右(从脚本调用GameObject之类的接口) 支持...
  • ILRuntime Unity热更新

    万次阅读 多人点赞 2019-05-28 19:35:44
    在新的项目中,使用到了ILRuntime的热更新方式,不同于XLua等,这种方式的热更新是由纯C#实现的,所以就不需要客户端懂Lua的代码。更详细的介绍可以看官方的文档。 官方的介绍及文档为:...

    在新的项目中,使用到了ILRuntime的热更新方式,不同于XLua等,这种方式的热更新是由纯C#实现的,所以就不需要客户端懂Lua的代码。更详细的介绍可以看官方的文档。

    官方的介绍及文档为:http://ourpalm.github.io/ILRuntime/public/v1/guide/index.html

    目前大致的理解为:将游戏分为两个部分,Unity和Hotfix。其中主要的游戏逻辑部分也就是可能需要热更的部分都写在Hotfix当中,然后将其导出为Hotfix.dll文件供Unity使用。游戏上线后若需要更新代码,只需要修改Hotfix中的代码,然后生成新的Hotfix.dll文件热更上去即可。

    接下来就用一个简单的Demo来实现这么一个过程。思路为,把原本UI逻辑的类放在Hotfix当中,由于没有继承MonoBehaviour,所以通过实现一个带有Start,Update等方法的接口,然后在Unity部分相应调用,来实现Hotfix中的生命周期。同时通过GameObject.Find的方法找到对应的组件,进行操作。

     

    创建Unity工程环境

    首先我们创建一个新的Unity工程,然后将ILRuntime需要的部分导入工程当中,即官方Demo中的Mono.Cecil.20,Mono.Cecil.Pdb,ILRuntime三个文件夹(删除ILRuntime/Adapters/Editor)

    勾选 player settings -> Other Settings - > Allow 'unsafe' Code选项

     

    创建Hotfix工程环境

    打开我们的VS,文件->新建->项目,创建一个C#类库,命名为Hotfix,如图

    然后在解决方案->引用处右键,添加引用,如图

    其中UnityEngine.dll,UnityEngine.UI.dll 和 UnityEngine.CoreModule.dll三个文件在Unity的安装目录下,Assembly-CSharp.dll在上面创建的Unity工程的Library/ScriptAssemblies目录下

    注:UnityEngine.CoreModule.dll是在Unity2017.2之后的版本才有,之前的版本可以不用添加,若在官方demo中UnityEngine.dll报找不到的错误的话,重新引用下正确目录下的dll文件即可。

     

    创建接口与适配器,并实现接口

    首先我们可以在Unity中创建一个简单的接口,用于处理Hotfix中的生命周期

    public interface IUI
    {
        void Start();
        void Update();
    }

    然后在Hotfix工程中,新建一个类Main.cs,实现IUI接口

    namespace Hotfix
    {
        //IUI为unity中的接口,所以要在unity中实现一个继承适配器
        public class MainUI:IUI
        {
            public void Start()
            {
    
            }
    
            public void Update()
            {
    
            }
        }
    }

    由于IUI是Unity的接口,而MainUI是Hotfix的类,这里有一个ILRuntime中跨域继承的概念,官方文档提到如果你想在热更DLL项目当中继承一个Unity主工程里的类,或者实现一个主工程里的接口,你需要在Unity主工程中实现一个继承适配器。

    所以我们在Unity中创建一个类 InterfaceIUIAdaptor.cs,实现继承适配器(根据文档给的代码进行修改)

    using ILRuntime.CLR.Method;
    using ILRuntime.Runtime.Enviorment;
    using ILRuntime.Runtime.Intepreter;
    using System;
    
    public class InterfaceIUIAdaptor : CrossBindingAdaptor
    {
        public override Type BaseCLRType {
            get {
                return typeof(IUI);//这是你想继承的那个类
            }
        }
    
        public override Type AdaptorType {
            get {
                return typeof(Adaptor);//这是实际的适配器类
            }
        }
    
        public override object CreateCLRInstance(ILRuntime.Runtime.Enviorment.AppDomain appdomain, ILTypeInstance instance)
        {
            return new Adaptor(appdomain, instance);//创建一个新的实例
        }
    
        //实际的适配器类需要继承你想继承的那个类,并且实现CrossBindingAdaptorType接口
        public class Adaptor : IUI, CrossBindingAdaptorType
        {
            ILTypeInstance instance;
            ILRuntime.Runtime.Enviorment.AppDomain appdomain;
    
            IMethod m_Start;
            bool m_StartGot;
    
            IMethod m_Update;
            bool m_UpdateGot;
    
            public Adaptor()
            {
    
            }
    
            public Adaptor(ILRuntime.Runtime.Enviorment.AppDomain appdomain, ILTypeInstance instance)
            {
                this.appdomain = appdomain;
                this.instance = instance;
            }
    
            public ILTypeInstance ILInstance { get { return instance; } }
    
            //你需要重写所有你希望在热更脚本里面重写的方法,并且将控制权转到脚本里去
            public void Start()
            {
                if (!m_StartGot)
                {
                    m_Start = instance.Type.GetMethod("Start", 0);
                    m_StartGot = true;
                }
                if (m_Start != null)
                {
                    appdomain.Invoke(m_Start, instance, null);//没有参数建议显式传递null为参数列表,否则会自动new object[0]导致GC Alloc
                }
            }
    
            public void Update()
            {
                if (!m_UpdateGot)
                {
                    m_Update = instance.Type.GetMethod("Update", 0);
                    m_UpdateGot = true;
                }
                if (m_Update != null)
                {
                    appdomain.Invoke(m_Update, instance, null);
                }
            }
        }
    }

     

    读取Hotfix.dll文件,并执行其内部操作

    首先我们将Hotfix工程中,解决方案右键生成,生成Hotfix.dll和Hotfix.pdb两个文件,将这两个文件拷贝到Unity的StreamingAssets目录下。

    然后我们创建一个新的类 Launch.cs,在这里面我们首先读取上面的两个Hotfix文件,然后进行一些ILRuntime的预设置,例如绑定继承适配器,注册委托等。最后我们要在里面找到Hotfix中实现IUI接口的类,因为这些类就是我们的UI逻辑类,然后在自己的Start,Update等生命周期方法中,调用Hotfix中IUI类对应的方法。代码如下:

    using ILRuntime.Runtime.Enviorment;
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using UnityEngine;
    
    public class Launch : MonoBehaviour
    {
        List<Action> DllUIUpdateList = new List<Action>();
    
        ILRuntime.Runtime.Enviorment.AppDomain appdomain;
        void Start()
        {
            StartCoroutine(LoadILRuntime());
        }
    
        IEnumerator LoadILRuntime()
        {
            //读取dll文件
            appdomain = new ILRuntime.Runtime.Enviorment.AppDomain();
    
            WWW www = new WWW(Application.streamingAssetsPath + "/Hotfix.dll");
    
            while (!www.isDone)
            {
                yield return null;
            }
            if (!string.IsNullOrEmpty(www.error))
            {
                Debug.LogError(www.error);
            }
            byte[] dll = www.bytes;
            www.Dispose();
    
            www = new WWW(Application.streamingAssetsPath + "/Hotfix.pdb");
    
            while (!www.isDone)
            {
                yield return null;
            }
            if (!string.IsNullOrEmpty(www.error))
            {
                Debug.LogError(www.error);
            }
            byte[] pdb = www.bytes;
            using (System.IO.MemoryStream fs = new MemoryStream(dll))
            {
                using (System.IO.MemoryStream p = new MemoryStream(pdb))
                {
                    appdomain.LoadAssembly(fs, p, new Mono.Cecil.Pdb.PdbReaderProvider());
                }
            }
    
            OnILRuntimeInit();
    
            OnILRuntimeInitialized();
        }
    
        void Update()
        {
            if (DllUIUpdateList.Count > 0)
            {
                foreach(var update in DllUIUpdateList)
                {
                    update();
                }
            }
        }
    
        void OnILRuntimeInit()
        {
            //跨域继承绑定适配器
            appdomain.RegisterCrossBindingAdaptor(new InterfaceIUIAdaptor());
            //Button点击事件的委托注册
            appdomain.DelegateManager.RegisterDelegateConvertor<UnityEngine.Events.UnityAction>((act) =>
            {
                return new UnityEngine.Events.UnityAction(() =>
                {
                    ((Action)act)();
                });
            });
        }
    
        void OnILRuntimeInitialized()
        {        
            //获取Hotfix.dll内部定义的类
            List<Type> allTypes = new List<Type>();
            var values = appdomain.LoadedTypes.Values.ToList();
            foreach (var v in values)
            {
                allTypes.Add(v.ReflectionType);
            }
            //去重
            allTypes = allTypes.Distinct().ToList();
    
            DllUIUpdateList.Clear();
            foreach (var v in allTypes)
            {
                //找到实现IUI接口的类 Adaptor 前面写的适配器IUI的类
                if (v.IsClass && v.GetInterface("Adaptor") != null)
                {
                    //生成实例
                    var gs = appdomain.Instantiate<IUI>(v.FullName);
    
                    //调用接口方法
                    gs.Start();
                    DllUIUpdateList.Add(gs.Update);
                }
            }
        }
    }
    

    注:代码中有一个委托注册的功能,是因为在Hotfix中调用UGUI的Button的onCkick,需要生成委托转换器,否则会报错

     

    搭建UI及实现UI逻辑

    Demo中,简单的在场景中创建一个简单的Button和Text,然后将Launch.cs挂到Canvas上即可。然后在我们之前Hotfix中创建的MainUI.cs中添加我们的UI逻辑:

    using UnityEngine;
    using UnityEngine.UI;
    
    namespace Hotfix
    {
        //IUI为unity中的接口,所以要在unity中实现一个继承适配器
        public class MainUI:IUI
        {
            Button m_btn;
            Text m_text;
            int count = 0;
            bool isClick = false;
    
            public void Start()
            {
                m_btn = GameObject.Find("Canvas/Button").GetComponent<Button>();
                m_text = GameObject.Find("Canvas/Text").GetComponent<Text>();
                m_text.text = "MainUI Start";
    
                //点击事件的委托需要在unity中实现委托转换器
                m_btn.onClick.AddListener(BtnClick);
            }
    
            public void Update()
            {
                if (isClick)
                {
                    if (count % 20 == 0)
                    {
                        m_text.text = "MainUI Update" + count / 20;
                    }
                    count++;
                }
            }
    
            void BtnClick()
            {
                isClick = true;
            }
        }
    }
    

    然后重新生成下dll文件,将原来Unity StreamingAssets下的文件替换掉即可(以后修改逻辑亦是如此,达到热更的效果)。

     

    运行效果如下:

     

    更详细的内容请看后面的文章~~~~

    展开全文
  • ILRuntime English Document 中文在线文档 由于近期墙的问题导致部分地区无法访问Github page,如遇到中文文档无法访问,请尝试翻墙 Unity Demo Project, Unity示例工程 QQ群:512079820
  • ILRuntime学习

    2019-04-22 17:08:11
    ILRuntime介绍 ILRuntime项目为基于C#的平台(例如Unity)提供了一个纯C#实现,快速、方便且可靠的IL运行时,使得能够在不支持JIT的硬件环境(如iOS)能够实现代码的热更新 ILRuntime优势 访问C#工程的现成代码,...

    ILRuntime介绍

    ILRuntime项目为基于C#的平台(例如Unity)提供了一个纯C#实现,快速、方便且可靠的IL运行时,使得能够在不支持JIT的硬件环境(如iOS)能够实现代码的热更新

    ILRuntime优势

    • 访问C#工程的现成代码,无需额外抽象脚本API
    • 直接使用VS2015进行开发,ILRuntime的解译引擎支持.Net 4.6编译的DLL
    • 执行效率是L#的10-20倍
    • 选择性的CLR绑定使跨域调用更快速,绑定后跨域调用的性能能达到slua的2倍左右(从脚本调用GameObject之类的接口)
    • 支持跨域继承
    • 完整的泛型支持
    • 拥有Visual Studio的调试插件,可以实现真机源码级调试。支持Visual Studio 2015 Update3 以及Visual Studio 2017
        

      

    ILRuntime demo工程

    1. 下载ILRuntimeU3D demo

    下载地址

    2. unity打开项目

    项目位置:\ILRuntimeU3D-master\ILRuntimeDemo

    3. vs打开HotFix_project工程

    \ILRuntimeU3D-master\HotFix_Project\HotFix_Project.sln

    4. 修改HotFix_Project工程的引用

    引用=》添加引用=》浏览 (如果存在可以先删除)

    1. UnityEngine

    F:/Unity/Editor/Data/PlaybackEngines/windowsstandalonesupport/Variations/win64_nondevelopment_mono/Data/Managed/UnityEngine.dll

    1. UnityEngine.UI

    F:/Unity/Editor/Data/UnityExtensions/Unity/GUISystem/UnityEngine.UI.dll

    1. UnityEngine.CoreModule

    F:/Unity/Editor/Data/Managed/UnityEngine/UnityEngine.CoreModule.dll

    5. 修改ILRuntimeU3D-master\ILRuntimeDemo*.csproj

    <AllowUnsafeBlocks>false</AllowUnsafeBlocks>

    6. 编译HotFix_Project工程

    • 右键=>生成
    • 生成成功,在\ILRuntimeU3D-master\ILRuntimeDemo\Assets\StreamingAssets\目录生成HotFix_Project.dll

    7. \ILRuntimeU3D-master\HotFix_Project\HotFix_Project.sln 热更新脚本

    8. \LRuntimeU3D-master\ILRuntimeDemo\ILRuntimeDemo.sln unity主工程

      


      

    ILRunTime的使用

    1. 下载ILRuntime相关文件

    2. 检查热更新

    • 如果有热更,进行热更新

    3. 实例化AppDomain(全局保存一个)

    AppDomain appdomain;
    appdomain = new ILRuntime.Runtime.Enviorment.AppDomain();
    

    4. 加载dll和pdb

    #if UNITY_ANDROID
          www = new WWW(Application.streamingAssetsPath + "/HotFix_Project.pdb");
    #else
          www = new WWW("file:///" + Application.streamingAssetsPath + "/HotFix_Project.pdb");
    #endif
          while (!www.isDone)
              yield return null;
          if (!string.IsNullOrEmpty(www.error))
              UnityEngine.Debug.LogError(www.error);
          byte[] pdb = www.bytes;
          using (System.IO.MemoryStream fs = new MemoryStream(dll))
          {
              using (System.IO.MemoryStream p = new MemoryStream(pdb))
              {
                  appdomain.LoadAssembly(fs, p, new Mono.Cecil.Pdb.PdbReaderProvider());
              }
          }
    

    5. 初始化

    • InitializeILRuntime();
    void InitializeILRuntime()
    {
      //这里做一些ILRuntime的注册,HelloWorld示例暂时没有需要注册的
    }
    
    • OnHotFixLoaded();
    void OnHotFixLoaded()
    {
      //第一次方法调用
    }
    

    6. 各个地方的使用

    • 主工程脚本调用热更脚本

      1. 调用类的静态方法
      • 方法1

      无参数

      //调用无参数静态方法,appdomain.Invoke("类名", "方法名", 对象引用, 参数列表);
      appdomain.Invoke("HotFix_Project.InstanceClass", "StaticFunTest", null, null);
      

      有参数

       //调用带参数的静态方法
       appdomain.Invoke("HotFix_Project.InstanceClass", "StaticFunTest2", null, 123);
      
      • 方法2

      无参数

      //预先获得IMethod,可以减低每次调用查找方法耗用的时间
      IType type = appdomain.LoadedTypes["HotFix_Project.InstanceClass"];
      //根据方法名称和参数个数获取方法
      IMethod method = type.GetMethod("StaticFunTest", 0);
      appdomain.Invoke(method, null, null);
      

      有参数

      IType intType = appdomain.GetType(typeof(int));
      //参数类型列表
      List<IType> paramList = new List<ILRuntime.CLR.TypeSystem.IType>();
      paramList.Add(intType);
      //根据方法名称和参数类型列表获取方法
      method = type.GetMethod("StaticFunTest2", paramList, null);
      appdomain.Invoke(method, null, 456);
      
      1. 调用类的成员方法
      • 方法1
        实例化对象
        object obj = appdomain.Instantiate("HotFix_Project.InstanceClass", new object[] { 233 })
        
        调用方法
        int id = (int)appdomain.Invoke("HotFix_Project.InstanceClass", "get_ID", obj, null);
        
      • 方法2
        实例化对象
        object obj2 = ((ILType)type).Instantiate();
        
        调用方法
        id = (int)appdomain.Invoke("HotFix_Project.InstanceClass", "get_ID", obj2, null);
        
      1. 调用类的静态泛型方法
      • 方法1(直接调用)
        构建参数
      IType stringType = appdomain.GetType(typeof(string));
      IType[] genericArguments = new IType[] { stringType };
      

      调用方法

      appdomain.InvokeGenericMethod("HotFix_Project.InstanceClass", "GenericMethod", genericArguments, null, "TestString");
      
      • 方法2(先获取IMethod)
        构建参数
        paramList.Clear();
        paramList.Add(intType);
        genericArguments = new IType[] { intType };
        
        调用方法
        method = type.GetMethod("GenericMethod", paramList, genericArguments);
        appdomain.Invoke(method, null, 33333);
        
    • 热更DLL里面的委托实例传到Unity主工程用

      1. 注册适配器
      //TestDelegateMethod, 这个委托类型为有个参数为int的方法,注册仅需要注册不同的参数搭配即可
      appdomain.DelegateManager.RegisterMethodDelegate<int>();
      
      //带返回值的委托的话需要用RegisterFunctionDelegate,返回类型为最后一个
      appdomain.DelegateManager.RegisterFunctionDelegate<int, string>();
      
      appdomain.DelegateManager.RegisterDelegateConvertor<TestDelegateMethod>((action) =>
      {
      //转换器的目的是把Action或者Func转换成正确的类型,这里则是把Action<int>转换成TestDelegateMethod
      return new TestDelegateMethod((a) =>
      {
          //调用委托实例
          ((System.Action<int>)action)(a);
      });
      });
      //对于TestDelegateFunction同理,只是是将Func<int, string>转换成TestDelegateFunction
      appdomain.DelegateManager.RegisterDelegateConvertor<TestDelegateFunction>((action) =>
      {
      return new TestDelegateFunction((a) =>
      {
          return ((System.Func<int, string>)action)(a);
      });
      });
      
      //下面再举一个这个Demo中没有用到,但是UGUI经常遇到的一个委托,例如UnityAction<float>
      appdomain.DelegateManager.RegisterDelegateConvertor<UnityEngine.Events.UnityAction<float>>((action) =>
      {
      return new UnityEngine.Events.UnityAction<float>((a) =>
      {
          ((System.Action<float>)action)(a);
      });
      });
      
      1. 使用
      appdomain.Invoke("HotFix_Project.TestDelegate", "Initialize2", null, null);
      appdomain.Invoke("HotFix_Project.TestDelegate", "RunTest2", null, null);
      
      TestMethodDelegate(789);
      TestFunctionDelegate(098);
      TestActionDelegate("Hello From Unity Main Project");
      
    • 继承(热更脚本中的类继承主工程脚本中的类)

      1. 注册适配器
      appdomain.RegisterCrossBindingAdaptor(new InheritanceAdapter());
      

      InheritanceAdapter.cs

      1. 实例化

    obj = appdomain.Instantiate(“HotFix_Project.TestInheritance”);
    ```
    3. 使用

    obj.TestAbstract(123);
    obj.TestVirtual("Hello");
    
    • CLR重定向

      使用到的地方(当我们需要挟持原方法实现,添加一些热更DLL中的特殊处理的时候,就需要CLR重定向了)

      • 重定向Log方法
        var mi = typeof(Debug).GetMethod("Log", new System.Type[] { typeof(object) });
        appdomain.RegisterCLRMethodRedirection(mi, Log_11);
        
      • Log11的方法
        //编写重定向方法对于刚接触ILRuntime的朋友可能比较困难,比较简单的方式是通过CLR绑定生成绑定代码,然后在这个基础上改,比如下面这个代码是从UnityEngine_Debug_Binding里面复制来改的
        //如何使用CLR绑定请看相关教程和文档
        unsafe static StackObject* Log_11(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack, CLRMethod __method, bool isNewObj)
        {
            //ILRuntime的调用约定为被调用者清理堆栈,因此执行这个函数后需要将参数从堆栈清理干净,并把返回值放在栈顶,具体请看ILRuntime实现原理文档
            ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;
            StackObject* ptr_of_this_method;
            //这个是最后方法返回后esp栈指针的值,应该返回清理完参数并指向返回值,这里是只需要返回清理完参数的值即可
            StackObject* __ret = ILIntepreter.Minus(__esp, 1);
            //取Log方法的参数,如果有两个参数的话,第一个参数是esp - 2,第二个参数是esp -1, 因为Mono的bug,直接-2值会错误,所以要调用ILIntepreter.Minus
            ptr_of_this_method = ILIntepreter.Minus(__esp, 1);
        
            //这里是将栈指针上的值转换成object,如果是基础类型可直接通过ptr->Value和ptr->ValueLow访问到值,具体请看ILRuntime实现原理文档
            object message = typeof(object).CheckCLRTypes(StackObject.ToObject(ptr_of_this_method, __domain, __mStack));
            //所有非基础类型都得调用Free来释放托管堆栈
            __intp.Free(ptr_of_this_method);
        
            //在真实调用Debug.Log前,我们先获取DLL内的堆栈
            var stacktrace = __domain.DebugService.GetStackTrance(__intp);
        
            //我们在输出信息后面加上DLL堆栈
            UnityEngine.Debug.Log(message + "\n" + stacktrace);
        
            return __ret;
        }
        
    • CLR绑定

      默认情况下,从热更DLL里调用Unity主工程的方法,是通过反射的方式调用的,这个过程中会产生GC Alloc,并且执行效率会偏低

      • 使用到的地方
        • 热更脚本调用主工程脚本
        • 但需要在主工程中提前做好相应工作
      • 注意事项
        • 一定要记得将CLR绑定的注册写在CLR重定向的注册后面,因为同一个方法只能被重定向一次,只有先注册的那个才能生效。
        • 可以选择性的对经常使用的CLR接口进行直接调用,从而尽可能的消除反射调用开销以及额外的GC Alloc
        • CLR绑定会生成较多C#代码,最终会增大包体和Native Code的内存耗用,所以只添加常用类型和频繁调用的接口即可
      1. 生成所需的绑定代码

      unity=》ILRuntime=》Generate…

      1. 添加到绑定列表
      public class CLRBindingTestClass
      {
        public static float DoSomeTest(int a, float b)
        {
          return a + b;
        }
      }
      

      ILRuntimeCLRBinding.cs=》class ILRuntimeCLRBinding=》GenerateCLRBinding添加添加types.Add(typeof(CLRBindingTestClass));

      1. 生成绑定代码
      • unity=》ILRuntime=》Generate CLR Binding Code
      • ILRuntime->Generate CLR Binding Code by Analysis是ILRT1.2版新加入的功能,可以根据热更DLL自生成绑定代码
      1. 注册
      • ILRuntime.Runtime.Generated.CLRBindings.Initialize(appdomain);
      1. 使用
       var type = appdomain.LoadedTypes["HotFix_Project.TestCLRBinding"];
       var m = type.GetMethod("RunTest", 0);
       appdomain.Invoke(m, null, null);
      
    • 协程Coroutine

      1. 注册
        //使用Couroutine时,C#编译器会自动生成一个实现了IEnumerator,IEnumerator<object>,IDisposable接口的类,因为这是跨域继承,所以需要写CrossBindAdapter(详细请看04_Inheritance教程),Demo已经直接写好,直接注册即可
        appdomain.RegisterCrossBindingAdaptor(new CoroutineAdapter());
      
      1. 适配方法
        CoroutineAdapter.cs
      appdomain.Invoke("HotFix_Project.TestCoroutine", "RunTest", null, null);
      
    • MonoBehaviour

      注意事项

      • 在热更DLL里面使用MonoBehaviour是可以做到的,但是并不推荐这么做
      • 缺什么补什么
      1. 热更脚本使用AddComponent

      重定向AddComponent

      unsafe void SetupCLRRedirection2()
      {
          //这里面的通常应该写在InitializeILRuntime,这里为了演示写这里
          var arr = typeof(GameObject).GetMethods();
          foreach (var i in arr)
          {
              if (i.Name == "GetComponent" && i.GetGenericArguments().Length == 1)
              {
                  appdomain.RegisterCLRMethodRedirection(i, GetComponent);
              }
          }
      }
      

      AddComponent方法

      unsafe static StackObject* AddComponent(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack, CLRMethod __method, bool isNewObj)
      {
          //CLR重定向的说明请看相关文档和教程,这里不多做解释
          ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;
      
          var ptr = __esp - 1;
          //成员方法的第一个参数为this
          GameObject instance = StackObject.ToObject(ptr, __domain, __mStack) as GameObject;
          if (instance == null)
              throw new System.NullReferenceException();
          __intp.Free(ptr);
      
          var genericArgument = __method.GenericArguments;
          //AddComponent应该有且只有1个泛型参数
          if (genericArgument != null && genericArgument.Length == 1)
          {
              var type = genericArgument[0];
              object res;
              if(type is CLRType)
              {
                  //Unity主工程的类不需要任何特殊处理,直接调用Unity接口
                  res = instance.AddComponent(type.TypeForCLR);
              }
              else
              {
                  //热更DLL内的类型比较麻烦。首先我们得自己手动创建实例
                  var ilInstance = new ILTypeInstance(type as ILType, false);//手动创建实例是因为默认方式会new MonoBehaviour,这在Unity里不允许
                  //接下来创建Adapter实例
                  var clrInstance = instance.AddComponent<MonoBehaviourAdapter.Adaptor>();
                  //unity创建的实例并没有热更DLL里面的实例,所以需要手动赋值
                  clrInstance.ILInstance = ilInstance;
                  clrInstance.AppDomain = __domain;
                  //这个实例默认创建的CLRInstance不是通过AddComponent出来的有效实例,所以得手动替换
                  ilInstance.CLRInstance = clrInstance;
      
                  res = clrInstance.ILInstance;//交给ILRuntime的实例应该为ILInstance
      
                  clrInstance.Awake();//因为Unity调用这个方法时还没准备好所以这里补调一次
              }
      
              return ILIntepreter.PushObject(ptr, __mStack, res);
          }
      
          return __esp;
      }
      
      1. 热更脚本使用GetComponent

      重定向GetComponent

      unsafe void SetupCLRRedirection2()
      {
          //这里面的通常应该写在InitializeILRuntime,这里为了演示写这里
          var arr = typeof(GameObject).GetMethods();
          foreach (var i in arr)
          {
              if (i.Name == "GetComponent" && i.GetGenericArguments().Length == 1)
              {
                  appdomain.RegisterCLRMethodRedirection(i, GetComponent);
              }
          }
      }
      

      GetComponent方法

      unsafe static StackObject* GetComponent(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack, CLRMethod __method, bool isNewObj)
      {
          //CLR重定向的说明请看相关文档和教程,这里不多做解释
          ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;
      
          var ptr = __esp - 1;
          //成员方法的第一个参数为this
          GameObject instance = StackObject.ToObject(ptr, __domain, __mStack) as GameObject;
          if (instance == null)
              throw new System.NullReferenceException();
          __intp.Free(ptr);
      
          var genericArgument = __method.GenericArguments;
          //AddComponent应该有且只有1个泛型参数
          if (genericArgument != null && genericArgument.Length == 1)
          {
              var type = genericArgument[0];
              object res = null;
              if (type is CLRType)
              {
                  //Unity主工程的类不需要任何特殊处理,直接调用Unity接口
                  res = instance.GetComponent(type.TypeForCLR);
              }
              else
              {
                  //因为所有DLL里面的MonoBehaviour实际都是这个Component,所以我们只能全取出来遍历查找
                  var clrInstances = instance.GetComponents<MonoBehaviourAdapter.Adaptor>();
                  for(int i = 0; i < clrInstances.Length; i++)
                  {
                      var clrInstance = clrInstances[i];
                      if (clrInstance.ILInstance != null)//ILInstance为null, 表示是无效的MonoBehaviour,要略过
                      {
                          if (clrInstance.ILInstance.Type == type)
                          {
                              res = clrInstance.ILInstance;//交给ILRuntime的实例应该为ILInstance
                              break;
                          }
                      }
                  }
              }
      
              return ILIntepreter.PushObject(ptr, __mStack, res);
          }
      
          return __esp;
      }
      
      1. 从主工程获取热更DLL的MonoBehaviour

      获取热更dll中的MonoBehaviour

      var type = appdomain.LoadedTypes["HotFix_Project.SomeMonoBehaviour2"] as ILType;
      var smb = GetComponent(type);
      var m = type.GetMethod("Test2");
      appdomain.Invoke(m, smb, null);
          ```
          >主工程工程定义GetComponent方法
          ```c#
      MonoBehaviourAdapter.Adaptor GetComponent(ILType type)
      {
          var arr = GetComponents<MonoBehaviourAdapter.Adaptor>();
          for(int i = 0; i < arr.Length; i++)
          {
              var instance = arr[i];
              if(instance.ILInstance != null && instance.ILInstance.Type == type)
              {
                  return instance;
              } 
          }
          return null;
      }
      
    • 反射Reflection(主工程中反射热更DLL中的类型)

        var it = appdomain.LoadedTypes["HotFix_Project.InstanceClass"];
        var type = it.ReflectionType;
        var ctor = type.GetConstructor(new System.Type[0]);
        var obj = ctor.Invoke(null);
      

      试一下用反射给字段赋值

      var fi = type.GetField("id", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
      fi.SetValue(obj, 111111);
      

      用反射调用属性检查刚刚的赋值

      var pi = type.GetProperty("ID");
      
    • LitJson(提供热更脚本中使用LitJson)

      在使用LitJson前,需要对LitJson进行注册,注册方法很简单,只需要在ILRuntime初始化阶段,在注册CLR绑定之前,执行下面这行代码即可:

      1. 注册
        LitJson.JsonMapper.RegisterILRuntimeCLRRedirection(appdomain);
      
      1. 使用

      LitJson的使用很简单,JsonMapper类里面提供了对象到Json以及Json到对象的转换方法,具体使用方法请看热更项目中的代码
      TestJson.cs

    • ValueTypeBinding(提供热更脚本使用)

      使用的原因

      1. Vector3等Unity常用值类型如果不做任何处理,在ILRuntime中使用会产生较多额外的CPU开销和GC Alloc
      2. 我们通过值类型绑定可以解决这个问题,只有Unity主工程的值类型才需要此处理,热更DLL内定义的值类型不需要任何处理
      • 注册方法
      appdomain.RegisterValueTypeBinder(typeof(Vector3), new Vector3Binder());
      appdomain.RegisterValueTypeBinder(typeof(Quaternion), new QuaternionBinder());
      appdomain.RegisterValueTypeBinder(typeof(Vector2), new Vector2Binder());
      

        


      
    #新知识

    1. unity测试代码运行的时间
      System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
      sw.Reset();
      sw.Start();
      sw.Stop();
      Debug.LogFormat("刚刚的方法执行了:{0} ms", sw.ElapsedMilliseconds);
    
    1. unity脚本代码性能(unity=》window=》profiler 不知道为什么我没有这个选项)
      Profiler.BeginSample("xxx");
      Profiler.EndSample();
    
    展开全文
  • ILRuntime Dotween

    2021-01-05 11:23:31
    ILRuntime Dotween 1; Unity 主程序导入dotween 2; 热跟程序引用dotween.dll 在unity 项目中找到dotween.dll 文件 我的dotween.dll 位置是 E:\ILRunTime\ILRuntimeProject\davidch-ILRuntimeProject-master\...
  • Unity ILRuntime框架设计

    2021-02-19 20:35:30
    本课程主要是针对ILRuntime设计一个独立的脚本热更新框,框架的优势: 1.将代码热更脱离Assetbundle资源热更,独立的部分更适用于各种不同的框架。 2.加快项目的逻辑更新,bug修复.(后期修bug,多数情况下并不用动到...
  • 从0开始学习ILRuntime到框架设计,解决所有ILRuntime使用过程的问题.
  • ilruntime-sample-源码

    2021-03-19 17:12:55
    ilruntime-sample
  • ILRuntime热更新DLl

    2018-12-21 15:08:52
    ILRuntime 实现热更新 ILRuntime 实现热更新 ILRuntime 实现热更新 这个积分都是谁改的呀 这个积分都是谁改的呀这个积分都是谁改的呀这个积分都是谁改的呀这个积分都是谁改的呀这个积分都是谁改的呀这个积分都是谁...
  • Unity热更新之ILRuntime

    2021-06-13 11:50:48
    ILRuntime项目为基于C#的平台(例如Unity)提供了一个纯C#实现,快速、方便且可靠的IL运行时,使得能够在不支持JIT的硬件环境(如iOS)能够实现代码的热更新 ILRuntime的优势同市面上的其他热更方案相比,ILRuntime...
  • ILRuntime无法断点调试

    2021-08-03 23:50:35
    ILRuntime无法断点调试
  • ILRuntime 是Unity热更新插件,支持 安卓,ios等热更,内有Demo示例
  • ILRuntime学习记录

    千次阅读 2018-07-25 10:04:52
    项目Github地址https://github.com/Ourpalm/ILRuntime Unity示例工程Github...在线文档https://ourpalm.github.io/ILRuntime/  最近按照主程要求,学习了ILRuntime这个u3d的热更,首先当然是找到QQ群组织:51207...
  • ILRuntime版本:1.6.6 接上文:基于ILRuntime和Addressable搭建热更框架记录 接入ILRuntime跑通之后现在我们需要热更工程中写代码了。但是我们在使用一些方法的时候没有生效并且报错。 这就需要参考一下对应的demo...
  • ET_GameFreamWork_ILRuntime三合一项目框架,项目包含一个搭好的GameFreamwork,ET,ILRuntime三合一的项目框架源码。启动ET服务端后,在Unity客户端按A键可以测试向服务端发消息。
  • ILRuntime 热更新

    2019-12-14 15:38:03
    ILRuntime是什么 经验汇总 主工程的代码,如果只是在ILRuntime热更工程中使用的话,在IL2CPP时,会被优化掉(认为没有被调用过),可以在相关代码上加上[Preverse]修饰来解决 打正式包时,在工程的宏定义里加入...
  • ILRuntime(一)

    千次阅读 2019-11-14 16:16:55
    之前简单的写了个ILRuntime和Unity互相调用的文章:...一些基本的概念在上面的文章讲过了,这边就懒得再说了,前期工作依旧是导入ILRuntime的库到Unity,和一个Hotf...
  • ILRuntime入门笔记

    2019-01-17 10:37:00
    基础知识 官方地址:...文档Markdown源文件:请阅读 ILRuntime\docs\source\src下的源文件 比这个目录下的源文件新 ILRuntime\Documents,经过对比两个目录有小部分差异。 官方Unity3D热更例...
  • ILRuntime中的反射 用C#开发项目,很多时候会需要使用反射来实现某些功能。但是在脚本中使用反射其实是一个非常困难的事情。因为这需要把ILRuntime中的类型转换成一个真实的C#运行时类型,并把它们映射起来 ...
  • ILRuntime中使用委托 如果只在热更新的DLL项目中使用的委托,是不需要任何额外操作的,就跟在通常的C#里那样使用即可 如果你需要将委托实例传给ILRuntime外部使用,那则根据情况,你需要额外添加适配器或者...
  • ILRuntime注意事项

    2021-04-13 15:15:57
    ILRuntime注意事项1. Const在IL反射读取失败 1. Const在IL反射读取失败 Public static class DynamicSystemEnum { public const string LBS_Scene = "Scene"; } 在IL中如果使用反射读取时,不能使用Const。必须...
  • ILRuntime Unity3D示例的热更新思路,用于线上游戏代码的热更。
  • ILRuntime Mac版本运行和iOS打包
  • Unity 接入 ILRuntime 热更方案

    千次阅读 2019-09-27 15:48:12
    ET 的热更使用的不是像 tolua 这样基于 Lua 的方案,而是基于 ILRuntime 的纯 C# 热更实现方案。 ILRuntime 的实现原理 对 Unity 引擎有一定了解的开发者都应该知道: Unity 支持使用 C# 做脚本语言,是依赖 ...
  • ILRuntimeU3D Unity3D demo project for ILRuntime 这个是ILRuntime的U3D示例工程

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 644
精华内容 257
关键字:

ilruntime