Unity热更新知识点详解及常用解决方案原理介绍

热更新知识点汇总

热更新的概念

游戏或者软件更新时,无需重新下载客户端进行安装,而是在应用程序启动的情况下,

在内部进行的资源或者代码更新。

热更新的优点

  1. 迅速修复Bug – 避免重新下载安装包,游戏内部及时更新Bug
  2. 减小安装包的体积 – 非核心资源上传服务器,运行时动态加载剩余资源
  3. 迅速更换游戏"内核" – 狸猫换太子,绕过审核,迅速对游戏进行更新

热更新解决方案

  • 基于Lua的热更新解决方案

    • 原理:本质上就是利用相关插件(xlua,tolua)提供一个Lua的运行环境(虚拟机),为Unity提供Lua编程的能力,让C#和Lua可以相互调用和访问。

    • 优点

      1. Lua由C语言编写通用性强几乎在任何操作系统均可运行。
      2. Lua作为轻量小巧的脚本语言,由Lua虚拟机解释执行,热更只需进行简单的文件替换,无需进行编译
      3. 开发经验浓厚,有很多成熟的项目和方案。
    • 缺点

      1. Unity原生使用C#,多使用一门Lua语言会增加学习和开发成本
      2. Lua是弱类型非面向对象的语言,面对较大的项目时将无法像传统OOP语言一样分层和模块化开发等,容易造成代码结构混乱和维护困难等问题。
      3. 需要利用插件在C#环境下提供Lua运行环境,并且还需要为Lua和C#之间提供数据的通信和相关转换,效率低下,远不如用原生C#进行开发。
    • 解决方案

      • XLua

        • 基本原理XLua框架的基础是实现了运行在C#环境上的Lua虚拟机使得Lua可以和C#实现相互调用和访问。

        • C#调用Lua原理:Lua由C语言实现自带C/C++的通信机制接口,C#可以借助C与Lua进行数据通信,在内存上进行数据交换。从内存中直接获取到的数据引用还需进一步转换成C#的相关类型才能使用,xLuacastersMap中定义了lua数据类型到C#类型的转换函数,转换后即可实现C#调用Lua。

        • Lua调用C#原理:数据通信原理同上,简单基本值类型通过C API可轻松实现传递,但复杂的对象则需要通过Lua的userdata表和C#对象的映射实现,每个C#对象均在Lua中对应一个userdata表,然后lua会为userdata设置元表,元表中表示的是实际对象的类型信息,在C#对象传递到Lua后还需告知诸如对象的类型,静态/成员方法、属性等信息注册到Lua后,Lua才能正确的调用C#相关对象函数等。

          元表在此可以理解为是C#中的Type类,所有C#对象均有一个类型对象指针,类型对象中存有方法表,静态字段等信息,userdata表只存有类中具体的成员,还需要元表充当类型对象才能正确的和C#对象映射成功。

        • 热补丁原理:利用IL注入,实现用Lua函数替换C#原函数。实现的基本原理为:根据Lua脚本热补丁命令生成匹配文件DelegatesGensBridge.cs)里面存有Lua提供的热补丁函数引用,再根据特性的标注[Hotfix]等,定位到目标函数上创建静态变量DelegateBridge。当Lua为其提供了热补丁函数,则此变量不为空否则为空,再利用此变量进行条件判断,实现不为空时根据匹配文件中Lua函数引用去执行,不继续向下执行原C#函数。若为空则执行原C#函数逻辑。

      • ToLua

        • 基本原理:ToLua框架主要是通过静态绑定来实现C#与Lua之间的交互的。
        • C#调用Lua原理:数据通信和xLua类似,获取内存中数据引用后也需要进行转换,转换方式和XLua有所区别,ToLua实现了LuaInterface建立了Lua与C#之间的映射,使用C#的类型建立了lua的基础数据模型,然后在这个基础上建立了运算规则。
        • Lua调用C#原理:与其它框架不同的正是其静态绑定的特点,在Lua调用C#时,其提供了一个C#文件(CustomSettings.cs)显示注册C#组件,启动虚拟机时会利用LuaInterface将注册了的C#组件映射到Lua上,包括基础类型的转换、自定义类型到table的转换、函数的转换,最终均注册到Lua的大G表(global table)中实现Lua调用C#。
      • 两种Lua解决方案的比较

        1. ToLua提供的暖更新不完善,C#项目转Lua做热更新建议用XLua的热补丁。
        2. ToLua采用的是静态绑定在文件中显示注册的方式减少了反射次数,XLua则是提供了一系列特性也一定程度上减少可反射次数。
        3. ToLua对泛型的支持糟糕,会有大量的拆装箱过程,效率较低。XLua完善了对泛型的支持,减少了拆装箱的次数。
  • 基于C#的热更新解决方案

    • 原理:本质上用编译好的新DLL替换需要更新的旧DLL。只能进行代码热更新,还需配合AB包的资源热更新一起使用。
    • 优点
      1. 和Unity开发均使用C#,开发语言统一,编码更容易。
      2. 使用纯C#开发无需另创虚拟机等环境,效率高,性能远高于Lua
    • 缺点
      1. 方案均有各自的局限性,直接反射方式不仅损耗性能,在不支持JIT的系统(IOS)上无法使用。
      2. ILRuntime解决了JIT的问题但不够成熟,使用问题较多,需要较高的动手解决问题能力。
    • 解决方案
      • DLL反射热更新: 使用编译后的DLL文件进行替换,利用反射方式把所需C#组件绑定到相应的对象上使用。
      • ILRuntime:本质还是DLL的替换,但实现了一个ILR(IL运行时)使其能够在不支持JIT的硬件环境(IOS)下实现代码热更新。

热更新基本流程

  1. 版本号的比较,如果版本号不同才继续以下流程(version.txt记录版本号,可选)
  2. 下载资源服务器上的对比文件(files.txt记录所有文件md5码,类似于目录)
  3. 确定下载列表,将最新下载的对比文件和本地旧对比文件对比,记录缺少或不同的文件。(利用files.txt中的md5码实现此步骤)
  4. 根据下载列表,下载所需的资源。(一般放在Application.persistentDataPath)
  5. 解压(如果文件压缩过需要先解压,可选)
  6. 保证下载成功后,用最新的对比文件覆盖本地的对比文件(更新目录

热更新规则

  1. 资源打包方式:需要更新的代码和资源,都必须打包成AssetBundle(AB包)进行下载和加载,AB包的特性适合做热更新。(理由如下)

    • AB包存储位置自定义,继而可放入可读可写的路径下便于实现热更新
    • AB包自定义压缩方式,可以选择不压缩或选择LZMA和LZ4等压缩方式,减小包的大小,更快的进行网络传输。
    • 资源可分布在不同的AB包中,最大程度减少运行时的内存压力, 可做到即用即加载,有选择的加载需要的内容
    • AB包支持后期进行动态更新,显著减小初始安装包的大小,非核心资源以AB包形式上传服务器,后期运行时动态加载,提高用户体验。
  2. 资源存放路径:优先选择将需要进行更新的代码和资源放在Application.persistentDataPath,相较其它特殊目录,该目录下可读可写,运行时有效且无内容限制更适合做热更新开发。(几个重要路径的对比如下)

    • Resources

      • Resources文件夹下的资源无论使用与否都会被打包

      • 所有资源全部打成一个大包,查找加载效率低下。

      • 资源会被压缩,转化成二进制

      • 打包后文件夹下的资源只读

      • 无法动态更改,无法做热更新

      • 使用Resources.Load加载

    • StreamingAssets

      • 流数据的缓存目录
      • 文件夹下的资源无论使用与否都会被打包
      • 资源不会被压缩和加密
      • 打包后文件夹下的资源只读,主要存放二进制文件
      • 打包后无法修改,无法做热更新
      • WWW类加载(一般用CreateFromFile ,若资源是AssetBundle,依据其打包方式看是否是压缩的来决定)
      • 相对路径,具体路径依赖于实际平台
      • Application.streamingAssetsPath路径在不同平台下有时不准确
    • Application.dataPath

      • 游戏的数据文件夹的路径(例如在Unity Editor中的Assets)
      • 无法做热更新
    • Application.persistentDataPath

      • 持久化数据存储目录的路径( 沙盒目录,打包之前不存在 )

      • 文件夹下的资源无论使用与否都会被打包

      • 运行时有效,可读写

      • 无内容限制,从StreamingAsset中读取二进制文件或从AssetBundle读取文件来写入PersistentDataPath

      • 适合热更新

猜你喜欢

转载自blog.csdn.net/Q540670228/article/details/122713401
今日推荐