Unity热更新那些事

news2024/11/25 7:00:37

目录

  • 热更新方案
  • Unity程序的两种编译方式
    • 编译阶段
    • 执行阶段
      • Mono方式
      • IL2CPP方式
      • 两种方式打包以后的项目目录结构
    • 其他
  • ILRuntime热更新
    • ILRuntime使用注意
    • ILRuntime的实现原理
    • ILRuntime的性能优化建议
    • ILRuntime的性能优化建议
  • HybridCLR热更新

参考链接
Unity热更新那些事
一小时极速掌握ILRuntime热更新
一小时极速掌握HybridCLR热更新

热更新方案

在这里插入图片描述

Unity程序的两种编译方式

  • Mono方式
  • IL2Cpp方式

编译阶段

执行:源码 -> 四个项目 -> 动态链接库(dll文件) -> CIL(通用汇编语言)
顺序:firstpass,Editor-firstpass->Editor,CSharp

动态链接库对应
Assembly-CSharp自己写的C#程序代码脚本
Assembly-CSharp-Editor编辑器相关脚本(需要创建“Editor”文件夹)
Assembly-CSharp-Editor-firstpass编辑器插件
Assembly-CSharp-firstpass插件(需要创建“Plugins”文件夹)

在这里插入图片描述

执行阶段

基本概念

  • CLR:通用语言运行平台(Common Language Runtime),是微软的.Net虚拟机
  • 主要作用:
    • 编译 – 运行前把C#编译为CIL
    • 运行 – 在运行的时候把CIL转换为各平台的原生码(安卓:ARM指令集,windows:x86、x64指令集)

在这里插入图片描述

Mono方式

  • 一个基于CLR的开源项目,允许引擎和用户的托管代码运行在每一个目标平台上
  • Mono支持的平台:Anfroid,Apple IOS,Linux,Windows等

跨平台原理

  • 把C#通过Mono complier(其他语言用的是Unity单独开发的一个Unity complier),编译为CIL语言
  • 各个平台下的Mono虚拟机,运行CIL语言,转换成原生码给CPU执行

Mono虚拟机如何运行CIL

  • JIT(Just In TIme)模式 – 在编译的时候,把C#编译成CIL,在运行时逐条读入,逐条解析翻译成原生码交给CPU再执行;
  • AOT(Ahead Of Time)模式 – 在编译成CIL之后,会把CIL再处理一遍编译为原生码,运行时交给CPU直接执行,Mono下的AOT只会处理部分的CIL,还有一部分CIL采用了JIT模式;
  • Full AOT模式 – 在编译为CIL之后,把所有的CIL编译为原生码,在运行的时候直接执行(ios平台只能使用这种)

IL2CPP方式

IL2CPP会在项目转成CIL之后,再把CIL转为CPP,然后在运行的时候把CPP加载进来,由各个平台的IL2PP VM转换为原生码

IL2CPP工作原理
使用IL2CPP开始构建时,Unity会自动执行以下步骤:

  • 将Unity Scripting API代码编译为常规.NET DLL(托管程序集)
  • 应用托管字节码剥离(此步骤可显著减小构建的游戏大小)
  • 将所有托管程序集转换为标准C++代码
  • 使用本机平台编译器编译生成的C++代码和IL2CPP的运行时部分
  • 将代码链接到可执行文件或DLL,具体取决于目标平台
    在这里插入图片描述

IL2CPP方式脚本编译流程

  • IL2CPP做的改变由下图红色部分标明
  • 在得到中间语言IL后,使用IL2CPP将他们重新变回C++代码,然后再由各个平台的C++编译器直接编译成能执行的原生汇编代码
    在这里插入图片描述

IL2CPP的优缺点

  • 优点
    • 运行速度快(CPP转原生码比CIL快)
    • 减少Unity公司的维护成本(Mono VM官方不支持这么多平台,所以很多平台的Mono VM都需要Unity自己维护,而C++编译器是各个平台现成的)
  • 缺点
    • 包体会变大
    • 编译速度慢
    • 不支持JIT

两种方式打包以后的项目目录结构

在这里插入图片描述

其他

IOS平台热更的困境

  • Unity只有IL2CPP模式的才支持64位系统,Mono不支持
  • 苹果在2016年1月要求所有新上架游戏必须支持64位架构
  • IOS系统禁止动态加载代码到内存并执行
    在这里插入图片描述

总结:C#脚本限制

  • IOS系统禁止动态加载代码到内存,并执行
  • 反射:
    • System.Reflection可用(只要编译器可以推断通过反射使用的代码需要在运行时存在)
    • System.Reflection.Emit命名空间中的任何方法不可用
  • 序列化:
    • 如果一个类型或一个方法仅通过反射被创建或被调用,则AOT编译器无法检测到需要为该类型或方法生成代码
  • 泛型虚方法:
    • 泛型虚方法由于在编译时类型不确定,编译器也不会在编译期生成针对特定类型的泛型方法调用

解决方案

采用解释执行语言,而非编译执行

  • Lua:Tolua/Xlua
  • C#:ILRuntime

ILRuntime热更新

官方文档

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

ILRuntime使用注意

  • 跨域委托:需要额外添加适配器或者转换器
  • 跨域继承:如果想在热更DLL项目当中继承/实现一个Unity主工程里的类/接口,需要在Unity主工程中实现一个继承适配器,并注册
  • 反射转换:热更工程中的IL类型和C#类型系统不能混用,要类型映射后使用
  • CLR重定向
  • CLR绑定

ILRuntime的实现原理

  • ILRuntime借助Mono.Cecil库来读取DLL的PE信息,以及当中类型的所有信息,最终得到方法的IL汇编码,然后通过内置的IL解译执行虚拟机来执行DLL中的代码
  • 为了高性能进行运算,尤其是栈上的基础类型运算,如int,float,long之类类型的运算,直接借助C#的Stack类实现IL托管栈肯定是个非常糟糕的做法。因为这意味着每次读取和写入这些基础类型的值,都需要将他们进行装箱和拆箱操作,这个过程会非常耗时并且会产生巨量的GC Alloc,使得整个运行时执行效率非常低下
  • 因此ILRuntime使用unsafe代码以及非托管内存,实现了自己的IL托管栈。

ILRuntime的性能优化建议

  • Release模式下进行性能测试
  • 关闭Development Build选项来发布Unity项目
  • 避免GC:
    • ILRuntime跨域调用默认采用反射,这种方式少用,多用CLR绑定或基于InvocationContext的调用
    • 基于IL托管栈重新实现值类型的代码绑定(使用unsafe代码以及非托管内存)
    • 频繁调用的方法(例如Update方法)上避免使用params可变参数列表(会new数组出来)

ILRuntime的性能优化建议

  • 不依赖MonoBehaviour的代码框架
  • 自动化CLR绑定代码生成
  • 与Addressable资源管理和热更系统的结合

HybridCLR热更新

官方文档

  • HybridCLR是一个特性完整、零成本、高性能、低内存的近乎完美的Unity全平台原生c#热更方案。

  • HybridCLR扩充了il2cpp的代码,使它由纯AOT runtime变成AOT+Interpreter 混合runtime,进而原生支持动态加载assembly,使得基于il2cpp backend打包的游戏不仅能在Android平台,也能在IOS、Consoles等限制了JIT的平台上高效地以AOT+interpreter混合模式执行,从底层彻底支持了热更新。

  • HybridCLR不仅支持传统的全解释执行模式,还开创性地实现了 Differential Hybrid Execution(DHE) 差分混合执行技术。即可以对AOT dll任意增删改,会智能地让变化或者新增的类和函数以interpreter模式运行,但未改动的类和函数以AOT方式运行,让热更新的游戏逻辑的运行性能基本达到原生AOT的水平。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1179609.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关闭 win10自动更新、关闭激活

做工控或者数据大屏、服务器之类的,都不想系统自动更新,不知不觉自动重启类。 激活过了一段时间,又要重新激活这些事情,所以需要手工去关闭这些服务。 一、关闭 win10自动更新 首先打开 服务,找到windows update: window +R 输入:Services.msc 停止windows update ,然…

Python 海龟绘图基础教学教案(六)

Python 海龟绘图——第 9 题 题目:绘制下面的图形 解析: 综合应用,绘制长方形。 答案: 不使用循环。 Python 海龟绘图——第 10 题 题目:绘制下面的图形 解析: 综合命令使用。 答案: 使用循环…

MYSQL多表联查on和where的区别

目录 一、背景 二、探究 2.1、统计每个班级中女生的数量 错误的写法 查询结果 正确的写法 查询结果 2.2、只统计"一班"的学生数量 错误的写法 查询结果 正确的写法 查询结果 三、总结 一、背景 在一次对数据进行统计的时候,需要对两张表进行…

【ES分词】

分词 #测试分词器 POST /_analyze {"text": "小米手机和华为手机都是国产mobilephone", "analyzer": "english" }不管analyzer是改成:standard还是chinese都无法实现中文分词。 处理中文分词一般采用IK分词器 安装链接&…

Mongoose 开源库--json 使用笔记

一、 json相关API mongoose 开源库可以使用json进行数据处理。 ①创建json字符串 // A helper macro for printing JSON: mg_snprintf(buf, len, "%m", MG_ESC("hi")) #define MG_ESC(str) mg_print_esc, 0, (str) char *mg_mprintf(const char *fmt, ...)…

银行项目网上支付接口调用测试实例详解

前言 公司最近有一个网站商城项目要开始开发了,这几天老板和几个同事一起开着需求会议,讨论了接下来的业务规划和需求策略,等技术需求一下来还要讨论技术需求,确认后再慢慢的进入开发阶段,趁着闲暇时间想总结一下进入…

【0基础学Java第七课】-- 类和对象02

7.类和对象02 7.6 封装7.6.1 封装的概念7.6.2 访问限定符7.6.3封装扩展之包a.包的概念b. 导入包中的类c. 自定义包d 常见的包 7.7 static 成员7.7.2 静态修饰成员变量7.7.4 static修饰成员方法7.7.5 static成员变量初始化 7.8 代码块7.8.1 代码块概念以及分类7.8.2 普通代码块7…

Linux个性化登录提示信息

在Linux系统中,您可以为每个用户登录后显示个性化的提示信息。这通常通过修改用户的shell配置文件来实现,这个文件通常是用户的.bashrc或.bash_profile文件。以下是一些示例步骤,来实现这个目标: 打开终端并登录到Linux系统。 使…

AI:64-基于深度学习的口罩佩戴检测

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

“2024上海智博会”为我国智能科技产业发展注入新的动力

作为开年智能科技第一展“2024上海智博会”将于2024年三月份在国际大都市上海隆重召开,本届展会将充分展示我国智能科技产业新技术、新产品及未来发展方向。 据悉,本届展会将汇聚全球智能科技产业的领军企业和人物,共同探讨和分享最新的技术趋…

Mysql8与mariadb的安装与常用设置

一、v10服务器mariadb的安装与常用设置 V10服务器默认安装了mariadb数据库。也可使用命令sudo yum install mariadb手动安装或升级默认安装的版本。 1.1 修改数据库密码 systemctl restart mariadb,重启mariadb服务;mysql -u root -p,要求输入密码直接回车&#…

贝叶斯建模:从先验合理性到后验分布

一、说明 本文探讨贝叶斯模型,首先用摸球游戏展开模型构建步骤,然后讨论分类算法,以及实际操作方法:网格法、二次近似、蒙特卡洛。 二、针对贝叶斯的模型构建 2.1 分支剪枝和假设 在贝叶斯分析中,我们可以将这个过程想…

【C++】多文件的代码规范

算是C嘎嘎入门教程(但至少需要知道HelloWorld怎么写 内容不能说全,因为是想到哪写到哪,再次就是C是真的很杂。 (写完后博文编辑器提示我,本文章可能要20分钟读完,做好心理准备… 1、头文件(*.h)、源文件(*.cpp) 头文件…

bin.zip和bin.tar.gz以及src.zip和src.tar.gz以及rpm和dmg的区别

下载JDK时Java Downloads | Oracle会有很多文件,时间长了容易混淆,在此记录一下。 如上面三张图所示: bin代表二进制文件,是编译后的文件,而src是源码。.tar.gz是linux的压缩包,.zip是windows的压缩包 所以: bin.ta…

11.6哈夫曼树

创建哈夫曼树 经过这一步后,树的集合里就有n个叶子结点 不断从树集合里取出两个权重最小的树合并成一个新树,这时候就是两个根节点并成兄弟到一个新的根节点下,这个新的根节点的权重是两个兄弟的权重和,之后再把 每次合并的时…

32岁华为员工合同不续约,赔偿N+1,额外3个月年终奖,加班费10万+,总共25万多...

各位中生代社区的读者大大们好,我是你们的老朋友大白:) 一直以来,IT行业都有着“程序员是吃青春饭”的说法,这一年龄危机甚至逐渐演变为“45岁退休,35岁换人”的段子。 作为国内知名互联网大厂,华为近几年就曾几次三番…

Bash脚本实现Linux开机自启Redis,Nginx,MySQL等服务

一. MySQL服务自启 在CentOS 7及以上版本,MySQL以服务形式进行启动,运行两行命令即可实现。 systemctl start mysqld systemctl enable mysqld 只需运行一次即可实现MySQL开机自启动 二. 编写bash脚本 使用bash命令编写脚本实现 #!/bin/bashfunction …

linux下实现电脑开机后软件自启动

实现linux的软件自启动,需要四个文件 第一个【displayScreen.desktop】文件,.desktop文件就是一个用来运行程序的快捷方式,也叫启动器,常用来自启动用的文件,内容如下 [Desktop Entry] #要执行的脚本位置 Exec/home/yicaobao/te…

19.7 Boost Asio 传输序列化数据

序列化和反序列化是指将数据结构或对象转换为一组字节,以便在需要时可以将其存储在磁盘上或通过网络传输,并且可以在需要时重新创建原始对象或数据结构。 序列化是将内存中的对象转换为字节的过程。在序列化期间,对象的状态被编码为一组字节…

串台 fit4life | 催吐辟谷减肥药,你没看错这就是我们仨的黑历史!

点击文末“阅读原文”即可参与节目互动 剪辑、音频 / 卷圈 运营 / SandLiu 卷圈 监制 / 姝琦 封面 / 姝琦Midjourney 产品统筹 / bobo 场地支持 / 声湃轩北京录音间 运动博主/食品博士/营养师居然也都曾有程度不同的进食障碍?! 是,这没…