Unity热更新那些事

目录

  • 热更新方案
  • 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的水平。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【算法与数据结构】216、LeetCode组合总和 III

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析:本题可以直接利用77题的代码【算法与数据结构】77、LeetCode组合,稍作修改即可使用。   …

实验5-2——网络yum源的配置

网络yum源的配置 实验步骤: 1.在/etc/yum.repos.d中新建一个文件夹bak备份原来的东西,查看/etc/yum.repos.d/内容 cd /etc/yum.repos.d mkdir bak ls 2.把/etc/yum.repos.d中已有的repo文件都移入bak文件夹中并查看 mv *.repo bak ls 3. 下载安装weget以防万一本…

C语言 每日一题 11.9 day15

数组元素循环右移问题 一个数组A中存有N( > 0)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(≥0)个位置,即将A中的数据由(A0​A1⋯AN−1)变换为&…

leetCode 206.反转链表 图解

206. 反转链表 - 力扣(LeetCode) 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表 class Solution { public:ListNode* reverseList(ListNode* head) {ListNode* s NULL;ListNode* phead;while(p) {headhead->nex…

SQL第三次上机作业

1.查询与王利就读同一专业学生的借书证号和姓名 USE TSGL GO SELECT Lno,Rname FROM Reader WHERE Dept(SELECT DeptFROM ReaderWHERE Rname王利) and Rname ! 王利2.查询比希望出版社出版的所有图书价格都高的图书信息 SELECT * FROM Book WHERE Price>(SELECT MAX(Price…

MSQL系列(十四) Mysql实战-SQL语句 left join inner join On和Where语句的区别

Mysql实战-SQL语句On和Where语句的区别 前面我们讲解了Join的底层驱动表 选择原理,也知道了基本的内连接外连接两种SQL查询表连接方式 但是我们再查询多表的时候on和where语句到底有什么区别? where是过滤条件 ,不满足where的一定不会出现在结果中on是连接条件, …

SPASS-描述性分析

将身高移入变量 结果展示: 表中分析变量“身高”的个案数、所有个案中的极大值、极小值、均值、标准差及偏度和峰度

【React】04.MVC模式和MVVM模式

React是Web前端框架 1、目前市面上比较主流的前端框架 ReactAngular(NG框架)Vue 主流的思想: 不在直接去操作DOM,而是改为“数据驱动思想” 操作DOM思想: 操作DOM比较消耗性能[主要原因就是,可能会导…

【C/PTA——7.数组1】

C/PTA——7.数组1 7-1 计算最大值出现的次数1.题目要求2.代码实现 7-2 求一批整数中出现最多的个位数字1.题目要求2.代码实现 7-3 装箱问题1.题目要求2.代码实现 7-4 数组-值钱的微信号1.题目要求2.代码实现 7-5 数组-吹泡泡1.题目要求2.代码实现 7-6 数组-数学鬼才1.题目要求2…

JavaWeb Day05 前后端请求响应与分层解耦

目录 一、请求与响应 (一)请求的参数接收 ①数组参数 ②集合参数 ③日期参数 ④json参数 ⑤路径参数 总结 (二)响应 ①简单文本text ②数组 ③列表 ④同一响应数据格式 ⑤总结 二、三层架构与分层解耦 &#xff0…

前端特殊字符转码

前端特殊字符转码 建议 最好不要传名称,传ID 是在不行就用这个方法 name encodeURIComponent(name),

医院检验信息管理系统源码 医院LIS系统源码 云LIS源码 区域LIS源码

医院检验信息管理系统源码 医院LIS系统源码 云LIS源码 区域LIS源码 医院检验信息管理系统,利用计算机网络技术、数据存储技术、快速处理技术,对检验科进行全方位信息化管理,使检验科达到自动化运行,信息化管理和无纸化办公的目的…

【C++】万字详解IO流(输入输出流+文件流+字符串流)

文章目录 一、标准输入输出流1.1提取符>>&#xff08;赋值给&#xff09;与插入符<<&#xff08;输出到&#xff09;理解cin >> a理解ifstream&#xff08;读&#xff09; >> a例子 1.2get系列函数get与getline函数细小但又重要的区别 1.3获取状态信息…

矩阵键盘独立接口设计(Keil+Proteus)

前言 实验&#xff1a;通过4*4的矩阵键盘&#xff0c;按下某个按钮之后会在数码管上面显示对应的键号。&#xff08;0~F&#xff09; 基础操作参考这篇博客&#xff1a; LED数码管的静态显示与动态显示&#xff08;KeilProteus&#xff09;-CSDN博客https://blog.csdn.net/w…

13 # 手写 concat 方法

concat 的使用 concat() 方法用于合并两个或多个数组。此方法不会更改现有数组&#xff0c;而是返回一个新数组。如果省略了所有参数&#xff0c;则 concat 会返回调用此方法的现存数组的一个浅拷贝。 <script>var arr1 ["k", "a", "i"…

Python tkinter库的Menu组件实现菜单栏、一级菜单、二级菜、三级菜单

在Python的Tkinter中&#xff0c;要显示菜单栏、一级菜单、二级菜、三级菜单&#xff0c;可以使用add_cascade方法将下一级菜单添加到上一级菜单中。 运行结果 下面是一个简单的示例&#xff1a; import tkinter as tkroot tk.Tk()# 创建菜单栏 menubar tk.Menu(root) root…

官方Redis视图化工具Redisinsight

一、下载最新版本的 docker pull redislabs/redisinsight mkdir /data/redisinsight docker run -d -u root -p 8001:8001 -v /etc/localtime:/etc/localtime -v /data/redisinsight:/db --restartunless-stopped redislabs/redisinsight:latest 二、浏览器打开 http://192…

C#解析XML并反序列化为Model的方法

虽然现在json大行其道&#xff0c;但是xml格式依旧占据着广阔的编程世界&#xff0c;不管光伏锂电激光卫星汽车等等工业领域&#xff0c;基本上都是以xml为主&#xff0c;广大的.NET开发人员有很多被xml折磨的都要转java了&#xff0c;这篇小作文就来玩一种迅速完成xml到model的…

DuiLib中常用各种RGB颜色对照表

【常识】常用RGB颜色对照表] 颜色样式***RGB*数值颜色代码颜色样式***RGB*数值颜色代码黑色0,0,0#000000白色255,255,255#FFFFFF象牙黑88,87,86#666666天蓝灰202,235,216#F0FFFF冷灰128,138,135#808A87灰色192,192,192#CCCCCC暖灰128,118,105#808069象牙灰251,255,242#FAFFF0石…