目录
第一章 Lua基本语法
1.1 分号
- Lua的分号可以省略。
1.2 注释
- 单行注释
--单行注释
- 多行注释
--[[
多行
注释
]]
--[[
第二种
多行
注释
]]--
--[[
第三种
多行
注释
--]]
1.3 print打印
print("Hello,world!!")
第2章 简单变量类型
- Lua中的简单变量类型 nil number string boolean
- Lua中所以的变量声明,都不需要声明变量类型,他会自动的判断类型,类似C# 的 var
- Lua中的一个变量可以随便赋值 —自动识别类型。
a = nil
a = 1
- 复杂数据类型
- 函数 function
- 表 table
- 数据结构 userdata
- 协同程序 thread(线程)
2.1 nil类型
- 空类型。类似于C#中的null。
a = nil
2.2 number类型
- 所以的数值都是number。
a = 1
a = 1.2
2.3 string类型
- 字符串的声明 使用单引号或者双引号包裹。
- Lua里没有 char。
a = "123"
a = '123'
2.4 boolean类型
a = true
a = false
2.5 type函数
- 通过type函数我们可以得到变量的类型。
- 打印结果为变量类型。
- type函数自身的返回值为string
a = 1
print(type(a))
2.6 没有声明的变量
- Lua中使用没有声明过的变量,不会报错,默认值为nul。
print(b)
第3章 字符串操作
3.1 获取字符串长度
- 一个汉子占3个长度
- 英文字符占1个长度
s = "aBcdEfG字符串"
print(#s)
--输出结果为16
3.2 字符串多行打印
- Lua中也支持转义字符 \n
print("123\\n123")
- 第二种方法
s = [[我是
jo
ker
]]
print(s)
3.3 字符串拼接
- 字符串拼接通过**..**
- ..可以与任何简单类型拼接
print("123" .. "456")
s1 = "123123"
s2 = 111
print(s1 .. s2)
--输出结果为123123111
3.4 占位符
- 常用的几个。 %d :与数字拼接 %a :与任何字符拼接 %s 与字符配对 …
print(string.format("我是joker,今年%d岁了", 18))
3.5 其他类型转字符串
a = true
print(tostring(a))
3.6 字符串提供的公共方法
3.6.1 小写转大写的方法
- 原本的变量不会改变,只是打印出来的改变了。
str = "abCdefg"
print(string.upper(str))
3.6.2 大写转小写
- 同上。
str = "abCdefg"
print(string.lower(str))
3.6.3 翻转字符串
- 同上。
str = "abCdefg"
print(string.reverse(str))
3.6.4 字符串索引查找
- Lua字符串从1开始而并非0。
- Lua可以多返回值。
str = "abCdefg"
print(string.find(str, "Cde"))
--输出为3 5
print(string.find(str, "C"))
--输出为3 3
3.6.5 截取字符串
str = "abCdefg"
print(string.sub(str, 3))
--输出为Cdefg
print(string.sub(str, 3, 4))
--有重载方法。输出为Cd
3.6.6 字符串重复
str = "abCdefg"
print(string.rep(str, 2))
--输出为abCdefgabCdefg
3.6.7 字符串修改
- 第二个参数为修改的次数。
str = "abCdefg"
print(string.gsub(str, "Cd", "**"))
--输出为ab**efg 1
str = "abCdefgCd"
print(string.gsub(str, "Cd", "**"))
--输出结果为ab**efg** 2
3.6.8 字符转ASCII码
- 第二个参数为脚标。
a = string.byte("Lua", 1)
print(a)
--输出结果为76
3.6.9 ASCII码转字符
a = 76
print(string.char(a))
--输出结果为L
第4章 运算符
4.1 算数运算符
-
-
-
- / % ^
-
-
- 没有自增自减 ++ – –
- 没有复合运算符 += -= /= *= %=
- 字符串可以进行算数运算符操作,会自动转成number。
print("123.4" + 1)
--输出为124.4
- ^ Lua中该符号是幂运算。
print("幂运算" .. 2 ^ 5)
--输出为幂运算32
4.2 条件运算符
-
< >= <= == ~=
- 不等于是~=
4.3 逻辑运算符
- C#中:&& || !
- Lua中:and or not
- Lua中也遵循逻辑运算的 “短路” 规则。
4.4 位运算符
- 不支持位运算符 & |
- 需要我们自己实现
4.5 三目运算符
- 不支持三目运算符 ? :
第5章 条件分支语句
- Lua中没有switch语法,需要自己实现
5.1 if语句
- 单分支
- if 条件 then……end
a = 9
if a > 5 then
print("123")
end
- 双分支
- if 条件 then……else……end
a = 9
if a < 5 then
print("123")
else
print("321")
end
- 多分支
- if 条件 then……elseif 条件 then…….else……end
- elseif 一定要连这写,否则报错。
a = 9
if a < 5 then
print("123")
elseif a == 6 then
print("6")
end
第6章 循环
6.1 while语句
- while 条件 do……end
num = 0
while num < 5 do
print(num)
num = num + 1
end
6.2 do while 循环
- repeat……until 条件 (注意:条件是结束条件)
num = 0
repeat
print(num)
num = num + 1
until num > 5 -- 满足条件跳出 结束条件 C#为进入条件
6.3 for循环
for i = 1, 5 do --默认递增 i会默认+1
print(i)
end
- 如果想自定义增量 直接逗号后面写
for i = 1, 5, 2 do --如果想自定义增量 直接逗号后面写
print(i)
end
第7章 函数
7.1 无参数无返回值
- function 函数名()……end
--F1() 这里调用会报错 Lua要按自顶向下的顺序运行
function F1()
print("F1函数")
end
F1()
- 第二种写法。
- 有点类似 C#中的 委托和事件。
F2 = function()
print("F2函数")
end
F2()
7.2 有参数
- 不需要写参数类型。
- 如果你传入的参数和函数个数不匹配,不会报错只会补空nil或者丢弃。
function F3(a)
print(a)
end
F3(1)
F3("123")
F3(true)
--如果你传入的参数和函数个数不匹配,不会报错只会补空nil或者丢弃
F3() --默认nil
F3(1,2,3)
7.3 有返回值
- 无需声明返回类型
function F4(a)
return a
end
temp = F4(1)
- 多返回值时,在前面声明多个变量来接取即可。
- 如果变量不够,不影响,直接取对于位置的返回值。
- 如果变量多了,不影响,直接赋值nil。
function F4(a)
return a, "123", true
end
temp, temp2, temp3 = F4("1")
7.4 函数的类型
- 函数类型就是function
F5 = function()
print("123")
end
print(type(F5))
--输出为function
7.5 函数的重载(Lua不支持重载)
- 重载:函数名相同,参数类型不同或者参数个数不同。
- Lua中不支持重载,默认调用最后一个声明的函数。
7.6 变长参数
- 变长参数使用,用一个表存起来再用。
- 变长参数没有规定传入类型。
function F7(...)
arg = {...}
for i = 1, #arg do
print(arg[i])
end
end
F7(1, "123", true, 4, 5, 6)--没有规定类型。
7.7 函数嵌套
function F8()
F9 = function()--可简写,直接return,不用取名。
print(123)
end
return F9
end
f9 = F8()
f9()
--输出为123
- 函数闭包。
function F9(x)
--改变传入参数的生命周期
return function(y)
return x + y
end
end
f10 = F9(10)
print(f10(5))
--输出为15
第8章 复杂数据类型——表
- 所以的复杂类型都是table(表)
8.1 数组
- 可以传入任何类型。
- Lua中索引从1开始。
- #是通用的获取长度的关键字。
- 在打印长度的时候nil被忽略。
- 如果表(数组)中,某一位变成nil 会影响#获取的长度,遇到nil会中断后续的长度。
a = {1, 2, 3, 4, "1231", true, nil}
print(a[1])
--输出为1
print(#a)
--输出为6
8.2 数组的遍历
a = {1, 2, 3, 4, "1231", true, nil}
for i = 1,#a do
print(a[i])
end
8.3 二维数组
- 二维数组本质上都是基于表。
a = {{1, 2, 3}, {4, 5, 6}}
print(a[1][1])
8.4 二维数组的遍历
a = {{1, 2, 3}, {4, 5, 6}}
for i = 1, #a, do
b = a[i]
for j = 1, #b do
print(b[j])
end
end
8.5 自定义索引
- 自定义索引不算入长度。
- 一般不建议自定义索引。
aa = {[0] = 1, 2, 3, [-1] = 4, 5}
print(aa[0])
--输出为1
print(aa[-1])
--输出为4
print(#aa)
--长度为3 自定义索引不算入长度。
注意深坑:中间索引断了一个数就输出索引最大值(断了[3] = 3),中间断了2个数就不认后面的索引。
aa = {[1] = 1, [2] = 2, [4] = 4, [5] = 5}
print(#aa)
--输出为5
aa = {[1] = 1, [2] = 2, [5] = 5, [6] = 6}
print(#aa)
--输出为2
第9章 迭代器遍历
- 迭代器遍历,主要是用来遍历表的。
- #得到长度,其实并不准确,一般不用#来遍历表
9.1 ipairs迭代器
- ipairs遍历,还是从1开始往后遍历的,小雨等于0的值得不到
- 只能找到连续索引的键,如果中间断序了,它也无法遍历出后面的内容。
- 和#类似。
- 使用前提:保证表中都是连续的,否则使用pairs迭代器。
a = {[0] = 1, 2, [-1] = 3, 4, 5}
for i, k in ipairs(a) do
print("ipairs遍历键值"..i.."_"..k)
end
--输出为1_2 2_4 3_5
a = {[0] = 1, 2, [-1] = 3, 4, 5,[5] = 6}
for i, k in ipairs(a) do
print("ipairs遍历键值"..i.."_"..k)
end
--输出为1_2 2_4 3_5 4_6
9.2 pairs迭代器遍历
- 它能够把所有的键都找到,通过键可以得到值。
a = {[0] = 1, 2, [-1] = 3, 4, 5,[5] = 6}
for i, v in pairs(a) do
print("pairs遍历键值"..i.."_"..v)
end
--输出为1_2 2_4 3_5 0_1 -1_3 5_6
9.3 迭代器遍历键
a = {[0] = 1, 2, [-1] = 3, 4, 5,[5] = 6}
for i in pairs(a) do
print("pairs遍历键"..i)
end
--输出为1 2 3 0 -1 5
9.4 迭代器遍历——虚拟变量
- 一种特殊写法,本质上也是先获得键。
for _, v in pairs(a) do
print(v)
end
第10章 复杂数据类型——表2
10.1 table表实现字典
10.1.1 字典的声明
- 字典是由键值对构成。
a = {["name"] = "joker", ["age"] = 14, ["1"] = 5}
10.1.2 字典的访问
- 访问单个变量,用中括号填键来访问。
print(a["name"])
print(a["age"])
print(a["1"])
- 还可以类似 . (dot)成员变量的形式得到值。
- 虽然可以通过 . (dot) 成员变量的形式得到值,但是不能是数字。
print(a.name)
print(a.age)
print(a.1)
--报错
print(a["1"])
--正常
10.1.3 字典的修改
a["name"] = "TLS"
print(a["name"])
print(a.sex)
10.1.4 字典的增加
- 新增只需要加新的键。
a["sex"] = false
print(a["sex"])
print(a.sex)
10.1.5 字典的删除
- 赋nil值可以认为是删除。
a["sex"] = nil
print(a["sex"])
print(a.sex)
10.1.6 字典的遍历
- 如果要模拟字典遍历一定要用pairs。
- 可以传多个参数,一样可以打印出来。
for k, v inpairs(a) do
print(k,v)
end
--键值对同时遍历
for k in pairs(a) do
print(k)
print(a[k])
end
--只遍历其中一个
10.2 table表实现类和结构体
- Lua中是默认没有面向对象对,需要我们自己来实现。
10.2.1 类
- 成员变量 成员函数…
- C#要是使用类,实力化对象new,静态直接 . (dot)
- Lua中类的表现,更像是一个类中有很多静态变量和函数。
- 在表内部函数中调用表本身的属性或者方法,一定要指定是谁,使用表名 . 属性 或 表名 . 方法。
- 第二种能够在函数内部调用自己属性或者方法的 方法,把自己作为一个参数传进来在内部访问。
Student = {
--年龄
age = 1,
--函数
sex = true,
--成长函数
Up = function()
--这样写 这个age和表中的age没有任何关系,它是一个全局变量
--print(age)
print(Student.age)
print("我成长了")
end,
--学习函数
Learn = function(t)
--第二种方法
print(t.sex)
print("好好学习,天天向上")
end
}
10.2.2 类中方法的调用
print(Student.age)
Student.Up()
- Lua中 . 和 : 有区别。
- 冒号调用方法,会默认把调用者 作为第一个参数传入方法中。
Student.Learn(Student)
Student:Learn()
10.2.3 类中成员的增加
- 声明表过后,在表外取声明表有的变量和方法。
- 建议都使用第三种方法。
Student.name = "joker"
- 函数也可以这样。
Student.Speak = function()
print("说话")
end
- 第二种方法
function Student.Speak2()
peint("说话2")
end
- 第三种方法 使用冒号(: )声明
- Lua中,关键字 self 表示默认传入的第一个参数
function Student:Speak2()
print(self.name .. "说话")
10.3 表的公共操作
- 表中 table 提供的一些公共方法。
- 例子
t1 = { {age = 1, name = "123"}, {age = 2, name = "345"} }
t2 = { name = "joker", sex = true }
10.3.1 表的插入
- 把t2插入到t1。
table.insert(t1, t2)
10.3.2 表的删除
- remove方法 传表进去,会移除最后一个索引的内容。
table.remove(t1)
- remove方法 传两个参数,第一个参数是要移除内容的表,第二个参数是要移除内容的索引。
table.remove(t1, 1)
10.3.3 表的排序
- 传入要排序的表默认升序排序。
t3 = {5, 2, 7, 9, 5}
table.sort(t3)
for _, v in pairs(t2) do
print(v)
end
--输出2 5 5 7 9
- 传入两个参数,第一个是用于排序的表,第二个是排序规则函数。
- 这里是降序。
t3 = {5, 2, 7, 9, 5}
table.sort(t3, function(a, b)
if a > b then
return true
end
end)
for _, v in pairs(t2) do
print(v)
end
--输出9 7 5 5 2
10.3.4 表的拼接
- 连接函数concat用于拼接表中的元素,返回值是一个字符串。
- 一般只用来拼接字符串和数字。
tb = {"123", "456", "789", "10101"}
str = table.concat(tb, ",")
print(str)
--输出为123,456,789,10101
第11章 多脚本执行
11.1 全局变量和本地变量
11.1.1 全局变量
- 这些都是全局变量,包括在循环里面声明
a = 1
b = "123"
for i = 1, 2 do
c = "joker"
end
print(c)
--输出joker 仍然可以用
11.1.2 本地变量
- 本地(局部)变量大关键字 local。
- 只在对应语句块有作用
for i = 1, 2 do
local d = "joker"
end
print(d)
--输出为空
11.2 多脚本执行
- 关键字 require(“脚本名”😉 / require(‘脚本名’😉。
- 不同文件需要加上路径。
- 只要运行过就可以使用到其他脚本的全局变量,但是本地变量不可以。
require("Test")
print(testA)
--输出123
print(testLocalA)
--输出为空
--Test文件
print("Test测试")
testA = “123”
local testLocalA = "456"
- 如果是require加载执行的脚本,加载一次过后不会再被执行。
require("Test")
require('Test')
--只执行一次
11.3 脚本卸载
- 关键字package.loaded[“脚本名”]
- 返回值是boolean,意思是该脚本是否被执行。
print(package.loaded["Test"])
- 卸载以及执行过的脚本
package.loaded["Test"] = nil
11.4 大G表(_G表)
- _G表是一个总表(table)他将我们声明的所有全局的变量都存储在其中。
- 本地变量,加了local的变量是不会存到大_G表中的。
11.5 本地变量特殊调用
- require执行一个脚本时,可以在脚本最后返回一个外部希望获取的内容。
local testLA = require("Test")
print(testLA)
--输出为123
--Test文件
print("Test测试")
testA = “123”
local testLocalA = "456"
--return出去
return testLoaclA
第12章 特殊用法
12.1 多变量赋值
a, b, c = 1, 2, "123"
- 多变量赋值,如果后面的值不够,会自动补空。
a, b, c = 1, 2
--c为nil值
- 如果后面的值多了,会自动省略。
a, b, c = 1, 2, 3, 4, 5, 6
--456省略了
12.2 多返回值
- 多返回值时,用几个变量接,就有几个值。
- 如果少了,就少接几个,如果多了就自动补空。
function Test()
return 10, 20, 30, 40
end
12.3 and or
- 逻辑与 逻辑或。
- and or 他们不仅可以连接 boolean 任何东西都可以用来连接。
- 在Lua中,只有nil和false才认为是假。
- “短路”对于and来说,有假则假,对于or来说,有真则真。
- 所以他们只需要判断第一个是否满足,就会停止计算了。
print(1 and 2)
--输出2
print(0 and 1)
--输出1
print(nil and 1)
--输出nil
print(false and 2)
--输出false
print(true and 3)
--输出3
print(true or 1)
--输出true
print(false or 1)
--输出1
print(nil or 2)
--输出2
12.3.1 三目运算符
- Lua不支持三目运算符。
- 运用“短路”实现? : 。
x = 3
y = 2
local res = (x > y) and x or y
print(res)
--(x > y) and x ——> x
--x or y ——> x
x = 1
y = 2
local res = (x > y) and x or y
print(res)
--(x > y) and x ——> (x > y)
--(x > y) or y ——> y
第13章 协同程序
13.1 协程的创建
13.1.1 create创建(常用方式)
- create创建协程的本质是一个thread(线程)对象。
- coroutine.create()
fun = function()
print(123)
end
co = coroutine.create(fun)
13.1.2 wrap创建
- wrap创建协程的本质是一个function对象。
- coroutine.wrap
co2 = coroutine.wrap(fun)
13.2 协程的运行
13.2.1 resume运行
- 第一种方式对对应的是通过create创建的协程。
- coroutine.resume()
coroutine.resume(co)
13.2.2 fun运行
- 第二种方式对应的是通过wrap创建的协程。
- 因为返回的是函数,所以直接当成函数调用就行。
co2()
13.3 协程的挂起
fun2 = function()
while true do
print(123)
--协程的挂起函数
coroutine.yield()
end
end
co3 = coroutine.create(fun2);
--每启动一次只会启动一次
coroutine.resume(co3)
- yield可以有返回值。
- 也可以有多个返回值。
- 用resume运行。默认第一个返回值是协程是否启动成功。
fun2 = function()
local i = 1
while true do
print(i)
i = i + 1
coroutine.yield(i)
end
end
co3 = coroutine.create(fun2);
isOk, tempI = coroutine.resume(c03)
--输出true 1
- 用fun调用,没有默认返回值,直接返回返回值。
co4 = coroutine.wrap(fun2)
print("返回值" .. co4())
13.4 协程的状态
- coroutine.status(协程对象)
- dead 结束(没有挂起)
- suspended 暂停(挂起了)
- running 进行中
- 这个函数可以得到当前正在运行的协程的线程号。
coroutine.running()
第14章 元表
14.1 元表概念
- 任何表变量都可以作为另一个表变量的元表。
- 任何表变量都可以有直接的元表(爸爸)。
- 当我们子表中进行一些特定操作时,会执行元表中的内容。
14.2 设置元表
- setmetatable(子表, 元表)
meta = {}
myTable = {}
setmetatable(myTable, meta)
14.3 特定操作
14.3.1 特定操作-__tostring
- 当子表要被当做字符串使用时,会默认调用这个元表中的__tostring方法。
meta2 = {
__tostring = function()
return "joker"
end
}
myTable2 = {
}
setmetatable(myTable2, meta2)
print(myTable2)
--输出为joker
- 元表调用子表的内容
meta2 = {
--参数t默认把子表传入
__tostring = function(t)
return t.name
end
}
myTable2 = {
name = "joker2"
}
setmetatable(myTable2, meta2)
print(myTable2)
--输出为joker2
14.3.2 特定操作-__call
- 当子表被当做一个函数来使用时,会默认调用这个__call中的内容。
- 只有设置了元表和有__call函数才能够这样调用。
meta3 = {
__call = function()
print("MkLeo")
end
}
myTable3 = {
name = "Tweek"
}
setmetatable(myTable3, meta3)
myTable3()
--输出为MkLeo
- __call的参数,第一个参数是表自己,第二个参数才是1。
- a相当于myTable3本身,然后调用了自己的__tostring方法。有点像 “:”调用。
meta3 = {
__tostring = function(t)
return t.name
end,
__call = function(a, b)
print(a)
print(b)
print("MkLeo")
end
}
myTable3 = {
name = "Tweek"
}
setmetatable(myTable3, meta3)
myTable3(1)
--输出为Tweek 1 MkLeo
14.3.3 特定操作-运算符重载
- __add相当于运算符重载,当子表使用+运算符时,会调用该方法。
- 使用条件运算符来比较两个对象,这两个对象的元表一定要一致才能准确调用方法。
- 其他运算符同理。
- +运算符 __add
- -运算符 __sub
- *运算符 __mul
- /运算符 __div
- %运算符 __mod
- ^运算符 __pow
- ==运算符 __eq 返回boolean
- <运算符 __lt 返回boolean
- <=运算符 __le 返回boolean
- ..运算符 __concat
meta4 = {
__add = function(t1, t2)
return 5
end
}
myTable4 = {}
setmetatable(myTable4, meta4)
myTable5 = {}
print(myTable4 + myTable5)
--输出5
- 有参数时
meta4 = {
__add = function(t1, t2)
return t1.age + t2.age
end
}
myTable4 = {age = 1}
setmetatable(myTable4, meta4)
myTable5 = {age = 2}
print(myTable4 + myTable5)
--输出3
14.3.4 特定操作-__index和__newIndex
- __index 当子表中找不到某个属性时,会到元表中 __index指定的表去找属性。
- __index的赋值,写在表外面来初始化(写里面有坑)。
meta6 = {
age = 1
--元表指定的表
meta6.__index = {age = 2}
--下面这种写法是坑,为nil值
--meta6.__index = meta6
}
--这样也是可以的
--meta6.__index = meta6
myTable6 = {}
setmetatable(myTable6, meta6)
print(myTable6.age)
--输出为1
- __index的嵌套
meta6Father = {
age = 1
}
meta6Father.__index = meta6Father
meta6 = {}
meta6.__index = meta6
myTable6 = {}
setmetatable(meta6, meta6Father)
setmetatable(myTable6, meta6)
14.3.4 特定操作-__newIndex
- __newIndex 当赋值时,如果赋值一个不存在的索引,那么会把这个值赋值到newIndex所指的表中,不会修改自己。
meta7 = {}
myTable7 = {}
setmetatable(myTable7, meta7)
myTable7.age = 1
print(myTable7.age)
--输出为nil
14.4 获取元表
- getmetatable(子表)
- 得到元表的方法。
14.5 rawget方法
- rawget当我们使用它时,会去找自己身上有没有这个变量,且只能找自己。
--在元表找
print(myTable.age)
--只找自己
print(rawget(myTable6, "age"))
14.6 rawset方法
- rawset该方法会忽略__newIndex的设置,只会改自己的变量。
rawset(myTable7, "age", 2)
--输出2
第15章 Lua面向对象
15.1 封装
- 面向对象 类 其实都是基于 table来实现。
- 元表相关的知识点。
- 下面例子是用Lua模拟C#的new对象。
Object = {}
Object.id = 1
function Object:Test()
print(self.id)
end
--冒号 是会自动将调用这个函数的对象作为第一个参数传入的写法
function Object:new()
--self代表的是 我们默认传入的第一个参数
--对象就是变量 返回一个新的变量
--返回出去的内容 本质上就是表对象
local obj = {}
--元表知识 __index 当找自己的变量找不到时就会去找元表中__index指向的内容
self.__index = self
setmetatable(obj, self)
return obj
end
- 调用上面代码测试。
local myObj = Object:new()
print(myObject.id)
--输出为1
myObject:Test()
--输出为1
--对空表中 声明一个新的属性 叫做id 并没有修改Object的内容
myObject.id = 2
print(Object.id)
--输出为1
myObject.id = 2
myObject:Test()
--输出为2 相当于把自己传入了Test()
15.2 继承
- C# class 类名 : 继承类
- _G知识点补充
_G["a"] = 1
_G.b = "123"
- 写一个用于继承的方法
Object = {}
Object.id = 1
function Object:subClass(className)
-- _G是总表,所有声明的全局变量都以键值对的形式存在其中
_G[className] = {}
--写相关继承的规则
--用到元表
local obj = _G[className]
self.__index = self
setmetatable(obj, self)
end
- 调用
- p1元表是Person Person元表是Object
- 顺序:p1找元表的__index,找到Person自己,Person再找元表的__index,找到Object自己。
Object:subclasee("Person")
local p1 = Person:new()
print(p1.id)
--输出为1
15.3 多态
- 相同行为,不同表像,就是多态。
- 相同方法,不同执行逻辑,就是多态。
Object:subClass("GameObject")
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
- 构建base
Object = {}
function Object:subClass(className)
_G[className] = {}
local obj = _G[className]
self.__index = self
--子类定义一个base属性代表父类
obj.base = self
setmetatable(obj, self)
end
- 调用 (注意有坑)
GameObject:subClass("Player")
function Player:Move()
--坑的主要问题所在
--这个base指的是 GameObject表
--这种方式调用相当于是把基类表作为第一个参数传入了方法中
--避免把基类表 传入到方法中 这样相当于就是公用一张表的属性了
self.base:Move()
end
local p1 = Player:new()
p1.Move()
--输出1 1
--目前这种写法有坑,不同对象使用的成员变量是相同的成员变量,不是自己的
local p2 = Player:new()
p2:Move()
--输出2 2 违背了面向对象
p1:Move()
--输出3 3 违背了面向对象
- 改进方法self.base.Move(self)
GameObject:subClass("Player")
function Player:Move()
--我们要执行父类逻辑,我们不要直接使用冒号调用
--要通过 . 调用,然后自己传入第一个参数
self.base.Move(self)
end
15.4 面向对象实现
- 万物之父 所有对象的基类 Object
Object = {}
--实力化方法
function Object:new()
local obj = {}
--给空对象设置元表,以及 __index
self.__index = self
setmetatable(obj, self)
end
--继承
function Object:subClass(className)
--根据名字生成一张表 就是一个类
_G[className] = {}
local obj = _G[className]
--设置自己的"父类"
obj.base = self
--给子类设置元表 以及__index
self.__index = self
setmetatable(obj, self)
end
- 调用测试
--声明一个新的类
Object;subClass("GameObject")
--成员变量
GameObject.posX = 0
GameObject.posY = 0
--成员方法
function GameObject:Move()
self.posX = self.posX + 1
self.posY = self.posY + 1
end
--实力话对象使用
local obj = GameObject:new()
print(obj.posX)
obj:Move()
print(obj.posX)
local.obj2 = GameObject:new()
print(obj2.posX)
obj2:Move()
print(obj2.posX)
--声明一个心得类 Player继承Object
GameObject:subclass("Player")
--多态 重写类GameObject的Move方法
function Player:Move()
--调用父类方法 用 . 自己传第一个参数
self.base.Move(self)
end
--实力化Player对象
local p1 = Player:new()
print(p1.posX)
p1:Move()
print(p1.posX)
local p2 = Player:new()
print(p2.posX)
p2:Move()
print(p2.posX)
第16章 自带库
16.1 时间相关
- 系统时间
print(os.time())
- 自己传入参数 得到时间
print(os.time({year = 2014, month = 8, day = 14}))
- os.data(“*t”😉
loacl nowTime = os.data("*t")
for k, v in pairs(nowTime) do
print(k, v)
end
print(nowTime.hour)
16.2 数学运算相关
- 绝对值
print(math.abs(-11))
- 弧度转角度
print(math.deg(math.pi))
- 三角函数 传弧度
print(math.cos(math.pi))
- 向下向上取整
print(math.floor(2.6))
print(math.ceil(5.2))
- 最大最小值
print(math.max(1, 2))
print(math.min(4, 5))
- 小数分离 分成整数部分和小数部分
print(math.modf(1.2))
- 幂运算
print(math.pow(2, 5))
- 随机数
- 先设置随机数种子
math.randomseed(os.time())
print(math.random(100))
- 开方
print(math.sqrt(4))
16.3 路径相关
- Lua脚本加载路径
print(package.path)
package.path = package.path .. ";C:\\\\"
print(package.path)
第17章 Lua垃圾回收
17.1 垃圾回收关键字collectgarbage
- 获取当前Lua占用内存数,K字节,用返回值*1024就可以得到具体的内存占用字节数。
test = {id = 1, name = "123123"}
print(collectgarbage("count"))
- 进行垃圾回收 理解有点像C#的GC。
- Lua中的机制和C#中垃圾回收机制很类似 解除羁绊 就是变垃圾。
collectgarbage("collect")
- Lua中有自动定时进行GC的方法。
- Unity中热更新开发,尽量不要去用自带垃圾回收。
本文来自投稿,不代表Crestruction立场,如若转载,请注明出处:https://crestruction.org/archives/c-tech/1110