Unity 面试篇|(五)热更新与Lua语言篇 【全面总结 | 持续更新】

目录

    • 1.什么是热更新?
    • 2.主流的代码热更方案有哪些?
    • 3.AssetBundle介绍
    • 4.AssetBundle的具体开发流程
    • 5.AssetBundle的压缩格式
    • 6.AssetBundle对象的加载方式
    • 7.ssetBundle资源卸载
    • 8.资源如何打包?依赖项列表如何生成?
    • 9.如何解析版本文件?如何加载AB包资源?具体流程是怎么样的?
    • 10.热更新打包方案有哪些?
    • 11.热更新的流程
    • 11.1简述Lua实现面向对象的原理
    • 12.简述Lua有哪8个类型?简述用途
    • 13.C#与Lua的交互原理简述
    • 14.Lua中 pairs与ipairs区别
    • 15.Lua中 点和冒号区别
    • 16. Lua深拷贝和浅拷贝
    • 17.Lua中的闭包简述
    • 18. __index和__newindex元方法的区别
    • 19.table的一些知识点
    • 20.Lua是如何实现热更新的
    • 21.Lua的sort排序用的是什么排序方法
    • 22. Lua和C#的交互原理

1.什么是热更新?

  • 热更新 是一种App软件开发者常用的更新方式。简单来说,就是在用户通过下载安装APP之后,打开App时遇到的即时更新。
  • 在安卓、iOS平台,热更新表示在更新游戏资源或逻辑的时候不需要开发者将游戏再打包、上传、审核、发布、玩家重新下载安装包更新游戏,仅需要开发者打出新的ab(AssetBundle)资源文件放到网上,然后游戏程序下载新的ab资源文件替换本地的资源文件来实现游戏更新的流程。
  • 热更代码可以理解成是特殊的资源。

2.主流的代码热更方案有哪些?

  • LUA热更(xLua/toLua等)(LUA与C#绑定,方案成熟)
  • ILRuntime热更
  • puerts
  • HyBridCLR(原huatuo)
  • iOS:IL2CPP,AOT(Ahead of Time,运行前编译)
  • 安卓:Mono,JIT(Just in Time,动态(即时)编译)
  • DLL基于动态即时编译,只能在JIT模式下使用,即无法在iOS平台使用
    lua有自己的虚拟机和运行时解释器,不受限于编译方式(IL2CPP、Mono)
    ILRuntime和LSharp也有自己的虚拟机和运行时解译引擎,也不受限于编译方式

3.AssetBundle介绍

  • AssetBundle是将资源使用Unity提供的一种用于存储资源的压缩格式打包后的集合,它可以存储任何一种Unity可以识别的资源,如模型,纹理图,音频,场景等资源。也可以加载开发者自定义的二进制文件。
  • 用途:
    • 制作DLC (动态的可下载内容)
    • 减少初始包大小
    • 加载为用户平台优化的资源
    • 减少运行时的内存压力

4.AssetBundle的具体开发流程

  • 创建Asset bundle,开发者在unity编辑器中通过脚本将所需要的资源打包成AssetBundle文件。
    上传服务器。开发者将打包好的AssetBundle文件上传至服务器中。使得游戏客户端能够获取当前的资源,进行游戏的更新。
  • 下载AssetBundle,首先将其下载到本地设备中,然后再通过AsstBundle的加载模块将资源加到游戏之中。
  • 加载,通过Unity提供的API可以加载资源里面包含的模型、纹理图、音频、动画、场景等来更新游戏客户端。
  • 卸载AssetBundle,卸载之后可以节省内存资源,并且要保证资源的正常更新。

5.AssetBundle的压缩格式

  • LZMA格式: 使用LZMA格式压缩的AssetBundle的包体积最小(高压缩比),但是相应的会增加解压缩时的时间。
  • LZ4格式:压缩后的AssetBundle包体的体积较大(该算法基于chunk)。但是使用LZ4格式的好处在于解压缩的时间相对要短。
  • 不压缩:没有经过压缩的包体积最大,但是访问速度最快。

6.AssetBundle对象的加载方式

  • Unity提供了三个不同的API从AssetBundles加载UnityEngine.Objects,这些API都绑定到AssetBundle对象上,并且这些API具有同步(和异步变体):
    • LoadAsset(LoadAssetAsync):从资源包中加载指定的资源
    • LoadAllAssets (LoadAllAssetsAsync):加载当前资源包中所有的资源
    • LoadAssetWithSubAssets (LoadAssetWithSubAssetsAsync)
  • 并且这些API的同步版本总是比异步版本快至少一个帧(其实是因为异步版本为了确保异步,都至少延迟了1帧),异步加载每帧会加载多个对象,直到它们的时间切片切出。

7.ssetBundle资源卸载

  • AssetBundle.Unload(false):内存中的AssetBundle对象包含的资源会被销毁。
  • AssetBundle.Unload(true):不仅仅内存中的AssetBundle对象包含的资源会被销毁。根据这些资源实例化而来的游戏内的对象也会销毁。
  • Reources.UnloadAsset(Object):显式的卸载已加载的Asset对象,只能卸载磁盘文件加载的Asset对象Resources。
  • UnloadUnusedAssets:用于释放所有没有引用的Asset对象
  • Destroy:主要用于销毁克隆对象,也可以用于场景内的静态物体,不会自动释放该对象的所有引用。虽然也可以用于Asset,但是概念不一样要小心,如果用于销毁从文件加载的Asset对象会销毁相应的资源文件!但是如果销毁的Asset是Copy的或者用脚本动态生成的,只会销毁内存对象。

8.资源如何打包?依赖项列表如何生成?

  • 查找指定文件夹ABResource里的资源文件
    • Directory.GetFile(资源路径)
    • 新建AssetBundleBuild对象
    • 获取资源名称,并赋值对应AB名称
    • 获取各个资源的依赖项:通过UnityEditor.AssetDataBase类获取各个资源的依赖项
  • 使用Unity自带的BuildPipeline进行构建AB包
    • BuildPipeLine.BuildAssetBundles(输出AB包路径)
    • File.WriteAllLines(将依赖项写入文件里)

9.如何解析版本文件?如何加载AB包资源?具体流程是怎么样的?

  • 解析版本文件列表
    • File.ReadAllLines(读取文件列表资源路径URL)
    • 获取资源名称,获取AB包名称,获取依赖项,字典容器存储
    • 获取Lua文件
  • 加载资源
    • 异步加载资源AB包,AssetBundleRequest请求,AssetBundle.LoadFromFileAsync
      先检查依赖项,再异步加载AB包依赖项
    • 加载成功后都有对应的回调方法,将资源作为参数传入

10.热更新打包方案有哪些?

  • 整包:将完整更新资源放在Application.StreamAssets目录下,首次进入游戏将资源释放到Application.persistentDataPath下。
    • 优点:首次更新少
    • 缺点:安装包下载时间长,首次安装久
  • 分包:少部分资源放在包里,其他资源存放在服务器上,进入游戏后将资源下载到Application.persistentDataPath目录下。
    • 优点:安装包小,安装时间短,下载快
    • 缺点:首次更新下载解压缩包时间旧
  • 适用性
    • 海外游戏大部分是使用分包策略,平台规定
    • 国内游戏大部分是使用整包策略

11.热更新的流程

  • 导出热更流程
    • 打包热更资源的对应的md5信息(涉及到增量打包)
    • 上传热更 ab 到热更服务器
    • 上传版本信息到版本服务器
  • 游戏热更流程
    • 启动游戏。
    • 根据当前版本号,和平台号去版本服务器上检查是否有热更。
    • 从热更服务器上下载 MD5 文件,比对需要热更的具体文件列表。
    • 从热更服务器上下载需要热更的资源,解压到热更资源目录。
    • 游戏运行加载资源,优先到热更目录中加载,再到母包资源目录加载。

11.1简述Lua实现面向对象的原理

  • 表table就是一个对象,对象具有了标识self,状态等相关操作
  • 使用参数self表示方法的该接受者是对象本身,是面向对象的核心点,冒号操作符可以隐藏该self参数
  • 类(Class):每个对象都有一个原型,原型(lua类体系)可以组织多个对象间共享行为
  • setmetatable(A,{__index=B}) 把B设为A的原型
  • 继承(Inheritance):Lua中类也是对象,可以从其他类(对象)中获取方法和没有的字段
  • 继承特性:可以重新定义(修改实现)在基类继承的任意方法
  • 多重继承:一个函数function用作__Index元方法,实现多重继承,还需要对父类列表进行查找方法,但多继承复杂性,性能不如单继承,优化,将继承的方法赋值到子类当中
  • 私有性(很少用)基本思想:两个表表示一个对象,第一个表保存对象的状态在方法的闭包中,第二个表用来保存对象的操作(或接口),用来访问对象本身。使第一个表完成内容私有性。

12.简述Lua有哪8个类型?简述用途

  • nil 空——可以表示无效值,全局变量(默认赋值为nil),赋值nil ,使其被删除。
  • number 整数
  • table 表
  • string 字符
  • userdata 自定义
  • function 函数
  • bool 布尔
  • thread线程

13.C#与Lua的交互原理简述

  • 想要理解Lua语言与其它语言交互的实质,我们首先就要理解Lua堆栈。
    简单来说,Lua语言之所以能和C/C++进行交互,主要是因为存在这样一个无处不在的虚拟栈。栈的特点是先进后出,在Lua语言中,Lua堆栈是一种索引可以是正数或者负数的结构,并规定正数1永远表示栈底,负数-1永远表示栈顶。
    换句话说,在不知道栈大小的情况下,我们可以通过索引-1取得栈底元素、通过索引1取得栈顶元素。
  • Lua是一种嵌入式脚本语言,可以方便的与c/c++进行相互调用。但是Unity中主要是用c#进行开发的,因此在Unity中使用Lua通常有以下两种方案:
    • 使用c#实现一个lua虚拟机
    • 基于原生的c lua api做一个封装,让c#调用
  • 从性能上考虑,当前主流方案都是第二种。
    基于第二种方案实现的框架目前主要有xLua,sLua,uLua,NLua(+KeraLua)。在这些方案中,都能找到一个相关的类,封装了c#对lua c api的调用。例如在xlua中是XLua.LuaDLL.Lua这个类,在slua中是SLua.LuaDll这个类。
    所以在Unity里执行Lua是以c作为中间媒介的:
C# <=> C <=> Lua
  • Lua与宿主语言(这里以c#为例)最基础的两种交互模式即:
    • c#执行lua代码
    • lua执行c#静态/成员函数
  • 这种交互是通过一个栈结构进行的。

14.Lua中 pairs与ipairs区别

  • pairs会遍历所有key,对于key的类型没有要求,遇到nil时可以跳过,不会影响后面的遍历,既可以遍历数组部分,又能遍历哈希部分。
  • ipairs只会从1开始,步进1,只能遍历数组部分, 中间不是数字的key忽略, 到第一个不连续的数字为止(不含),遍历时只能取key为整数值,遇到nil时终止遍历。

15.Lua中 点和冒号区别

  • 点 :无法传递自身,需要显示传递
  • 冒号 :隐式传递自身

16. Lua深拷贝和浅拷贝

  • 如何实现浅拷贝
    使用 = 运算符进行浅拷贝
    • 拷贝对象是string、number、bool基本类型。拷贝的过程就是复制黏贴!修改新拷贝出来的对象,不会影响原先对象的值,两者互不干涉。
    • 拷贝对象是table表,拷贝出来的对象和原先对象时同一个对象,占用同一个对象,只是一个人两个名字,类似C#引用地址,指向同一个堆里的数据~,两者任意改变都会影响对方。
  • 如何实现深拷贝
    复制对象的基本类型,也复制源对象中的对象
    常常需用对Table表进行深拷贝,赋值一个全新的一模一样的对象,但不是同一个表。
    Lua没有实现,封装一个函数,递归拷贝table中所有元素,以及设置metetable元表。
    如果key和value都不包含table属性,那么每次在泛型for内调用的Func就直接由if判断返回具体的key和value。
    如果有包含多重table属性,那么这段if判断就是用来解开下一层table的,最后层层递归返回。

17.Lua中的闭包简述

  • 闭包=函数+引用环境
    子函数可以使用父函数中的局部变量,这种行为可以理解为闭包!
    • 闭包的数据隔离
      不同实例上的两个不同闭包,闭包中的upvalue变量各自独立,从而实现数据隔离
    • 闭包的数据共享
      两个闭包共享一份变量upvalue,引用的是更外部函数的局部变量(即Upvlaue),变量是同一个,引用也指向同一个地方,从而实现对共享数据进行访问和修改。
    • 利用闭包实现简单的迭代器
      迭代器只是一个生成器,他自己本身不带循环。我们还需要在循环里面去调用它才行。
      • while…do循环,每次调用迭代器都会产生一个新的闭包,闭包内部包括了upvalue(t,i,n),闭包根据上一次的记录,返回下一个元素,实现迭代
      • for…in循环,只会产生一个闭包函数,后面每一次迭代都是使用该闭包函数。内部保存迭代函数、状态常量、控制变量。

18. __index和__newindex元方法的区别

  • __newindex用于表的更新,__index用于表的查询。
    • 如果访问不存在的数据,由__index提供最终结果
    • 如果对不存在的数据赋值,由__newindex对数据进行赋值
  • __index元方法可以是一个函数,Lua语言就会以【表】和【不存在键】为参数调用该函数
  • __index元方法也可以是一个表,Lua语言就访问这个元表
    对表中不存在的值进行赋值的时候,解释器会查找__newindex
  • __newindex元方法如果是一个表,Lua语言就对这个元表的字段进行赋值

19.table的一些知识点

  • table 是 Lua 的一种数据结构,用于帮助我们创建不同的数据类型,如:数组、字典等;
  • table 是一个关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是 nil,所有索引值都需要用 “[“和”]” 括起来;如果是字符串,还可以去掉引号和中括号; 即如果没有[]括起,则认为是字符串索引,Lua table 是不固定大小的,你可以根据自己需要进行扩容;
  • table 的默认初始索引一般以 1 开始,如果不写索引,则索引就会被认为是数字,并按顺序自动从1往后编;
  • table 的变量只是一个地址引用,对 table 的操作不会产生数据影响;
  • table 不会固定长度大小,有新数据插入时长度会自动增长;
  • table 里保存数据可以是任何类型,包括function和table;
  • table所有元素之间,总是用逗号 “,” 隔开;

20.Lua是如何实现热更新的

  • Lua的模块加载机制,热更的核心就是替换Package.loaded表中的模块。
  • 导出函数require(mode_name)
  • 查询全局缓存表package.loaded
  • 通过package.searchers查找加载器
    • package.loaded
      存储已经被加载的模块:当require一个mode_name模块得到的结果不为假时,require返回这个存储的值。require从package.loader中获得的值仅仅是对那张表(模块)的引用,改变这个值并不会改变require使用的表(模块)。
    • package.preload
      保存一些特殊模块的加载器:这里面的值仅仅是对那张表(模块)的引用,改变这个值并不会改变require使用的表(模块)。
    • package.searchers
      require查找加载器的表:这个表内的每一项都是一个查找器函数。当加载一个模块时,require按次序调用这些查找器,传入modname作为唯一参数。此方法会返回一个函数(模块的加载器)和一个传给这个加载器的参数。或返回一个描述为什么没有找到这个模块的字符串或者nil。

21.Lua的sort排序用的是什么排序方法

快速排序

public void QuickSort(int[] arr,int low,int high){if (low>high)return;int pivot = partition(arr,0,arr.Length()-1);QuickSort(arr,low,pivot-1);QuickSort(arr,pivot+1,high);
}
public int partition(int[] arr,int low,int high){int pivot = arr[low];while(low<high){while( low < high && arr[high] >= pivot) high--;arr[low] = arr[high];while(low > high && arr[low] <= pivot) low++;arr[high] = arr[low];}arr[low] = pivot;return low;
}

22. Lua和C#的交互原理

  • C# -->Lua:由C#先将数据放入栈中,由lua去栈中获取数据,然后返回结果数据到栈顶,再由栈顶返回至C#。
  • Lua -->Wrap -->C#:先生成C#源文件所对应的Wrap文件或者编写C#源文件所对应的C模块,然后将源文件内容通过Wrap文件或者C模块注册到Lua虚拟机中,然后由Lua去调用这个模块的函数

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

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

相关文章

华为OD机试2024年最新题库(Python)

我是一名软件开发培训机构老师&#xff0c;我的学生已经有上百人通过了华为OD机试&#xff0c;学生们每次考完试&#xff0c;会把题目拿出来一起交流分享。 重要&#xff1a;2024年1月-5月&#xff0c;考的都是OD统一考试&#xff08;C卷&#xff09;&#xff0c;题库已经整理…

Flutter之运行错误:this and base files have different roots

运行时报错&#xff1a; this and base files have different roots: E:\Demolpro\waqu\build\flutter-plugin-_android_lifecycle and C:\Users\78535\AppData\Local\Pub\Cache\hosted\pub.dev\flutter_pulgin_android_lifecycle-2.0.17\android 如图&#xff1a; 这种情况…

【JavaSE】P33~P113 方法,重载,数组,对象,构造器,this关键字,数组和对象的内存图,JavaBean

练习 1 方法方法定义及调用JShell简单使用&#xff08;要求Java9以上&#xff09;方法语句流程控制及注意事项选择循环 方法的重载 2 数组三种初始化方式数组在内存中的存储内存图 3 对象对象内存图Getter/Setter快捷写法this 关键字构造方法JavaBean的四个标准对象数组 二、编…

微信小程序支付之V2支付

一、引入maven <dependency><groupId>com.github.binarywang</groupId><artifactId>weixin-java-pay</artifactId><version>3.3.4.B</version><exclusions><exclusion><artifactId>qrcode-utils</artifactId&…

生成视频 图像拖拽生成视频

目录 motionctrl DragNUWA 预训练模型&#xff1a; motionctrl 该工具的多功能性使其成为视频制作领域的一项重要创新。它不仅能与现有的视频生成工具如LVDM和VideoCrafter1无缝集成&#xff0c;还能与SVD等其他视频生成方法兼容&#xff0c;为视频创造提供更多可能性。此外…

带F/V变换的反馈型相位控制电路D211,内置过载限制功能、电压监测功能

D211是一块相位控制集成电路&#xff0c;该电路内部具有F-V转换接口、控制放大器、过载限制、软启动、自动重触发、电压监视、电压电流同步等功能。主要应用于电动工具中马达转速的控制。 主要特点&#xff1a; 内置F-V转换接口 外控制集成放大器 内置过载限制功能 …

【数据结构】八大排序之计数排序算法

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 目录 一.计数排序简介及思想 二.计数排序代码实现 三.计数排序复杂度分析 &#x1f4cc;时间复杂度 &#x1f4cc;空间复杂度 结语 一.计数排序简介及思想 计数排序(Cou…

Gitlab集成openLDAP统一认证登录

vim /etc/gitlab/gitlab.rb&#xff0c; 可以配置很多个server&#xff0c;因此与sssd服务一样可以配置多个ldap作为高可用 gitlab-ctl reconfiguregitlab-rake gitlab:ldap:checkgitlab-ctl restart gitlab-rake gitlab:ldap:check Checking LDAP ...LDAP: ... Server: ldapm…

Leetcode刷题(二十四)

括号生成&#xff08;Medium&#xff09; 数字 n 代表生成括号的对数&#xff0c;请你设计一个函数&#xff0c;用于能够生成所有可能的并且 有效的 括号组合。示例 1&#xff1a;输入&#xff1a;n 3 输出&#xff1a;["((()))","(()())","(())()&…

发起人自选-钉钉审批

场景描述 配置一个审批流程&#xff0c;在某些审批节点&#xff0c;不能确定谁具体来审批&#xff0c;所以需要手工选择一个人或者多个人保证流程能得以顺利通过。有些审批流程的做法是&#xff0c;上一个节点来选择指定的人&#xff0c;而钉钉的做法是发起人来指定。 钉钉设…

【Maven】007-Maven 工程的继承和聚合关系

【Maven】007-Maven 工程的继承和聚合关系 文章目录 【Maven】007-Maven 工程的继承和聚合关系一、Maven 工程的继承关系1、继承的概念2、继承的作用3、继承的语法4、父工程统一管理依赖版本父工程声明依赖版本子工程继承以来版本 二、Maven 工程的聚合关系1、聚合的概念2、聚合…

linux文本三剑客(grep,sed,awk)使用

目录 一、grepgrep概念grep使用 二、sedsed概念sed使用 三、awkawk概念awk使用 一、grep grep概念 Linux系统中grep命令是一种强大的文本搜索工具&#xff0c;它能使用正则表达式搜索文本&#xff0c;并把匹配的行打印出来(匹配到的标红)。grep全称是Global Regular Expressi…

【信号与系统】【北京航空航天大学】实验一、信号的MATLAB表示及信号运算

一、实验目的 1、初步掌握 MATLAB 仿真软件的使用&#xff1b; 2、学习使用 MATLAB 产生基本时域信号&#xff0c;并绘制信号波形&#xff1b; 3、学习利用 MATLAB 实现信号的基本运算&#xff1b; 4、利用 MATLAB 分析常用的连续时域信号。 二、实验内容 1、 生成连续信号 …

Redis 常见的性能问题有哪些?该如何解决?

Redis 常见的性能问题有哪些&#xff1f;该如何解决&#xff1f; Redis 常见的性能问题包括内存使用过高、网络延迟、阻塞操作、持久化操作引起的性能问题等。以下是一些常见性能问题及解决方法&#xff1a; 内存使用过高&#xff1a; 问题&#xff1a; 当 Redis 使用的内存超…

jQuery圆形轮播自动切换图文

jQuery圆形轮播自动切换图文 注意这里用到了swiper插件&#xff0c;记得引入swiper.js和swiper.css swiper官网 这里面用到的swiper版本是Swiper 4.4.2 不同版本有些写法会不同&#xff0c;可对照官方文档进行调整 效果展示 jquery圆形轮播自动切换文字 html代码片段 <li…

【LangChain学习之旅】—(6) 提示工程(下):用思维链和思维树提升模型思考质量

【LangChain学习之旅】—&#xff08;6&#xff09; 提示工程&#xff08;下&#xff09;&#xff1a;用思维链和思维树提升模型思考质量 什么是 Chain of ThoughtFew-Shot CoTZero-Shot CoTChain of Thought 实战CoT 的模板设计程序的完整框架Tree of Thought总结 Reference&a…

优雅草蜻蜓API大数据服务中心v1.0.4更新-加入蓝奏云直链解析·每日Bing·字数统计·今日油价·历史上的今天等接口

2024年1月13日优雅草蜻蜓API大数据服务中心v1.0.4更新-加入蓝奏云直链解析每日Bing字数统计今日油价历史上的今天等接口 优雅草api服务-大数据中心自12月29日推出以来截止2024年1月13日累计被调用次数为413次&#xff0c;共收录23个接口&#xff0c;截止前一日2024年1月12日当…

OpenCV-Python(36):ORB算法

ORB&#xff08;Oriented FAST and Rotated BRIEF&#xff09;是一种用于图像特征提取和描述的算法。它是FAST角点检测器和BRIEF特征描述符的结合体&#xff0c;通过在FAST角点周围计算BRIEF描述符来提取关键点的特征。 ORB算法具有以下特点&#xff1a; 1.速度快&#xff1a…

《向量数据库指南》让「引用」为 RAG 机器人回答增加可信度

在之前的文章中&#xff0c;我们已经介绍了如何用 Milvus 向量数据库以及 LlamaIndex 搭建基础的聊天机器人《Chat Towards Data Science &#xff5c;如何用个人数据知识库构建 RAG 聊天机器人&#xff1f;》《书接上回&#xff0c;如何用 LlamaIndex 搭建聊天机器人&#xff…

pyqt5 pyinstaller 打包 QThread QLable QscrollArea 滑动 红果短剧

废话 不多说&#xff0c;直接上代码&#xff01;&#xff01;&#xff01; UI.py self.scrollArea QtWidgets.QScrollArea(self.centralwidget)self.scrollArea.setGeometry(QtCore.QRect(20, 130, 541, 511))self.scrollArea.setWidgetResizable(True)self.scrollArea.setOb…