此文章部分内容和思维导图来源于唐老狮相关Lua课程,通过Vistual Studio Code语法测试,如有问题,请在以下留言

由于全部为本工作室二级网站—学习格整理而成,故本网站对本文章拥有相关创作权,因此在搬运或者转载二次改变前请与本人联系,禁止一切未经允许的搬运行为,若出现或发现侵权行为,本人有权对相关侵权行为进行举报,维护本人和合法权益。

因本人知识储备有限,可能存在错误,如有错误,可在评论处进行评论(评论回复将通过qq邮箱进行回复),一起探讨进步

Sublime Text

Visual Studio Code(安装lua)

Typora

一.Lua相关注释

这里需要注意的是在C#中单行注释是使用”//“,多行注释是”/%内容%/“,在lua中的单行注释和多行注释相关格式如下图

1
--单行注释
1
2
3
--[[
多行注释
]]

二.数据类型

写在最前面

  • 在lua中无需定义数据的类型,在使用中会自动识别

  • lua当中的基本数据类型:

    (1)nil(类似于c#中null)

    (2)number(所有的数值都是number==>number包括int float double)

    (3)string (lua中没有char,字符串的声明使用单引号或者双引号包裹)

    (4)boolean(返回的是true或false)

  • lua当中的复杂数据类型:

    (1)函数(function)

    (2)表(table)【表需要特别学习,特别的重要】

    (3)数据结构(userdata)

    (4)协同程序(thread(线程))

  • lua中带 所有的变量申明,都不需要申明变量类型,同理可以随便赋值,可以自动判断类型==>类似c#中的var

  • lua中使用没有声明过的变量并不会报错,默认返回的是nil

  • lua默认是没有 面向对象的 ,需要自己实现

  • 在lua中的”数组” 起始位置与c#不同,从1开始

  • 当对变量多赋值时,会将后面多赋的值自动省略

  • 当对变量少赋值时,会将后面不够的值直接自动赋为空值
  • 多返回值时,用几个变量接就会有多少个值
  • 如果变量数大于方法返回的值数量,则会进行补空处理

  • 如果 变量数小于方法返回的值数量,则会根据变量数量进行接取

1.基本数据类型

nil的使用

1
2
a=nil  --定义a这个变量的值为nil
print(a) --这里是输出a这个变量,输出值也同样是nil,不过输出的是字符串类型

number(数值类型)

  • 这里需要注意一点:在上面赋过值得变量,可以重复使用,在下面依然可以重新赋值,也同样会根据相关赋值的类型转化为相对应的类型
1
2
3
a=1 b=1.25
print(a) --输出的值是1
print(b) --输出的值是1.25

string(字符串类型)

  • 在lua中是没有字符的,在定义字符串时可以使用以下三种方式:
    (1)单引号 'SeveneStudio'
    (2)双引号 "SeveneStudio"
    (3)中括号 [[SeveneStudio]]
1
2
3
4
5
6
a="SeveneStudio_Logo" 
print(a) --输出的值是SeveneStudio_Logo
a='SeveneStudio——Company'
print(a) --输出的值是SeveneStudio——Company
a=[[SevenStuido_KaiFa]]
print(a) --输出的值是SevenStuido_KaiFa

boolean(Boolean类型)

  • 在lua中只有true和false代表真与假,这个与c#有着区别
1
2
3
4
a=true
print(a) -- 输出的值是true
a=false
print(a) -- 输出的值是false

2.type()的使用和字符串扩展

type() 获取变量类型

  • 通过type()可以获取变量类型==>返回值是变量的数据类型,属于string类型
1
2
3
4
a=123
print(type(a)) --因为a的类型是number,所以通过type()获取的输出的是number
a="123"
print(type(a)) --因为a的类型是string,所以通过type()获取的输出的是string

3.字符串的相关使用

第一小节

相关方法:print(#变量名)

注意点
1.一个汉字占3个长度
2.英文字符占1个长度
1
2
s="aBcDEfG字符串"
print(#s)

说明:字符串的换行/字符串多行打印在lua中是与c#一样,是支持转义字符的

相关格式:可以使用\n或者[[字符串]]

1
2
3
4
5
6
7
print("123\n123")
s=[[
这里

SeveneStudio
]]
print(s)

相关格式:

(1)在lua中字符串的拼接通过..实现,字符串..字符串

1
2
3
4
print("HaXinXi".."XueYuan")  -- HaXinXiXueYuan
s1="Wang"
s2="Li"
print(s1..s2) -- HaXinXiXueYuan

(2)string.format()

注意点
%d 与数字进行拼接
%a 与任何字符拼接
%s 与字符配对
1
2
print(string.format("我是哈信息%d级学生",18))   --我是哈信息18级学生
print(string.format("我的家乡是%s市","承德")) --我的家乡是承德市

格式:string.upper(字符串)

注意点
string.upper(argument): 字符串全部转为大写字母,
string.lower(argument):字符串全部转为小写字母
1
2
3
s="SeveneStudio"
print(string.upper(s)) --SEVENESTUDIO
print(string.lower(s)) --sevenestudio

第二小节

格式:string.reverse(arg)了解即可,用途不是很大

1
print(string.reverse(s)) --oidutSeneveS

格式:string.find (str, substr, [init, [plain]])

init 指定了搜索的起始位置,默认为 1,可以一个负数,表示从后往前数的字符个数。
plain 表示是否以正则表达式匹配。

注意点
(1)lua的起始位置与c#不同,从1开始;
(2)string.find ()会返回起始位置和结束位置
1
2
s="SeveneStudio"
print(string.find(s,"eve")) --2 4

格式:string.sub(s, i [, j])

s:要截取的字符串;

i:截取开始位置;

j:截取结束位置,默认为 -1,最后一个字符。

1
2
3
string.sub(s, i [, j])
s="SeveneStudio"
print(string.sub(s,2,6)) --evene

格式:string.rep(string, n)

注意点
返回字符串String的n个拷贝,即将字符串复制n个返回
1
2
s="SeveneStudio"
print(string.rep(s,3)) --SeveneStudioSeveneStudioSeveneStudio

第三小节

格式:string.gsub(mainString,findString,replaceString,num)

mainString 为要操作的字符串;

replaceString 要替换的字符;

findString 为被替换的字符;

num 替换次数(可以忽略,则全部替换);

这里会返回两个:1.替换后的字符串,2.替换次数
1
2
s="SeveneStudio"
print(string.gsub(s,"e","*")) --S*v*n*Studio 3

格式:字符转ASCII码:string.byte(string,int)

​ ASCII码转字符:string.char(int)

注意点
string.byte(string,int) 字符转ASCII码 ==>string是指字符串,int是需要转换的位置
string.char(int) ASCII码转字符==>int指需要ASCII码转化为字符
1
2
3
4
print("字符转ASCII码")
print(string.byte(s,1)) -- 83
print("ASCII码转字符")
print(string.char(string.byte(s,1))) --S

4.复杂数据类型

  • 函数(function)

  • 表(table)【表需要特别学习,特别的重要】

  • 数据结构(userdata)

  • 协同程序(thread(线程))

注意:

(1)lua函数的使用与c#不同,需要在函数创建后调用

(2)在lua中当你传入的参数和函数中的参数个数不匹配时,并不会报错,而是少于参数个数补空或者多余参数个数丢失

(3)多返回值时,在前面申明多个变量来接取即可,如果变量不够,不会影响结果,值会根据实际接取对应位置的返回值

(4)在lua中并不支持函数的重载,如果函数名相同,参数不同时,则会执行最后声明的函数

基本语法

1
2
3
4
5
function 函数名()
end

a=function()
end

1.无参数无返回值的函数

1
2
3
4
5
6
7
8
9
print("**********无参数无返回值**********")
function F1()
print("f1函数")
end
F1()
F2=function ()
print("F2函数")
end
F2()

2.有参数无返回值

1
2
3
4
5
6
7
8
print("**********有参数无返回值**********")
function F3(a)
print(a)
end
F3(1)
F3("SeveneStudio")
F3(true)
F3(1,2,3)
1
2
3
4
5
**********有参数无返回值**********
1
SeveneStudio
true
1

3.有参数有返回值

1
2
3
4
5
6
7
8
9
temp=F4("1")
print("只有一个变量:",temp)
temp,temp2=F4("1")
print("只有两个变量(1):",temp)
print("只有两个变量(1):",temp2)
temp,temp2,temp3=F4("1")
print("有三个变量(1):",temp)
print("有三个变量(2):",temp2)
print("有三个变量(3):",temp3)

相关的测试结果

1
2
3
4
5
6
只有一个变量:   1
只有两个变量(1): 1
只有两个变量(1): 123
有三个变量(1): 1
有三个变量(2): 123
有三个变量(3): true

4.变长参数

1
2
3
4
5
6
7
8
9
10
print("**********变长参数**********")
function F7(...)
--变长参数使用一个表存起来
--这里需要注意变长参数以...代表
arg = {...}
for i = 1, #arg, 1 do
print(arg[i])
end
end
F7(1,"HX",true,4,5,6)

5.嵌套函数

1
2
3
4
5
6
7
8
print("**********嵌套函数**********")
function F8()
return function ()
print(123)
end
end
f9=F8()
f9()

在嵌套函数中引入了一个新的词:闭包【面试常问】

闭包:通过调用含有一个内部函数加上该外部函数持有的外部局部变量(upvalue)的外部函数(就是工厂)产生的一个实例函数

闭包组成:外部函数+外部函数创建的upvalue+内部函数(闭包函数)

1
2
3
4
5
6
7
8
function F9(x)
--改变传入参数的生命周期
return function (y)
return x+y
end
end
f10=F9(10)
print(f10(5))

相关的测试结果

这里最后执行的结果为15==>F9(10)执行的x=10,f(10)执行的是y=5

这里需要注意一个知识点:所有复杂类型本质都是table表

注意:

1.在lua中索引是从1开始的

2.通常在获取长度的时候关键字是#

3.打印长度时,nil(空)在末尾是被忽略的,但是不在末尾而是在某一位置,则会影响获取长度,由于底层的不同,有时转而打印在nil(空)之前的长度,有时会出现计算长度包括nil

1.数组的定义与长度获取

1
2
3
4
5
a={1,2,3,"SeveneStudio",true,nil}
print("打印数组第一个值",a[1])
print("a这个数组的长度为",#a)
b={1,nil,"SeveneStudio",true,8,5,6}
print("b这个数组的长度为",#b)

相关的测试结果

1
2
3
打印数组第一个值        1
a这个数组的长度为 5
b这个数组的长度为 7

2.数组的遍历

1
2
3
4
5
6
7
8
print("*********数组遍历*********")
for i=1,#a do
print(a[i])
end
print("---------------------------")
for i=1,#b do
print(b[i])
end

相关的测试结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
*********数组遍历*********
1
2
3
SeveneStudio
true
---------------------------
1
nil
SeveneStudio
true
8
5
6

3.二维数组的定义

1
2
3
4
print("*********二维数组*********")
a={{1,2,true},{4,5,"SeveneStudio"}}
print(a[1][3])
print(a[2][3])

相关的测试结果

1
2
3
*********二维数组*********
true
SeveneStudio

4.二维数组的遍历

1
2
3
4
5
6
7
print("*********二维数组遍历*********")
for i = 1, #a do
b=a[i]
for j = 1, #b do
print(b[j])
end
end

相关的测试结果

1
2
3
4
5
6
7
*********二维数组遍历*********
1
2
true
4
5
SeveneStudio

5.自定义索引

注意:

1.计算长度时会忽略小于等于0的索引

2.当对自定义索引进行跳跃性设置时,若只跳跃一个值并不会断掉,长度受最大的索引影响

【迭代器遍历主要是用于遍历表】

  • ipairs遍历

pairs遍历是从1开始往后遍历的,小于等于是不会获取到的
智能找到连续索引的键值,若中间断序,无法遍历后面的内容

1
2
3
4
5
6
7
8
9
10
a={[0]=1,2,[-1]=3,4,5,[5]=6}
print("***********ipairs迭代器遍历")
--第一种方式
for i, k in ipairs(a) do
print("ipairs遍历键值"..i.."_"..k)
end
--第二种方式==>可以省略k
for i in ipairs(a) do
print("ipairs遍历键值"..i)
end

相关的测试结果

1
2
3
4
5
6
7
***********ipairs迭代器遍历
ipairs遍历键值1_2
ipairs遍历键值2_4
ipairs遍历键值3_5
ipairs遍历键值1
ipairs遍历键值2
ipairs遍历键值3

  • pairs【推荐使用pairs遍历不规则数组

能够把所有的键都可以找到,通过键可以得到值

1
2
3
4
5
6
7
8
9
10
a={[0]=1,2,[-1]=3,4,5,[5]=6}
print("***********pairs迭代器遍历")
--ipairs遍历是从1开始往后遍历的,小于等于是不会获取到的
--智能找到连续索引的键值,若中间断序,无法遍历后面的内容
for i, k in pairs(a) do
print("pairs遍历键值"..i.."_"..k)
end
for i in pairs(a) do
print("pairs遍历键值"..i)
end

相关的测试结果

1
2
3
4
5
6
7
8
9
10
11
12
13
***********pairs迭代器遍历
pairs遍历键值1_2
pairs遍历键值2_4
pairs遍历键值3_5
pairs遍历键值0_1
pairs遍历键值5_6
pairs遍历键值-1_3
pairs遍历键值1
pairs遍历键值2
pairs遍历键值3
pairs遍历键值0
pairs遍历键值5
pairs遍历键值-1
lua字典的使用与C#相似

1.字典的声明与访问使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
print("****************字典的声明和使用****************")
--字典由键值对构成
a={["id"]=1,["name"]="SeveneStudio",["age"]=22,["hobby"]="踢足球,打篮球"}
--第一种方式访问
print(a["id"])
print(a["name"])
print(a["age"])
print(a["hobby"])
print("--------------------------------------------------")
--第二种方式访问
print(a.id)
print(a.name)
print(a.age)
print(a.hobby)
print("***修改***")
a["name"]="HYH"
print(a["name"])
print(a.name)
print("***增加***")
a["sex"]="man"
print(a["sex"])
print(a.sex)

相关的测试结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
****************字典的声明和使用****************
1
SeveneStudio
22
踢足球,打篮球
--------------------------------------------------
1
SeveneStudio
22
踢足球,打篮球
***修改***
HYH
HYH
***增加***
man
man

2.字典的遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
print("****************字典的遍历****************")
for k,v in pairs(a) do
print(k,v)
end
print("****************字典的值遍历****************")
for _,v in pairs(a) do
print("单个值:",v)
print("带上键:",_,v)
end
print("****************字典的键遍历****************")
for k in pairs(a) do
print(k)
print(a[k])
end

相关的测试结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
****************字典的遍历****************
sex man
name HYH
hobby 踢足球,打篮球
age 22
id 1
****************字典的值遍历****************
单个值 man
带上键 sex man
单个值 HYH
带上键 name HYH
单个值 踢足球,打篮球
带上键 hobby 踢足球,打篮球
单个值 22
带上键 age 22
单个值 1
带上键 id 1
****************字典的键遍历****************
sex
man
name
HYH
hobby
踢足球,打篮球
age
22
id
1

在开始的时候已经说过了lua没有面向对象的,所以这里通过表类实现类

下面给大家说明一下相关知识逻辑

TODO:这里到时候叙说

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Student={
--变量==>年龄,性别,姓名
name="小明",--姓名
age=18,--年龄
sex=true,--性别==>这里通过true代表男
--函数
Eat=function (food)
--如果采用这种方式进行书写,是与表中的name是毫无关系的,print中的name会看成全局变量哦
--想要在表内部函数中调用表本身的属性和方法,一定要使用表名.属性或者表名.方法
print(name)
print("我们中午吃的:",food)
end,
Learn=function ()
print("好好学习,天天向上")
end,
--在函数中调用自己属性或者方法的方式是把自己作为一个参数传进去,在内部访问
Sleep=function (s)
print(s.sex)
end
}
Student.Sleep(Student)
--lua在申明表后,在表外是可以申明表没有的变量和方法
Student.hobby="踢足球,打篮球";
Student.Speak=function ()
print(Student.name,"说话了")
end
function Student.SpeakTwo()
print("又说话了")
end
--lua无法通过实例化对象new的方式,更像是一个类中有很多静态方法和变量,通过类名点的方式调用
print(Student.name,"今年",Student.age)
print(Student.Eat("汉堡包"))
Student.Speak();
print(Student.hobby);
Student.SpeakTwo();
Student:SpeakTwo();

--lua中的点和冒号的区别
--冒号调用方法会默认把调用者作为第一个参数传入方法中
function Student:SpeakThree()
--lua中有一个关键字self表示默认传入的第一个参数
print(self.name.."说话")

end
Student:SpeakThree()
Student.SpeakThree(Student)

三.运算符

(1)在lua中是没有自增自减和复合运算的 (2)字符串可以进行算数运算符操作,可自动转化成number

加法(+)减法(-)乘法(*)除法(/)取余(%) 幂(^) 整除(//)

大于(>) 小于(<) 大于等于(>=) 小于等于(<=) 等于等于(==) 不等于(~=)

  • 在lua中逻辑与==>and
  • 在lua中逻辑或==>or
  • 在lua中逻辑否==>not
  • 在lua中同样遵守逻辑运算的“短路”规则
1
2
3
print(true and false)
print(true or false)
print(not false)
*特殊知识记录
and 逻辑与 or 逻辑或
and or 他们不仅可以连接boolean ,任何东西都可以连接
在lua中只有nil和false才会认为是假
“短路”===>对于and 是有假则假,对于 or 则是有真则真
所以只需要判断第一个是否满足就会停止计算
对于运算符and来说,假设它的第一个操作数为假,就返回第一个操作数;不然返回第二个操作数
对于运算符or来说,假设它的第一个操作数为真。就返回第一个操作数,不然返回第二个操作数

在lua中不支持位运算(& |),需要自己去实现

在lua中不支持三目运算符(?:),需要自身根据需要实现

模拟三目运算符

说明:这里x=3,y=2,所以很明显的说明x>y返回时true,然后继续看便是 true and x ,由于and是有假则假,所以true and x肯定是true了,对于or来说,true or y这个已经有一个真的了,所以直接返回3

1
2
3
4
5
x=3
y=2
--这里x>y返回的是true (x>y) and x==>3 是真的,就不用看or了
local res=(x>y) and x or y
print(res)

说明:(1)这里x=1,y=3,所以x>y返回的是false,所以现在变化成了false and y,在前面逻辑运算符中说到过and的逻辑,当第一个值为假,则返回第一个值,所以r是false

(2)根据上一个所知转化为 false or y,根据or的逻辑,第一个为假,则会返回第二个值,所以res是3

1
2
3
4
5
6
7
x=1
y=3
--这里x>y返回的是false,则(x>y)==>false and x==>x ==>false or y or是真则真==>取y,则是3
local r=(x>y) and x
print(r)
local res=(x>y) and x or y
print(res)

四.Lua循环和条件分支

注意:lua中是没有Switch语句的

1.条件分支语句

Lua 提供了以下条件分支语句:

语句 描述
[if 语句] if 语句 由一个布尔表达式作为条件判断,其后紧跟其他语句组成。
[if…else 语句] if 语句 可以与 else 语句搭配使用, 在 if 条件表达式为 false 时执行 else 语句代码。
[if 嵌套语句] 你可以在ifelse if中使用一个或多个 ifelse if 语句 。

基本语法:

if 条件 then ……end

1
2
3
4
a=8
if a>5 then
print("大于5")
end

基本语法

if 条件 then ……else ……end

1
2
3
4
5
6
a=22
if a>15 then
print("大于15")
else
print("小于15")
end

if 条件 then ……elseif 条件 then ……end

1
2
3
4
5
6
7
8
a=5
if a<5 then
print("小于5")
elseif a==5 then
print("等于5")
else
print("大于5")
end

2.循环语句

Lua 语言提供了以下几种循环处理方式:

循环类型 描述
[while 循环] 在条件为 true 时,让程序重复地执行某些语句。执行语句前会先检查条件是否为 true。
[for 循环] 重复执行指定语句,重复次数可在 for 语句中控制。
[repeat…until] 重复执行循环,直到 指定的条件为真时为止
[循环嵌套] 可以在循环内嵌套一个或多个循环语句(while do … end;for … do … end;repeat … until;)

基本语法:

while 条件 do……end

1
2
3
4
5
num=0
while num<5 do
print(num)
num=num+1
end

基本语法

repeat ……until 条件(条件事结束条件)

1
2
3
4
5
num=0
repeat
print(num)
num=num+1
until num>5

for 初始值,结束值,递增值

注意:lua中默认递增,i会默认+1,若保持默认递增值可以省略,若递减则需将递增值改为负数

1
2
3
4
5
6
7
8
9
10
11
12
print("****默认递增for循环****")
for i = 1, 10 do
print(i)
end
print("****每次递增3的for循环****")
for i = 1, 10, 3 do
print(i)
end
print("****每次递减1的for循环****")
for i = 5, 1, -1 do
print(i)
end

五.多脚本执行

在实际的开发过程中,无论是Java还是C#都需要多脚本的调用

1.全局变量和本地变量

这里全局变量直接就可以定义。例如

1
2
3
a=123 --number类型
b="SeriousWission" --字符串类型
c=true --boolean类型

在c#中的本地变量(亦可以说局部变量)是可以用private【私有】protected(保护,只有子类可以访问),在lua中就比较简易了,直接用local 来表示,例如

1
2
local d="HaXinXi"  --局部字符串类型
local e=123456 --局部字符串类型

2.全局方法和本地方法

前面已经说明了全局变量和本地变量,其实全局方法和本地方法是和其相差不多的

1
2
3
4
5
6
7
8
-- 全局方法
funOne=function ()
print("方法")
end
-- 本地方法
local funTwo=function ()
print("local方法")
end

3.脚本的调用

前面两个标题已经说明了一大部分知识点了,下面就是脚本的调用

这里先写一个Test.lua文件作为后面使用

1
2
3
4
5
6
7
8
9
10
print("Test这里加载了这个lua文件测试")
testA="123456"
local testLocalA="456789"
funOne=function ()
print("方法")
end
local funTwo=function ()
print("local方法")
end
return testLocalA

在lua中如果想用其他脚本,需要使用一个关键字:require(‘脚本名’)/ require(“脚本名”)这两种格式都是可以的,例如:

1
2
3
4
require("Test")  -- 调用同路径中的Test脚本
funOne() -- 这里因为上一行已经加载了Test脚本,而且Test脚本中funOne方法是共有的,所以可以直接使用
print(testA) --打印testA参数的值
print(testLocalA) -- 打印testLocalA参数的值

这里需要说明重要的点

相关说明
只要require执行完的脚本,任何全局变量,包括表,方法是可以直接拿来用的
这里需要注意若调用相同路径的文件可以直接使用文件名,而不同路径需要写入路径名
require不会重复加载同一个lua

4.脚本的卸载/移除

前面已经说完了脚本的声明调用,当这个脚本不需要了,该如何呢

先说明如何判断脚本是否声明过了

1
package.loaded["Test"]

当知道了判断脚本是否被调用了,在后面不需要了直接将其判断为空或者将判断是否调用过直接定义为false也是也是可以的

1
2
package.loaded["Test"]=nil  -- 将判断包是否加载过为nil
package.loaded["Test"]=false --将判断包是否加载过为false

5.局部变量使用

对于局部变量的使用可以在lua文件的最后一行返回一个局部参数,然后在需要的地方来用local 参数接收即可

1
2
local a=require("Test") --这里调用Test.lua文件,用local a来接收,其实收到的也就是Test.lua中的testLocalA
print(a)

6.大G表

_G是一个总表,本质是table,他将我们申明的所有全局变量都存储在其中

声明的local本地/局部变量是不会存到_G表中的

1
2
3
4
5
6
a=1
b="serious"
c=true
for key, value in pairs(_G) do
print(key,value)
end

相关参考信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
string  table: 0000021B456413D0
ipairs function: 00007FFDB36C9980
setmetatable function: 00007FFDB36C8B20
_G table: 0000021B45633FA0
rawequal function: 00007FFDB36C8C90
c true
b serious
load function: 00007FFDB36C9EB0
rawlen function: 00007FFDB36C8DB0
getmetatable function: 00007FFDB36C8A40
assert function: 00007FFDB36CA580
debug table: 0000021B45641690
package table: 0000021B45641050
os table: 0000021B45641190
xpcall function: 00007FFDB36CAA80
table table: 0000021B45641510
dofile function: 00007FFDB36CA3A0
io table: 0000021B456411D0
pairs function: 00007FFDB36C9780
next function: 00007FFDB36C9640
loadfile function: 00007FFDB36C9AC0
require function: 0000021B456412D0
utf8 table: 0000021B45640F90
tonumber function: 00007FFDB36C85C0
select function: 00007FFDB36CA620
a 1
rawget function: 00007FFDB36C8E80
arg table: 0000021B45641010
math table: 0000021B456415D0
pcall function: 00007FFDB36CA800
_VERSION Lua 5.4
print function: 00007FFDB36C8210
warn function: 00007FFDB36C8330
collectgarbage function: 00007FFDB36C91E0
type function: 00007FFDB36C9590
coroutine table: 0000021B45641490
tostring function: 00007FFDB36CADA0
rawset function: 00007FFDB36C8FF0

六.协同程序

前言:协程其实可以看作是一个线程对象,前面已经说了方法的定义与使用,这里就直接写一个方法

首先是一个比较简单的方法,打印的是SeriousWission这个字符串

1
2
3
fun=function ()
print("SeriousWission")
end

协同程序的创建

这里需要说明的便是这两种方式的使用和运行是不一样的

第一种方式-coroutine.create

1
2
co=coroutine.create(fun)
print(co) --打印结果 thread: 0000025793510878

第二种方式-coroutine.warp

1
2
co2=coroutine.wrap(fun)
print(co2) -- function: 0000025793542920

这样看的还是不太明显,使用type()看看这两个协程程序的类型

1
2
3
4
5
6
-- 创建协程的第一种方式
co=coroutine.create(fun)
print(type(co)) --thread
-- 创建协程的第二种方式
co2=coroutine.wrap(fun)
print(type(co2)) --function

这样就能看出来这两种创建协程的方式类型是不同的,第一种方式本质是线程,但是第二种则本质是方法

协同程序的运行

这里运行协程程序因为前面用到的方式不同,使用也是不同的

1
2
3
4
5
--运行通过coroutine.create()创建的协程
coroutine.resume(co)
--运行通过coroutine.wrap()创建的协程
--直接用接收coroutine.wrap()的方法名运行即可
co2()--因为本质是方法,可以直接使用方法来运行

协同程序的挂起

这里需要注意一个点,c#的协程在启动后会按照一定时间不停的执行,但是lua只会执行一次

首先我们重新在定义一个方法并在里面放上协程的挂起函数

1
2
3
4
5
6
7
8
9
10
fun2 =function ()
local i=0
while true do
i=i+1
print("I Like Lua".."==>"..i)
-- 协程挂起函数
coroutine.yield()
end
end

使用上面说到的运行协同程序的方式看看效果

1
2
3
4
5
6
7
8
9
10
11
--coroutine.create()创建
co3=coroutine.create(fun2)
--coroutine.resume()运行
coroutine.resume(co3) --输出结果为:I Like Lua==>1
coroutine.resume(co3) --输出结果为:I Like Lua==>2

--coroutine.wrap()创建
co4=coroutine.wrap(fun2)
--运行
co4() --输出结果为:I Like Lua==>1
co4() --输出结果为:I Like Lua==>2

即使while是一个死循环,这两种方式都只会执行一次,只有再创建相应的开始程序才会执行

协同程序的挂(加返回值)

协程的挂起说完了,其实协程是可以有返回值的

定义一个function()【为了方便直接使用fun2的】

1
2
3
4
5
6
7
8
9
fun3 =function ()
local i=0
while true do
i=i+1
print("I Like Lua".."==>"..i)
-- 协程挂起函数
coroutine.yield(i)
end
end

执行这两个程序

1
2
3
4
5
6
7
8
9

-- 这里需要明白,是coroutine.resume这时有两个返回值,第一个返回的是程序是否执行成功,第二个是yield的值
co3=coroutine.create(fun3)
isOK,tempOne=coroutine.resume(co3)
print(isOK,tempOne)

-- coroutine.wrap返回的是没有程序是否成功这个值,只会返回程序中的值和yield的值
co4=coroutine.wrap(fun3)
print("返回值==>"..co4())

七.元表

首先先对元表是什么来一个大白话的解释

元表可以解释为一个表的父亲,也就是父表,
任何表变量都可以作为另一个表变量的元表
任何表变量都可以有自己的元表(父亲)
当我们子表中进行一些特定操作时,会执行元表的内容(其中包括tostring(),index()等)

  1. 设置元表

设置元表就相比较简单了==>setmetatable(子表,元表【可以看做是父表】),下面举个例子看看

1
2
3
mytable = {}                          -- 普通表
mymetatable = {} -- 元表
setmetatable(mytable,mymetatable) -- 把 mymetatable 设为 mytable 的元表
  1. 特定操作

__tostring():当子表要被当做字符串使用时,会默认调用这个元表中的tostring方法

1
2
3
4
5
6
7
8
mytable2={
__tostring=function()
return "爱吃大饼的小虎"
end
} --定义一个表
mymetatable2={} --定义一个元表
setmetatable(mytable2,mymetatable2) --- 输出结果:爱吃大饼的小虎
print(myTable2) --这里print是输出语句,里面输出的字符串,因此便会输出 爱吃大饼的小虎

同样里面function里面也是可以带参数的,那就再写一个

1
2
3
4
5
6
7
8
9
10
mytable3={
__tostring=function(t)
return t.name
end
} --定义一个表
mymetatable3={
name="爱吃大饼的小虎"
} --定义一个元表
setmetatable(mytable3,mymetatable3) ---输出结果:爱吃大饼的小虎
print(mytable3) --这里不难看出,子表需要知道父表有name这个参数,才可以输出

__call :在 Lua 调用一个值时调用:简单来说就是当做函数来用时调用

这里需要注意一点:只有元表里有__call元方法,才可以使用方法调用,不然会报错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
meta4={
--当子表要被当做字符串使用时,会默认调用这个元表中的tostring方法
--这里为了凸显出不同,特意换了一个字符串输出
__tostring=function()
return "爱吃大饼的小虎"
end,
__call=function ()
print("七鳄学习格")
end
}
myTable4={}
--设置元表函数setmetatable(子表,元表【可以看做是父表】)
setmetatable(myTable4,meta4)
--当作函数来调用,这时不会调用__tostring(),而是__call()
myTable4() --输出结果:七鳄学习格

这时 传入一个参数 会出现什么效果呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
meta5={
--当子表要被当做字符串使用时,会默认调用这个元表中的tostring方法
--这里为了凸显出不同,特意换了一个字符串输出
__tostring=function(t)
return t.name
end,
__call=function (a)
print(a)
print("七鳄学习格")
end
}
myTable5={
name="黄雨涵123"
}
--设置元表函数setmetatable(子表,元表【可以看做是父表】)
setmetatable(myTable5,meta5)
--当作函数来调用,这时不会调用__tostring(),而是__call()
myTable5(1)
--输出结果:
黄雨涵123
七鳄学习格
--这里可能会意外,需要解释一下:当方法中带参数,会将myTable5本身传进去,所以打印的便是myTable5中的name参数的值,
--当__call=function (a)中在传入一个参数,是__call=function (a,b)时便会打印传入的数值1,
--简单来说第一个是表本身,第二个参数是传入的数值
  1. 元表中运算符

其实元表也是支持 运算符 的,下面就直接用程序做解释吧(以下罗列了常用的运算符)

翻阅Lua官网API可知:需要注意元表中的运算符只有小于或者小于等于,如果想实现大于或者大于等于可以选择取反实现
模式 描述
__add 对应的运算符 ‘+’.
__sub 对应的运算符 ‘-‘.
__mul 对应的运算符 ‘*’.
__div 对应的运算符 ‘/‘.
__mod 对应的运算符 ‘%’.
__unm 对应的运算符 ‘-‘.
__concat 对应的运算符 ‘..’.
__eq 对应的运算符 ‘==’.
__lt 对应的运算符 ‘<’.
__pow 对应的运算符‘^’
__le 对应的运算符 ‘<=’.

这里罗列了代码实现效果与输出结果,可能有些乱,但是看上去还是比较容易看明白的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
meta5={
------对应的是运算符+,当执行加法执行此方法
__add=function(t1,t2)
return t1.age+t2.age
end,
----对应的是运算符-,当执行减法执行此方法
__sub=function (t1,t2)
return t1.age-t2.age
end,
----对应的是运算符*,当执行乘法执行此方法
__mul=function (t1,t2)
return t1.age*t2.age
end,
----对应的是运算符/,当执行除法执行此方法
__div=function (t1,t2)
return t1.age/t2.age
end,
----对应的是运算符==,当执行相等执行此方法
__eq=function (t1,t2)
return "" --这里只会返回true或者false
end,
----对应的是拼接,当执行拼接执行此方法
__concat=function (t1,t2)
return t1.str..t2.str
end,
----对应的是运算符==,当执行相等执行此方法
__lt=function (t1,t2)
return "" --这里只会返回true或者false
end,
}
myTable5={age=1,str='黄雨涵'}
myTable6={age=2,str='真帅'}
--设置元表函数setmetatable(子表,元表【可以看做是父表】)
setmetatable(myTable5,meta5)
setmetatable(myTable5,meta6)
print(myTable5+myTable6) --3
print(myTable5-myTable6) ---1
print(myTable5*myTable6) --2
print(myTable5/myTable6) --0.5
print(myTable5==myTable6) --false
print(myTable5..myTable6) --黄雨涵真帅
print(myTable5<myTable6) --true

__index:当子表中,找不到某一个属性时,会到元表中的 _index中指定的表 去找索引;

__index遵循向上查找原则

这里其实有一个坑,举个例子阐述一下

1
2
3
4
5
6
7
meta7={
age=1
}
myTable7={}
--设置元表函数setmetatable(子表,元表【可以看做是父表】)
setmetatable(myTable7,meta7)
print(myTable7.age) --输出结果:nil
解释:

这里很有可能会认为输出的是1,因为age=1,但是他的结果为nil,也就是意味着为空;在使用_index时需要用 _index指定表,如下代码:

这里有一个小小的坑:__index的赋值需要放到表外面赋值
1
2
3
4
5
6
7
8
9
meta7={
age=1
}
--用元表指定所指定的表,这里就指向元表本身即可,其实指向一个新表也是可以的=>meta7.__index={age=2}
meta7.__index=meta7
myTable7={}
--设置元表函数setmetatable(子表,元表【可以看做是父表】)
setmetatable(myTable7,meta7)
print(myTable7.age) --输出结果:1

其实到这里就基本上够用了,但是再上升一个高度[层层查找]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
meta7={
--这里将myTable7的元表中的age注释上
--age=1
}
meta7Father={
age=1
}
meta7Father.__index=meta7Father
meta7.__index=meta7
myTable7={}
--设置元表函数setmetatable(子表,元表【可以看做是父表】)
setmetatable(meta7,meta7Father)
setmetatable(myTable7,meta7)
print(myTable7.age) --输出结果:1
解释:

这里很显然,myTable7中没有age这个字段,然后到其元表的meta7中查找,但是meta7的age被注释上了,刚刚我们设置了meta7的元表是meta7Father,这个时候便会到meta7Father中查找,如果没有便无法在找了,便会返回nil,但是meta7Father中有age字段,便会返回1

__newindex:当赋值时,如果赋值一个不存在的索引,会把这个值赋值到newindex所指的表中,不会修改自己

__newindex同样遵循向上查找原则
1
2
3
4
5
6
7
8
9
10
meta8={}
meta8.__newindex={}
myTable8={}
--设置元表函数setmetatable(子表,元表【可以看做是父表】)
setmetatable(myTable8,meta8)
myTable8.age=10
--这时打印出的是nil
print(myTable8.age)
--如果想获取到age的值,则需要获取__newindex中的age
print(meta8.__newindex.age) --输出结果:10
  1. 得到元表

getmetatable(table): 返回对象的元表(metatable

1
2
3
4
meta9={}
myTable9={}
setmetatable(myTable9,meta9)
print(getmetatable(myTable9))
  1. 查找参数/设置参数

rawget():绕过__index,在自身身上查找有没有这个变量

1
2
3
4
meta10={}
myTable10={}
setmetatable(myTable10,meta10)
print(rawget(myTable10,"age"))

rawset():绕过__newindex,在自身身上设置变量

1
2
3
4
5
6
meta11={}
meta11.__newindex={}
myTable11={}
setmetatable(myTable11,meta11)
rawset(myTable11,"age",2)
print(myTable11)

八.面向对象

面向对象思维导图

这里复习一下c#知识,在c#中面向对象的三大特性:继承,封装,多态;

  • 封装:指能够把一个实体的信息、功能、响应都装入一个单独的对象中的特性。
  • 继承:继承的方法允许在不改动原程序的基础上对其进行扩充,这样使得原功能得以保存,而新功能也得以扩展。这有利于减少重复编码,提高软件的开发效率。
  • 多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。

在lua中的对象便是由属性方法 组成,lua中最基本的结构 是table,所以需要用table来描述对象的属性

封装

1
2
Object ={}
Object.id=1

这还是只是一个表,还有一个参数是id等于1,想要实现c#的实例化,就要有一个实例化方法

1
2
3
4
5
6
7
8
9
-- 冒号是会自动将调用这个函数的对象作为第一个参数传入的写法
function Object:new()
--self 代表的是我们默认传入的第一个参数
--对象就是变量,返回一个新的变量
--返回出去的内容 本质上就是表对象
local obj={}
--元表知识: __index 当找自己的变量找不到时,就会找元表当中的__index指向的内容
return obj;
end

这时我们即可调用这个方法来完成实例化

1
2
local myObj=Object:new()
print(myObj)

此时你想要得到id的值,你会发现print(myObj.id)的值为nil,这是因为你获取的是Object:new()里面的obj,这是没有id的,而且又没指定元表,当然id是nil(空)咯,所以想要获取id的值,你需要指定其元表

1
2
3
4
5
6
7
8
9
-- 冒号是会自动将调用这个函数的对象作为第一个参数传入的写法
function Object:new()
local obj={}
--元表知识: __index 当找自己的变量找不到时,就会找元表当中的__index指向的内容
-- 这里的self可以理解为函数调用者
self.__index=self
setmetatable(obj,self)
return obj;
end

这是在通过print(myOj.id)时,obj中没有id,然后到其元表,这里使用self是因为冒号是会自动将调用这个函数的对象作为第一个参数,也就是Object了,这样就可以获取Object里的id的值了,所以打印结果为:1

上面说完了参数的使用,当然少不了在c#中的方法的调用,那我们就设置一个无参的方法和一个有参的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Object.name="爱吃大饼的小虎"
function Object:Test()
print(self.name)
end
function Object:TestTwo(age)
Object.age=age
print(self.age)
end
-- 冒号是会自动将调用这个函数的对象作为第一个参数传入的写法
function Object:new()
local obj={}
--元表知识: __index 当找自己的变量找不到时,就会找元表当中的__index指向的内容
-- 这里的self可以理解为函数调用者
self.__index=self
setmetatable(obj,self)
return obj;
end

这个时候我们就可以调用这个Test()方法和Test(age)的方法了

1
2
myObj:Test() --因为获取的Object.name的值,所以为:爱吃大饼的小虎
myObj:TestTwo(2)--因为是将age这个值2赋给Object.age,所以为:2

那当name的值赋值是什么结果呢?

1
2
3
myObj.name="七鳄学习格"
print(Object.name) ---这里的结果还是:爱吃大饼的小虎
myObj:Test() --这里的结果还是:七鳄学习格
解释: 这是因为通过myObj.name等于是对obj空表申明一个新的属性是叫作name,所以并未对元表的Object的name造成影响,同理当调用myObj:Test()时打印的也是myObj的name的值是 :七鳄学习格 ## 继承 在c#中的继承是 class 类名:需要继承的类(父类),在lua是需要自己写来实现,首先我们先复用上面的Object代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Object ={}
Object.id=1
Object.name="爱吃大饼的小虎"
function Object:Test()
print(self.name)
end
function Object:TestTwo(age)
Object.age=age
print(self.age)
end
-- 冒号是会自动将调用这个函数的对象作为第一个参数传入的写法
-- 冒号是会自动将调用这个函数的对象作为第一个参数传入的写法
function Object:new()
--self 代表的是我们默认传入的第一个参数
--对象就是变量,返回一个新的变量
--返回出去的内容 本质上就是表对象
local obj={}
--元表知识: __index 当找自己的变量找不到时,就会找元表当中的__index指向的内容
self.__index=self
setmetatable(obj,self)
return obj;
end

这里需要复习一下G(大G表)的相关知识,我们知道 _G是一个总表,本质是table,他将我们申明的所有全局变量都存储在其中

1
2
3
4
5
--这里相当于将a和b的值传入到_G表中,方可通过_G.a和_G.b获取值
_G["a"]=1
_G.b=123
print(_G.a)
print(_G.b)

其实到这里就比较容易说通了,我们可以通过结合_G表来实现继承,示例代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
--className表示一个新的类的名称
function Object:subClass(className)
--相当于将新的类放到_G表中
_G[className]={}
local obj= _G[className]
--指向元表
self.__index=self
setmetatable(obj,self)

end
--调用这个subClass方法
Object:subClass("Person")
--相当于实例化Person
local p1=Person:new()
print("获取Person的值:"..p1.id) --这里的结果为1
解释: 这里需要说明一下p1的new方法相当于执行的function Object:new()方法,那么就需要按照如下推理了
1
2
3
4
(1)p1:Person:new()===>function Object:new()
(2)所以function Object:new()中的obj,self相当于Person,所以p1中没有id,就要到Person中找,诚然也是没有的
(3)那么就要到Person的元表中__index去找
(4)这里就用到function Object:subClass(className)了,Person相当于className,然后local obj= _G[className],所以Person就是obj,self相当于Object,这里就找到id了,结果是1

然后说一下赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Object:subClass("Person")
local p1=Person:new()
--这里p1.id获取到的是元表Person的元表Object的id的值
print("获取Person的值:"..p1.id) --1
p1.id=100
--这里相当于给p1添加一个新的属性id,并为其赋值为100,既然自己有id属性,当然就不会在向上找了
print(p1.id) --100
Object:subClass("Monster")
local m1=Person:new()
--这里m1.id相当于新建一个类,名字交Monster,然后没有id这个属性,所以就向上找,获取到的是元表Person的元表Object的id的值
print("获取Monster的值:"..m1.id) --1
--这里相当于给m1添加一个新的属性id,并为其赋值为200,既然自己有id属性,当然就不会在向上找了
m1.id=200
print(m1.id) --200

多态

首先主要明白什么是多态

(1)相同行为 不同表象 就是多态

(2)相同方法 不同执行逻辑 就是多态

而需要实现多态需要拥有三个条件:继承,重写,父类引用指向子类对象:Parent p = new Child();

因为需要用到上面的代码,复用一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Object ={}
Object.id=1
Object.name="爱吃大饼的小虎"
function Object:Test()
print(self.name)
end
function Object:TestTwo(age)
Object.age=age
print(self.age)
end
-- 冒号是会自动将调用这个函数的对象作为第一个参数传入的写法
-- 冒号是会自动将调用这个函数的对象作为第一个参数传入的写法
function Object:new()
--self 代表的是我们默认传入的第一个参数
--对象就是变量,返回一个新的变量
--返回出去的内容 本质上就是表对象
local obj={}
--元表知识: __index 当找自己的变量找不到时,就会找元表当中的__index指向的内容
self.__index=self
setmetatable(obj,self)
return obj;
end
--className表示一个新的类的名称
function Object:subClass(className)
--相当于将新的类放到_G表中
_G[className]={}
local obj= _G[className]
--指向元表
self.__index=self
--子类 定义个base属性 base属性代表父类
obj.base = self
setmetatable(obj,self)

end

实现所谓的继承,重写,父类引用指向子类对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
--相当于创建一个GameObject类
Object:subClass("GameObject")
-- 创建posX和posY两个属性
GameObject.posX = 0;
GameObject.posY = 0;
--创建一个方法
function GameObject:Move()
self.posX = self.posX + 1
self.posY = self.posY + 1
print(self.posX)
print(self.posY)
end
--相当于创建一个GameObject类
GameObject:subClass("Player")
--调用Move其方法
function Player:Move()
--base 指的是 GameObject 表(类)
--这里相当于继承GameObject的Move方法
self.base:Move()
end
--实例化p1
local p1 = Player:new()
p1:Move()

这样基本的多态就ok了,但是当我们在创建一个p2

1
2
3
--实例化p2
local p2 = Player:new()
p2:Move() --结果可以自己看看是2,2,这是超级大的bug,他等于改变了GameObject的posX和posY的值了
原因: 通过冒号调用p1的Move方法,因为self.base:Move()指的是GameObject的Move方法,因为用冒号调用等于将GmaeObject本身作为第一个值,相当于就是公用一张表的属性了,然后将在不停的改变posX和posY的值,如果要执行父类逻辑 我们不要直接使用冒号调用,要通过.调用 然后自己传入第一个参数 所以这里就需要用self.base.Move(self)这样调用方可解决这个问题(Bug) # 九.自带库 ## 时间库 - os.time ([table]) 按table的内容返回一个时间值(数字),若不带参数则返回当前时间.(在许多系统中该数值是当前距离某个特定时间的秒数。),按照这种方式写即可
1
print(os.time()) 
1
2
--自己传入参数,得到时间
print(os.time({year=2014,month=8,day=14}))
  • os.date ([format [, time]])

功能:返回一个按format格式化日期、时间的字串或表

*t”:将返一个带year(4位),month(1-12), day (1—31), hour (0-23), min (0-59), sec (0-61), wday (星期几, 星期天为1), yday (年内天数), and isdst (是否为日光节约时间true/false)的带键名的表;

若没有”*t”则返回一个按C的strftime函数格式化的字符串;

若不带参数,则按当前系统的设置返回格式化的字符串 os.date() <=> os.date(“%c”)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
local nowTime=os.date("*t")
for index, value in pairs(nowTime) do
print(index,value)
end
print(nowTime.hour)
--结果如下
sec 26
month 2
isdst false
wday 3
yday 45
year 2023
min 6
hour 21
day 14

如果使用带标记(见下表)的特殊字符串,os.data函数会将相应的标记位以时间信息进行填充,得到一个包含时间的字符串。

特殊字符串 说明
%a 一星期中天数的简称(Wed)
%A 一星期中天数的全称(Wednesday)
%b 月份的简称(Sep)
%B 月份的全称(September)
%c 日期和时间(11/11/11 11:11:11)
%d 一个月中的第几天(0-31)
%H 24小时制中的小时数
%I 12小时制中的小时数
%j 一年中的第几天(259)
%M 分钟数
%m 月份数
%P 上午(am)或者下午(pm)
%S 秒数(00-59)
%w 一星期的第几天(3)
%W 一年中的第几个星期(0-52)
%x 日期(01/01/01)
%X 时间(00:00:00)
%y 两位数的年份(00-99)
%Y 完整的年份(2001)
%% 字符串”%”

例如:

1
2
print(os.date("today is %A, in %B"))  --运行结果: today is Tuesday, in February
print(os.date("%X", 906000490)) --运行结果: 10:48:10

数学库

比较常用的

1
2
3
4
5
print("绝对值:"..math.abs(-11))
print("π的值:"..math.pi)
print("返回小于或等于 x 的最大整数:"..math.floor(8.15))
print("返回其参数中的最大值:"..math.max(1,8,11,98,14,32))
print("返回其参数中的最小值:"..math.min(11,-8,1,98,14,32))

这里按照实际需求来用咯

库/方法 功能
math.abs (x) 返回 x 的绝对值
math.acos (x) 返回 x 的反余弦值(以弧度为单位)
math.asin (x) 返回 x 的反正弦(以弧度为单位)
math.atan (x) 返回 x 的反正切(以弧度为单位)
math.atan2 (y, x) 返回 y/x 的反正切(以弧度为单位),但使用两个参数的符号来查找结果的象限。 (它还可以正确处理 x 为零的情况。)
math.ceil (x) 返回大于或等于 x 的最小整数。
math.cos (x) 返回 x 的余弦(假定为弧度)。
math.cosh (x) 返回 x 的双曲余弦值。
math.deg (x) 返回以度为单位的角度 x(以弧度为单位)。
math.exp (x) 返回值 e 幂 x。
math.floor (x) 返回小于或等于 x 的最大整数。
math.fmod (x, y) 返回 x 除以 y 的余数,该余数将商向零舍入。
math.frexp (x) 返回 m 和 e 使得 x = m2e, e 是一个整数并且 m 的绝对值在 [0.5, 1) 范围内(或者当 x 为零时为零)。
math.huge 值 HUGE_VAL,大于或等于任何其他数值的值。
math.ldexp (m, e) 返回 m2e(e 应该是一个整数)。
math.log (x) 返回 x 的自然对数。
math.log10 (x) 返回 x 的以 10 为底的对数。
math.max (x, …) 返回其参数中的最大值。
math.min (x, …) 返回其参数中的最小值。
math.modf (x) 返回两个数字,x 的整数部分和 x 的小数部分。
math.pi pi 的值。
math.pow (x, y) 返回 xy。 (您也可以使用表达式 x^y 来计算此值。)
math.rad (x) 返回以弧度为单位的角度 x(以度为单位)。
math.random ([m [, n]]) 此函数是 ANSI C 提供的简单伪随机生成器函数 rand 的接口。当不带参数调用时,返回范围 [0,1) 内的统一伪随机实数。 当使用整数 m 调用时,math.random 返回范围 [1, m] 内的统一伪随机整数。 当使用两个整数 m 和 n 调用时,math.random 返回范围 [m, n] 内的统一伪随机整数。
math.randomseed (x) 将 x 设置为伪随机生成器的”种子”:相等的种子产生相等的数字序列。
math.sin (x) 返回 x 的正弦值(假定为弧度)。
math.sinh (x) 返回 x 的双曲正弦值。
math.sqrt (x) 返回 x 的平方根。 (您也可以使用表达式 x^0.5 来计算此值。)
math.tan (x) 返回 x 的正切(假定为弧度)。
math.tanh (x) 返回 x 的双曲正切。

文件路径

1
print(package.path)

十.I/O

Lua I/O 库用于读取和处理文件。分为简单模式(和C一样)、完全模式

  • 简单模式
1
2
file = io.open("test.lua", "r") -- 以只读方式打开文件 
--打开文件的格式file = io.open (filename [, mode])这里的mode是类型,具体类型如下表
模式 描述
r 以只读方式打开文件,该文件必须存在。
w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
r+ 以可读写方式打开文件,该文件必须存在。
w+ 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
a+ 与a类似,但此文件可读可写
b 二进制模式,如果文件是二进制文件,可以加上b
+ 号表示对文件既可以读也可以写
1
2
io.input(file) -- 设置默认输入文件为 test.lua
print(io.read()) -- 输出文件第一行

这里io.read()是可以添加参数的,具体如下:

“*n” 读取一个数字并返回它。例:file.read(“*n”)
“*a” 从当前位置读取整个文件。例:file.read(“*a”)
“*l”(默认) 读取下一行,在文件尾 (EOF) 处返回 nil。例:file.read(“*l”)
number 返回一个指定字符个数的字符串,或在 EOF 时返回 nil。例:file.read(5)
1
2
3
4
5
io.close(file) -- 关闭打开的文件
file = io.open("test.lua", "a") -- 以附加的方式打开只写文件
io.output(file) -- 设置默认输出文件为 test.lua
io.write("-- test.lua 文件末尾注释") -- 在文件最后一行添加 Lua 注释
io.close(file) -- 关闭打开的文件

十一.垃圾回收

在lua中垃圾回收的关键字是collectgaabage,其中加入不同的参数实现不同的功能,其实比较常用的基本就count和collect两个;

collectgarbage(“count”)

以 K 字节数为单位返回 Lua 使用的总内存数。 这个值有小数部分,所以只需要乘上 1024 就能得到 Lua 使用的准确字节数(除非溢出)。

1
2
3
--使用就比较简单了
test={id=1,name="123"}
print(collectgarbage("count")) --74.1328125

collectgarbage(“collect”)

做一次完整的垃圾收集循环,即垃圾回收,这里lua中的机制和C#中垃圾回收机制很类似 解除羁绊 就是变垃圾,所以如下代码会发现

1
2
3
4
5
6
--复用一下上面的代码
test={id=1,name="123"}
print(collectgarbage("count")) --74.4267578125
test = nil
collectgarbage("collect") --做一次垃圾回收
print(collectgarbage("count")) --45.8173828125==>这里会发现内存数变小了

做个最后的结语咯

其实lua中 有自动定时进行GC的方法,采用了自动内存管理。 这意味着你不用操心新创建的对象需要的内存如何分配出来, 也不用考虑在对象不再被使用后怎样释放它们所占用的内存。这也就可以说:Unity中热更新开发,尽量不要去用自动垃圾回收

对所有代码都严格审核规范

数据类型知识点

多脚本执行

面向对象

对每一个知识点都做细节

lua知识点覆盖,并能够应用于学习和工作当中

如果感觉这篇文章不错,记得点击一下的分享和复制地址按钮转发一下,经济充裕的话可以打赏一下作者,感激不尽,创作不易,期待每一个点滴

爱发电:爱吃大饼的小虎正在创作博客,独立游戏 | 爱发电 (afdian.net)