UnityC#脚本的热更新原理

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

Unity的脚本如何跨平台

想要了解Unity的热更原理,必须要先了解Unity脚本的编译和跨平台机制。通常游戏的跨平台主要指安卓和IOS端。Unity的官方脚本语言是C#,但也有不少项目会采用C# + Lua语言的方式进行开发。它们主要有三种跨平台的形式:JIT、AOT、脚本语言。
Unity的C#代码在代码被打包时会被编译器变为成为中间语言IL(Intermediate Language),而不是机器码(NativeCode,机器的可执行代码)。后续对这些IL的编译方式不同可以分为AOT和JIT。

JIT(Just In Time)

JIT是一种动态编译技术,是指Unity打包时将C#编译成IL后,在运行时.NET JIT编译器将IL翻译NativeCode的过程。通常IL由Mono VM编译执行,这是因为Mono VM中包含了.NET JIT编译器。
下面是MonoVM的运行流程图,它就在运行时将IL编译成机器码并保存到内存中并执行。

AOT(Ahead Of Time)

指在程序程序运行前将代码变成称机器码,它不需要再运行时对代码进行解释和编译,这样可以提高程序的执行速度和安全性。Unity AOT的跨平台原理是将程序的C#源代码在打包时编译成与平台无关的IL然后通过特定的编译器将IL代码编译成特定平台的Native Code。不同平台只需要提供对应的编译器即可,无需在运行时对代码进行解释和编译,从而实现跨平台。
下面是Unity推荐的IL2CPP编译器原理图。在打包时把C#代码先编译成IL,再由IL2CPP编译器编译成C++代码,再由特定平台的C++编译器编译成NativeCode。
最后需要L2CPP VM的原因是虽然代码转换成了C++代码,但C#中的内存是由GC自动管理,而C++需要手动管理内存,因此还需要一个IL2CPP VM用于GC管理等操作。

脚本语言

Lua是一种跨平台的脚本语言,它主要依赖解释器和虚拟机实现跨平台功能。正常的lua脚本跨平台流程是:

  1. 编写lua脚本
  2. 使用lua解释器将lua脚本解释称字节码
  3. 由lua虚拟机执行字节码

由于解释器和虚拟机都是跨平台的,lua脚本也就可以在不同的平台上运行了。

字节码(bytecode)指的是一种中间码,它是一种介于源代码和机器码之间的一种代码形式。字节码是针对特定虚拟机(如Java虚拟机、.NET CLR虚拟机)的指令集,每条指令都比较简单,并且都能够被轻易地转换成机器码。字节码通常是在解释执行或者即时编译的过程中生成的,可以有效地提高程序的执行效率和跨平台能力。
在编译型语言中,源代码会被直接编译成机器码,而在解释型语言中,源代码则会被解释器逐行执行。相比之下,字节码的执行效率通常比解释器高,但比直接执行机器码要低。但是,字节码的好处在于它可以在多个平台上运行,只需要在特定平台上实现一个对应的解释器或者即时编译器即可。这也是为什么很多跨平台的语言(如Java和Lua)都采用了字节码的形式。
举个例子,Java源代码在编译时会被转换成Java字节码,然后在JVM上解释执行或者即时编译成机器码。这样,Java程序就可以在不同的操作系统和硬件上运行,只需要在不同平台上实现对应的JVM即可。

另外,lua也提供了JIT版本,以便在运行时将lua代码编译成NativeCode执行,与普通的lua解释器相比,可以显著地提升lua代码的执行速度。

C#如何热更新

Lua脚本语言是解释执行的,运行前加载更新后的代码就可以达到热更新的效果。在本文中要说明的是C#的热更新。
理想化的热更新流程是:

  1. 把需要更新的代码编译成动态链接库
  2. 游戏启动时加载新的动态链接库
  3. 用反射的形式获取动态链接库中的实例或方法

这种模式在PC和Android平台是可以的,但在IOS平台是不可行的。因为IOS对申请的内存禁止了可执行权限,所以运行时创建/加载的NativeCode是无法执行的。
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offsize)函数是申请内存的函数,它的第三个参数是代表了申请内存的保护方式:

  • PROT_EXEC 映射区域可被执行
  • PROT_READ 映射区域可被读取
  • PROT_WRITE 映射区域可被写入

而IOS平台是不支持PROT_EXEC的。运行时申请的内存不可执行,所以这种理想化的热更新流程无法在IOS平台起作用。
详细原因可以看下这篇文章。
为了解决IOS上的热更新问题,有两个主流方案:ILRuntime 和 HybridCLR。

ILRuntime

Unity会把C#代码打包成DLL,ILRuntime在运行时用自己的解释器来解释IL并执行,而不是直接调用.NET FrameWork或Mono虚拟机来运行代码。它借助Mono.Cecil库来读取DLL的PE信息,以及当中类型的所有信息,最终得到方法的IL汇编码,然后通过内置的IL解译执行虚拟机来执行DLL中的代码。

但是ILRuntime会有一些限制,见https://ourpalm.github.io/ILRuntime/public/v1/guide/FastQA.html。

  1. ILRuntime和原始的 compiler是两套东西,也就是说你的热更DLL和主工程的DLL实质是不互通的(如热更DLL中一个类要继承主工程DLL的一个类),所以就存在跨域问题,需要写委托适配器,委托转换器。在发布版本后这些不能热更,使用之前一定要预留好可能会使用的
  2. 部分 C# 语法不支持:由于 ILRuntime 是基于 Mono 实现的,而 Mono 不支持所有 C# 语法,所以 ILRuntime 在某些 C# 语法方面也有限制,比如属性、泛型委托、可选参数等
  3. 需要特殊处理的代码:由于 ILRuntime 的实现方式,一些特殊的代码需要进行特殊处理,比如反射、LINQ、协程等
  4. 性能问题:由于 ILRuntime 需要动态解析和执行代码,相对于编译时静态绑定的方式,其性能会有一定程度的下降。同时,在使用过程中也需要注意避免频繁的跨域调用和反射操作,以免影响性能
  5. ILRuntime对多线程Thread不兼容,在热更代码里使用多线程会导致Unity崩溃闪退

HybridCLR

是一个特性完整、零成本、高性能、低内存近乎完美的Unity全平台原生c#热更方案。
IL2CPP是一个纯静态的AOT运行时,不支持运行时加载dll,因此不支持热更新。HybridCLR扩充了IL2CPP的代码,使其由纯AOT Runtime变成“AOT+Interpreter”混合Runtime,进而原生支持动态加载Assembly,使得基于IL2CPP打包的游戏不仅能在Android平台,也能在IOS、Consoles等限制了JIT的平台上高效地以AOT+interpreter混合模式执行。

ILRuntime是引入一个第三方VM(Virtual Machine),在VM中解释执行代码,来实现热更新。这些热更新方案的VM与IL2CPP是独立的,意味着它们的元数据是不相通的,在热更新里新增一个类型是无法被IL2CPP所识别的例如通过System.Activator.CreateInstance是不可能创建出这个热更新类型的实例),这种看起来像、实际上却又不是的伪CLR虚拟机,在与IL2CPP这种复杂的CLR运行时交互时,产生极大量的兼容性问题,另外还有严重的性能问题。
HybridCLR对IL2CPP运行时进行扩充,添加interpreter模块,进而实现像Mono一样的混合执行模式。这样一来就能彻底支持热更新了,并且兼容性极佳。对开发者来说,除了以解释模式运行的部分执行得比较慢,其他方面跟标准的运行时没有区别。
通俗地说,il2cpp相当于Mono的AOT模块,HybridCLR相当于Mono的interpreter模块,两者合一成为完整Mono。HybridCLR使得IL2CPP变成一个全功能的Runtime,原生(即通过System.Reflection.Assembly.Load)支持动态加载dll,从而支持ios平台的热更新。

正因为HybridCLR是原生Runtime级别实现,热更新部分的类型与主工程AOT部分类型是完全等价并且无缝统一的。可以随意调用、继承、反射、多线程,不需要生成代码或者写适配器。而其他热更新方案则是独立VM,与IL2CPP的关系本质上相当于Mono中嵌入lua的关系。因此类型系统不统一,为了让热更新类型能够继承AOT部分类型,需要写适配器,并且解释器中的类型不能为主工程的类型系统所识别。特性不完整、开发麻烦、运行效率低下。

HybirdCLR的原理细节Walon有分享,可以在其知乎专栏查看。文章来源地址https://www.toymoban.com/news/detail-482181.html

参考

  • ILRuntime中文官网,https://ourpalm.github.io/ILRuntime/public/v1/guide/principle.html
  • 曾志伟, 【Unity游戏开发】Mono和IL2CPP的区别, https://zhuanlan.zhihu.com/p/352463394
  • HybridCLR介绍,walon,https://www.zhihu.com/question/519548488

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

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

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

相关文章

  • Unity跨平台开发指南(PC/VR/Android/WebGL)

    通常我在进行不同平台的设置时会基于以下几点: 1:创建、开发、打包时我们通常针对Player和Quality设置进行质量的设定 2:在不同平台上运行时,有不同的平台包体大小,加载方式的限定,测试、打包上的区别,帧率稳定60 3:代码封装上的区别,特别针对单一项目转为不同

    2024年01月21日
    浏览(59)
  • 跨平台应用开发进阶(三十二) :AK/SK鉴权原理简介

    ak/sk 是一种身份认证方式,常用于系统间接口调用时的身份验证,其中 ak 为 Access Key ID , sk 为 Secret Access Key 。客户端和服务端两者会协商保存一份相同的 sk ,其中 sk 必须保密。 AK : Access Key Id ,⽤于标⽰⽤户; SK : Secret Access Key ,是⽤户⽤于加密认证字符串和⽤来验证认证

    2024年02月13日
    浏览(53)
  • webpack 的热更新及其原理

    Webpack 的热更新(Hot Module Replacement,简称HMR)是一种开发时提供实时更新的功能,它使得在修改代码后,不需要完全刷新页面就能立即看到更新的效果。 HMR 的原理涉及以下几个主要步骤: 启动时建立 WebSocket 连接:在项目启动时,Webpack 会创建与开发服务器的WebSocket连接,用

    2024年02月13日
    浏览(37)
  • Unity跨平台UI解决方案:可能是最全的FairyGUI系列教程

    FairyGUI的项目文件结构 .objs 内部数据目录。注意:不要加入版本管理,因为这里的内容是不需要共享的。 assets 包内容放置目录,资源内容都在这里面,里面还可以分不同的包,便于区分管理(看下图) settings 配置文件放置目录。 ****.fairy 项目识别文件,也就是项目名称 目录

    2024年04月14日
    浏览(50)
  • Docker如何跨平台下载Arm架构的镜像

    Docker是一个开源的容器化平台,它可以让开发者打包应用程序及其依赖项到一个容器中,并在任何地方运行这个容器,而不用担心环境配置的问题。类比一下,就好像打包一个包裹,里面包含了应用程序和它需要的各种工具、库等等,这个包裹可以在不同的地方被运输和打开

    2024年01月17日
    浏览(46)
  • 组件化、跨平台…未来前端框架将如何演进?

    前端框架在过去几年间取得了显著的进步和演进。前端框架也将继续不断地演化,以满足日益复杂的业务需求和用户体验要求。从全球web发展角度看,框架竞争已经从第一阶段的前端框架之争(比如Vue、React、Angular等),过渡到第二阶段的框架之争(比如Next、Nuxt、Remix、小程

    2024年02月14日
    浏览(58)
  • 【前端工程化面试题目】webpack 的热更新原理

    可以在顺便学习一下 vite 的热更新原理,请参考这篇文章。 首先有几个知识点需要明确 热更新是针对开发过程中的开发服务器的,也就是 webpack-dev-server webpack 的热更新不需要额外的插件,但是需要在配置文件中 devServer 属性中配置 hot: true,需要安装 webpack-dev-server 这个

    2024年02月19日
    浏览(62)
  • 语义网与云计算:如何实现跨平台的数据共享和协同

    语义网和云计算是当今最热门的技术趋势之一,它们为人工智能、大数据分析和实时数据处理提供了强大的支持。在这篇文章中,我们将深入探讨语义网和云计算的核心概念、算法原理、实例代码和未来发展趋势。 语义网是一种基于语义技术的网络,它旨在解决信息的语义差

    2024年04月15日
    浏览(43)
  • Unity下实现跨平台的RTMP推流|轻量级RTSP服务|RTMP播放|RTSP播放低延迟解决方案

    2018年,我们开始在原生RTSP|RTMP直播播放器的基础上,对接了Unity环境下的低延迟播放,毫秒级延迟,发布后,就得到了业内一致的认可。然后我们覆盖了Windows、Android、iOS、Linux的RTMP推送、轻量级RTSP服务和RTSP|RTMP播放。 目前看,Unity环境下,我们在行业内的延迟几乎是最低的

    2024年01月22日
    浏览(49)
  • 【Flutter跨平台插件开发】如何实现kotlin跟C++的相互调用

    在 Kotlin 中,可以使用 JNI (Java Native Interface) 来调用 C++ 代码 调用步骤: 创建 C++ 文件并实现函数。 在 Kotlin 中声明需要调用的 native 函数并加载 native 库。 调用示例 Flutter 插件项目的例子 在 Flutter 插件中引用已有的 C++ 源码需要以下步骤: 首先,在 Flutter 插件的 android 目录下

    2024年01月25日
    浏览(67)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包