0
点赞
收藏
分享

微信扫一扫

【热更新实践】Lua和热更新

Lua重点语法

基本概念

(1)基本概念

  • 这是lua中唯一的数据结构,本质上是键值对,下标从1开始。
  • 数组的索引可以是数组或者字符串
  • table 不会固定长度大小,有新数据添加时 table 长度会自动增长,没初始的 table 都是 nil。

代码演示:

a = {6,8,9,"a",x=123,pos = {x=90,y=89,z=20}}
a.z=156
print(a.z)
print(a.x)
print(a.pos)
print(a.pos.x)

for k,v in pairs(a) do
print(k, ":", v)
end

for k,v in ipairs(a) do
print(k, ":", v)
end

输出效果:

【热更新实践】Lua和热更新_版本号

知识点:

  • table中nil不可以做为键
  • ipairs和pairs的区别
  • 两者都可以用来遍历,但是ipairs只能遍历数组部分,pairs可以遍历数组部分和hash部分
  • 对于nil的处理不同,pairs遇到nil会跳过之后继续执行,ipairs会停止遍历

case 1:

b={pos={2,6,9},8,90,"z",x=678,122,}
for k,v in pairs(b) do
print(k, ":", v)
end

for k,v in ipairs(b) do
print(k, ":", v)
end

输出效果:

【热更新实践】Lua和热更新_热更新_02

case 2:

b={pos={2,6,9},8,nil,90,"z",x=678,122,}
for k,v in pairs(b) do
print(k, ":", v)
end
print("------------------------------------")
for k,v in ipairs(b) do
print(k, ":", v)
end

输出效果:

【热更新实践】Lua和热更新_服务器_03

(2)底层实现

table的底层分为数组部分+哈希表部分

每个table结构最多由3块连续内存组成:Table结构+数组(存放了连续的整数索引)+哈希表(大小为2的整数次幂)

(3)表的索引

a = {6,8,9,"a",x=123,pos = {x=90,y=89,z=20}}
print(a[1])
print(a.pos,a.x)

输出效果:

【热更新实践】Lua和热更新_服务器_04

解读:有两种方式,[]可以实现所有的索引,.只能实现索引为字符的索引


元表和元方法

元表的本质:一个普通的table+定义了特定事件下的原始值

由key(一个__开头的string)和metavalue(大多数是一个function,被叫做元方法)组成

操作:

  • 获取:getmetatable
  • 改变:setmetatable

每一种类型的数据共享同一个metatable,除了string类型,其他类型都是默认没有元表的。

  • __index & __newindex
  • 在表中查找一个值,如果没找到,则判断其是否有元表
  • 如果没有元表,查找结束,返回nil;如果有元表,则调用__index去父脚本里查找
  • __rawget & __rawset
  • __rawget:不想从__index对应的元方法中查找值
  • __rawset:不想执行__newindex对应的元方法

推崇的做法:

  • 在将一个表设置为另一个对象的元表之前,先将所需的所有元方法写好
  • 在对象创建之后立即为其设置元表


实现一个只读的表

只读表需要满足的条件:

  • 禁止在表中创建新的值
  • 禁止改变已有的值
  • 子表也只是可读

代码实现:

参照了这篇博文:

​​https://blog.csdn.net/Mr_Sun88/article/details/105648580?spm=1001.2101.3001.6650.4&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-4-105648580-blog-90167437.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-4-105648580-blog-90167437.pc_relevant_default&utm_relevant_index=6​​

--首先创建需要被设为只读的表
local testTable = {}
testTable.width = 10
testTable.print = function ()
print("This is a readonly table.")
testTable.childtable = {2,5,8,9}
end

--设置只读的方法
function setReadOnlyTable(t)
local proxy = {}
local meta = {
__index = t,
__newindex = function (t,k,v)
error("testTable is a readonly table.")
end
}

--设置元表
setmetatable(proxy, meta)
return proxy
end

--测试
local readonlyTable = setReadOnlyTable(testTable)
print("------------------------------------")
print(readonlyTable.width)
readonlyTable.print()
print("------------------------------------")
print(readonlyTable.childtable[3])
readonlyTable.childtable[3]=18
readonlyTable.x="abc"

string

lua中有8种基本的数据类型

nil, boolean, number, string, function, userdata, thread, and table

string有三种表示形式

a="abc"
b='hjk'
c= [[I'm a good man.]]
print(a,b,c)

输出效果:

【热更新实践】Lua和热更新_版本号_05

C#中字符char用单引号来表示,lua中没有char这种数据结构,单个字符的string就用来表示字符了。

实现面向对象

要求:

  • 实现对象:利用table,可以定义table的属性和方法
  • 实现类:利用元表
  • 实现继承和多态
  • 实现私有成员


GC

参考:​​https://blog.csdn.net/fwb330198372/article/details/104263213​​

lua有自动的内存管理,会有一个垃圾收集器来自动的回收垃圾。

当一个对象被垃圾收集器确认不会再被访问到的时候,就会被认为是死对象。

垃圾收集器有两种模式:incremental & generational


采用三色增量标记清除法:

两色增量标记清除法的问题:需要停下来等待GC

三色含义:

  • 白色:新建对象的初始状态。如果在一轮GC扫描结束后,对象还是为白色,该对象可以被进行回收了。
  • 灰色:表示对象已经被GC扫描过,但该对象引用的对象还没被扫描到。
  • 黑色:表示对象已经被GC扫描过,同时该对象引用的对象也被扫描过了。

采用三色增量标记清除法GC的流程:

  • 开始:将新创建的对象置为白色
  • 初始化:遍历根结点中引用的对象,从白色置为灰色,放到灰色节点列表中
  • 标记阶段:
  • 回收阶段:遍历对象,如果白色,没有被引用的对象,那么被回收

协程

lua支持协程

  • 进程:
  • 是程序运行的基本单位;
  • 每个进程都有自己独立的内存空间,所以进程间切换的开销比较大
  • 通信:不同的进程通过进程间通信
  • 优势:稳定;劣势:切换开销大
  • 线程:
  • 一个进程至少包含一个线程,可能有多个
  • 又叫轻量级进程,是CPU调度的最小单位
  • 通信:通过共享内存
  • 优势:切换快,开销小;劣势:没有进程稳定,容易丢失数据
  • 协程:
  • 一个线程可以有多个协程,是一种用户态的轻量级线程
  • 协程的调度是不被操作系统控制的,由程序来控制


排序算法

冒泡排序

代码:

a={6,9,10,3,2,8}

--排序方法
function bubbleSort(t)
local index = 0
for i=1, #t -1 do
for j=1, #t-i do
if t[j] > t[j+1] then
--交换
t[j], t[j+1] = t[j+1], t[j]
end

end
index = index+1
end
return t
end

--打印方法
function PrintTable(t)
for k,v in pairs(t) do
print(k,":",v)
end
end

--测试
PrintTable(a)
local b=bubbleSort(a)
print("------------排序后--------------")
PrintTable(b)

输出效果:

【热更新实践】Lua和热更新_服务器_06

快速排序

代码:

function quickSort(t, left, right)
if left >= right then return end

local low = left
local high = right
local base = t[low]--提取基准

while low < high do
while t[high]>= base and low <high do
high = high -1
end
t[low] = t[high]

while t[low]<= base and low < high do
low = low +1
end
t[high] = a[low]
end
t[low] =base

--接下来递归

quickSort(t,left,low-1)
quickSort(t, low+1,right)
end

--打印方法
function PrintTable(t)
for k,v in pairs(t) do
print(k,":",v)
end
end

print("------------quickSort--------------")
quickSort(a, 1, #a)
PrintTable(a)

输出效果:

【热更新实践】Lua和热更新_热更新_07

热更新

原理

Unity游戏热更新包含两个方面:资源+脚本

热更新的方案:AssetsBundle

涉及3个目录:

  • 游戏资源目录:
  • windos:Application.dataPath + "/StreamingAsset"
  • ios: Application.dataPath + "/Raw"
  • android: jar:file://Application.dataPath + "!/assets"
  • 数据目录: 可读可写,安卓和ios上的游戏资源目录是仅可读的
  • 网络资源地址:服务器地址,用来存放游戏资源的网址

热更的步骤:

  • 第一次开启游戏:将游戏资源目录中的内容复制到数据目录中
  • 游戏开启后:从网络资源地址下载更新的文件到数据目录中;过程中会先下载服务器上的files.txt,和本地的MD5做比较,更新有变化的文件
  • 游戏过程中:从数据目录中获取、解包。

数据目录中包含:不同版本的资源文件+用于版本控制的file.txt(包含:资源文件的名称+MD5)

过程

Unity3D 热更新的一般方法:

  • 导出热更资源
  • 打包md5信息
  • 上传热更ab到热更服务器
  • 上传版本信息到版本服务器
  • 游戏流程热更
  • 启动游戏
  • 根据版本号和平台号去版本服务器上检查是否有热更
  • 从热更服务器上下载MD5文件
  • 从热更服务器上下载需要热更的资源,解压到热更资源目录
  • 游戏运行加载资源,优先到热更目录,然后才是母包资源目录

MD5:通过MD5算法生成电子签名,类似于数字指纹;Unity热更中MD5文件存储的信息包括:AB路径、MD5值、未压缩文件大小、压缩文件大小


版本号

一般是四位:

  • 巨大版本号:有巨大的变化才会变
  • 整包更新版本号:走应用商店的大的版本迭代
  • 服务器协议版本号:需要更新商店的版本号
  • 编译/热更版本号:每次热更都+1
举报

相关推荐

0 条评论