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
输出效果:
知识点:
- 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
输出效果:
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
输出效果:
(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)
输出效果:
元表和元方法
元表的本质:一个普通的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)
输出效果:
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)
输出效果:
快速排序
代码:
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)
输出效果:
热更新
原理
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