Unity 热更新方案之——ILRuntime

这篇具有很好参考价值的文章主要介绍了Unity 热更新方案之——ILRuntime。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

做游戏离不开热更新,目前市面上热更新方案用的比较多的是Lua(XLua,ToLua),最近又出现了基于C#的热更新 huatuo(已改名HybridCLR又叫wolong)。来不及学习了,以后用到了再去了解吧。
笔者入行做的第一个项目是利用ILRuntime进行热更新的,当时也是用的稀里糊涂的,一些坑点都是项目主程去解决的。这里做一个简单的回顾。


一、ILRuntime是什么?

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

2.实现原理
ILRuntime借助Mono.Cecil库来读取DLL的PE信息,以及当中类型的所有信息,最终得到方法的IL汇编码,然后通过内置的IL解译执行虚拟机来执行DLL中的代码来实现热更新功能。
查看ILRuntime源码你会发现,内部有一个很大的switch/case结构,就是针对基本上每一条IL指令码进行解释,同时维护一个Stackframe用于模拟cpu的函数调用的基本操作进行辅助解释。
ILRuntime中解释热更dll中的自定义类实例,在框架层这边都是对应的同一个warper,即ILTypeInstance。
ILTypeInstance会知道最终被调用方法的il指令内容,如果调用,则就是switch逐句去解析这个方法的IL代码。
这样一来就没有什么执行权限的问题,简单理解为读取一个普通文件,然后解析文件内容。
如果是反射处理这种情形,那就是真实的构建出一个新的类型,然后调用新类型的方法,这倒是会涉及到内存权限问题。

二、ILRuntime使用

当时使用的时候只记得有以下限制:
1.不能手动挂载热更Mono脚本,只能通过代码AddComponent
2.不能使用非System.Action/Fun类型的委托,需要手动注册委托类型转换
3.需要将类的成员初始化赋值删除,改为在方法内初始化
4.不允许使用ref和out
5.将热更脚本放在特定文件夹。通过定义宏,在日常开发中在Assembly-CSharp编译调用,打包时将热更脚本单独打DLL。

当时每天都在赶UI,没有去细究为什么这么做。
由于项目已经挂掉了,这里就不去纠结了。下面记录一下自己学习ILRuntime的历程。

1.跨域委托

只在热更新的DLL项目中使用的委托,是不需要任何额外操作的,就跟在通常的C#里那样使用即可。
如果你需要将委托实例传给ILRuntime外部使用,那则根据情况,你需要额外添加适配器或者转换器。

示例:同一个参数组合的委托,只需要注册一次即可

Action,以及Func委托需要在主工程注册适配器
// 无返回值委托
appDomain.DelegateManager.RegisterMethodDelegate<int, float>();
// 带返回值委托
appDomain.DelegateManager.RegisterFunctionDelegate<int, float, bool>();

自定义委托需要额外添加转换器DelegateConvertor
// 自定义委托
delegate bool SomeFunction(int a, float b);
app.DelegateManager.RegisterDelegateConvertor<SomeFunction>((action) =>
{
    return new SomeFunction((a, b) =>
    {
       return ((Func<int, float, bool>)action)(a, b);
    });
});

官方建议:
尽量避免不必要的跨域委托调用。
尽量使用Action以及Func这两个系统内置万用委托类型。

2.跨域继承

如果你想在热更DLL项目当中继承一个Unity主工程里的类,或者实现一个主工程里的接口,你需要在Unity主工程中实现一个继承适配器。
为什么需要适配器?
1)防止热更层用到的框架层代码被裁减。
为什么会被裁减呢?因为Unity打包的时候真的不把这个热更dll看做dll,因为这个热更dll是脱离unity框架层的。自然在unity打包的时候,为了包体大小会把认为没有使用的代码全部过滤掉。这种情况下ILRuntime解释执行的时候,去反射调用框架层代码就会被视为错误,因为框架层不存在这些被调用的代码。

因为脱离了关系,那么如何在框架层中驱动的时候,可以同步驱动到热更层,这就成了一个问题。这就需要框架层引用热更层的相关instance去驱动 ,那么如何引用?这就是适配器的作用。适配器工作在框架层,其显式强调了需要引用驱动的类型实例,然后重写相关函数体内容,去实质调用 热更类型实例 的方法。具体参考MonoBehaviourAdapter即可理解。

ILRuntime提供了一个代码生成工具来自动生成跨域继承的适配器代码。

示例:

    void OnHotFixLoaded()
    {
        Debug.Log("首先我们来创建热更里的类实例");
        TestClassBase obj;
        Debug.Log("现在我们来注册适配器, 该适配器由ILRuntime/Generate Cross Binding Adapter菜单命令自动生成");
        appdomain.RegisterCrossBindingAdaptor(new TestClassBaseAdapter());
        Debug.Log("现在再来尝试创建一个实例");
        obj = appdomain.Instantiate<TestClassBase>("HotFix_Project.TestInheritance");
        Debug.Log("现在来调用成员方法");
        obj.TestAbstract(123);
        obj.TestVirtual("Hello");
        obj.Value = 233;
        Debug.LogFormat("obj.Value={0}", obj.Value);


        Debug.Log("现在换个方式创建实例");
        obj = appdomain.Invoke("HotFix_Project.TestInheritance", "NewObject", null, null) as TestClassBase;
        obj.TestAbstract(456);
        obj.TestVirtual("Foobar");
        obj.Value = 2333333;
        Debug.LogFormat("obj.Value={0}", obj.Value);
    }

3.CLR绑定与重定向

为什么需要绑定与重定向机制?
1)防止热更层用到的框架层代码被裁减。
2)加速热更代码的执行。
加速热更代码执行其实是ILRuntime解释每条il指令的时候,都会去现有缓存中查找当前指令是否为重定向函数,如果为重定向函数,则直接调用,如果不是重定向函数,则会反射调用。通过反射来调用接口调用效率会比直接调用低很多,反射传递函数参数时需要使用object[]数组,这样不可避免的每次调用都会产生不少GC Alloc。众所周知GC Alloc高意味着在Unity中执行会存在较大的性能问题。

ILRuntime提供了一个代码生成工具来自动生成CLR绑定代码。

生成代码示例:

namespace ILRuntime.Runtime.Generated
{
    unsafe class HelloWorld_Binding
    {
        public static void Register(ILRuntime.Runtime.Enviorment.AppDomain app)
        {
            BindingFlags flag = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
            MethodBase method;
            Type[] args;
            Type type = typeof(global::HelloWorld);
            args = new Type[]{};
            method = type.GetMethod("TestHotfixInvokeMain", flag, null, args, null);
            app.RegisterCLRMethodRedirection(method, TestHotfixInvokeMain_0);

            args = new Type[]{};
            method = type.GetConstructor(flag, null, args, null);
            app.RegisterCLRMethodRedirection(method, Ctor_0);

        }

        static StackObject* TestHotfixInvokeMain_0(ILIntepreter __intp, StackObject* __esp, IList<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);
            global::HelloWorld instance_of_this_method = (global::HelloWorld)typeof(global::HelloWorld).CheckCLRTypes(StackObject.ToObject(ptr_of_this_method, __domain, __mStack), (CLR.Utils.Extensions.TypeFlags)0);
            __intp.Free(ptr_of_this_method);

            instance_of_this_method.TestHotfixInvokeMain();

            return __ret;
        }

        static StackObject* Ctor_0(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack, CLRMethod __method, bool isNewObj)
        {
            ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;
            StackObject* __ret = ILIntepreter.Minus(__esp, 0);

            var result_of_this_method = new global::HelloWorld();

            return ILIntepreter.PushObject(__ret, __mStack, result_of_this_method);
        }

    }
}


先学到这,持续更新中。。。

参考链接:
github仓库地址:https://github.com/Ourpalm/ILRuntime
中文文档:https://ourpalm.github.io/ILRuntime/public/v1/guide/index.html
使用ILRuntime遇到的一些问题
王王王渣渣ILRuntime系列文章来源地址https://www.toymoban.com/news/detail-404295.html

到了这里,关于Unity 热更新方案之——ILRuntime的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ILRuntime是如何与Unity互相调用的

    一、ILRuntime的基本介绍 ILRuntime是一个跨平台CLR实现,它可以在多个平台上运行C#代码,包括Android、iOS、Windows、Linux等等。ILRuntime的实现方式是将C#代码编译成IL代码,然后在运行时通过JIT或AOT的方式将IL代码转换为机器代码,从而实现跨平台的效果。ILRuntime的主要功能包括热更

    2024年02月16日
    浏览(40)
  • Unity游戏开发客户端面经——热更新(初级)

    前言:记录了总6w字的面经知识点,文章中的知识点若想深入了解,可以点击链接学习。由于文本太多,按类型分开。这一篇是 热更新 常问问题总结,有帮助的可以收藏。 1.1 为什么使用Lua作为热更新语言,不用C#         热更新本身对于资源热更新是非常容易的,Unit

    2023年04月20日
    浏览(33)
  • Unity 热更新方案和流程

    在开发商业游戏时,热更新是一个很重要的模块,这里讲的热更新不是指仅仅修复Bug,而是进行游戏功能的更新。简单来讲,就是启动游戏后,跑个条,下载资源和代码,然后再进入游戏。本篇博客所写的内容并不是最优的解,只是完成了热更新这个事情而已,具体使用还需

    2024年02月09日
    浏览(30)
  • Unity联网多人游戏技术方案调研

    Listen Server (Host) 和 Relay转发服务器游戏包同时包含客户端和服务端逻辑,联网时一个客户端开主,称为Host,其他客户端连入。局域网和互联网都支持。互联网需要有一个匹配服务器帮助找到不同人建立的主机。如果不使用Relay服务器,那对于互联网连接就要使用NAT穿透下的

    2023年04月09日
    浏览(68)
  • Unity3D 游戏服务器怎么实现热更新详解

    Unity3D是一款强大的游戏开发引擎,它不仅可以用于游戏客户端的开发,还可以用于游戏服务器的搭建。在游戏开发过程中,热更新是一项非常重要的功能,它可以使游戏在不重新启动的情况下,更新游戏内容,修复bug,提高游戏体验。本文将详细介绍Unity3D游戏服务器如何实

    2024年01月16日
    浏览(41)
  • 优化Unity日志系统的消耗及在ILRuntime模式下双击能跳转到对应的文件行号

    Unity的日志控制: 日志系统打开,但是只打印错误日志。这样其他级别的日志就不会有打印消耗。 但是还是有字符串拼接的消耗。 Conditional属性是一个C#特性,它允许你根据预处理器指令的定义来有条件地执行方法。例如下面的代码: 如果没有OPEN_MAIN_LOG_LOGWARNING宏,编译的时

    2024年02月11日
    浏览(24)
  • Unity 热更新技术 | (一) 热更新的基本概念原理及主流热更新方案介绍

    🎬 博客主页:https://xiaoy.blog.csdn.net 🎥 本文由 呆呆敲代码的小Y 原创,首发于 CSDN 🙉 🎄 学习专栏推荐:Unity系统学习专栏 🌲 游戏制作专栏推荐:游戏制作 🌲Unity实战100例专栏推荐:Unity 实战100例 教程 🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📆 未来很长

    2024年02月01日
    浏览(46)
  • [游戏开发]Unity颜色矫正无障碍方案

    之前有在关注色盲视觉纠正问题,最近在调整游戏的时候就打算把这个用上。 色弱色盲,这其实算是一种误称吧,只是人类中的少数派,只不过看到的颜色和大部分人不一样。下文用,视觉少数者,来称呼吧。 本质上是因为感知颜色的细胞发生突变,感知与大部分人有差异

    2024年02月15日
    浏览(31)
  • 【Unity 框架】QFramework v1.0 使用指南 工具篇:05. ResKit 资源管理&开发解决方案 | Unity 游戏框架 | Unity 游戏开发 | Unity 独立游戏

    Res Kit,是资源管理快速开发解决方案 特性如下: 可以使用一个 API 从 dataPath、Resources、StreammingAssetPath、PersistentDataPath、网络等地方加载资源。 基于引用计数,简化资源加载和卸载。 拥抱游戏开发流程中的不同阶段 开发阶段不用打 AB 直接从 dataPath 加载。 测试阶段支持只需打

    2024年02月01日
    浏览(44)
  • Photon Unity Networking 实时多人在线游戏开发解决方案

    作者:禅与计算机程序设计艺术 2019年,由英特尔、Facebook等公司联合举办的GDC大会上宣布了Unity Technologies将推出一个新品牌——Unity Game Development Platform(UGDP)。这个平台将包括对虚幻引擎4、Unreal Engine 4和原生Unity引擎的支持。在这个平台基础上,Unity Technologies推出了实时的多

    2024年02月09日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包