教程-【唐老师】Unity热更新之Lua语法
环境搭建以及helloworld
第一个程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
print("Hello World!")
|
变量
空 nil、 数值 number、字符串 string、布尔 boolean
函数 function
表 table
数据结构 userdata
协同程序 thread(线程)
lua中所有的变量都不需要声明变量类型,类型C#的var
lua中的变量可以随便赋值,自动识别类型//我理解为弱类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| a = nil print(a)
a = 1.2 print(a)
a = "helloworld" print(a)
a = false print(false)
|
通过type可以获得变量类型
type函数也有返回值,是string类型的
使用没有声明过的变量,不会报错,默认值为空
字符串操作
1 2 3
| a = "双引号字符串" b = '单引号字符串'
|
取字符串长度,中文在lua中占3个长度
1 2 3 4 5 6
| s = "aBcDeF" print(#s)
s = "aBcDeF字符串" print(#s)
|
字符串多行打印
1 2 3 4 5 6 7
| print("123\n123")
s = [[123 123 123 ]] print(s)
|
字符串拼接
1 2 3 4 5 6 7
| print("123".."456")
s1 = "123" s2 = 456
print(s1..s2)
|
占位符
1 2 3 4 5 6 7 8
| print(string.format("我叫紫地丁,我%d岁了",3))
|
其他类型转换到string
1 2
| a = true print(tostring(a))
|
字符串提供的公共方法
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
| str = "aBcDeF" print(string.upper(str))
print(string.lower(str))
大部分字符串操作不会改变原字符串 print(str)
print(string.reverse(str))
print(string.find(str,"cDe"))
print(string.sub(str,3,4))
print(string.rep(str,2))
print(string.gsub(str,"cD","**"))
a = print(string.byte("Lua"),1)
print(string.char(a))
|
运算符
算数运算符
还是那么些个+-*/%,但没有++,–,也没有复合运算符+=,-=等
还有C#没有的幂运算符^
string可以进行算数操作,会自动转成number
条件运算符
还是那么些个>,<,==,>=,<=
以及有点不一样的不等于~=,即不等于
逻辑运算符
与and,或or ,非not
Lua支持逻辑运算的短路规则
1 2 3 4 5
| print(false and print("123"))
print(true and print("123"))
|
条件分支语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| a = 9 if a>5 then print("啊对对对") else print("不对不对不对") end
if a<5 then print("1234") elseif a ==5 then print("555") else print ("67890") end
|
循环语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| num = 0 while num<5 do num = num+1 print(num) end
repeat num=num-1 print(num) until num<0
for i=1,5 do print(i) end
for i = 5,1,-1 do print(i) end
|
函数
无参数无返回
1 2 3 4 5 6 7 8 9 10 11 12 13
| function f1() print("f1") end
f2 = function() print("f2") end
f1()
f2()
|
有参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function f3(a) print(a) end
f3(2)
f3("123")
f3(true)
f3()
f3(1,2,3)
|
有返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function f4(a) return a end
function f5(a) return a,"456",true end
print(f4("123"))
print(f5("123"))
a,b,c = f5("123") print(a) print(b) print(c)
|
函数是一个变量类型
函数重载
- Lua中不支持函数重载,默认调用最后一个声明的函数
变长参数
需要先用一个表将变长参数存起来,再使用变长参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function f6(...) arg = {...} for i =1,#arg do print(arg[i]) end end
f6(1,2,true,"4",5)
|
函数嵌套
1 2 3 4 5 6 7 8 9 10
| function f7() f8 = function() print(123) end return f8 end
f88 = f7() f88()
|
Lua中闭包的体现:通过函数嵌套改变参数的生命周期
1 2 3 4 5 6 7 8 9
| function f9(x) return function(y) return x+y end end
f10 = f9(10) print(f10(20))
|
闭包函数:声明在一个函数中的函数,叫做闭包函数。
闭包:内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。
闭包的特点:
让外部访问函数内部变量成为可能;
局部变量会常驻在内存中;
可以避免使用全局变量,防止全局变量污染;
会造成内存泄漏(有一块内存空间被长期占用,而不被释放)
table实现数组
数组
声明数组
1
| a = {1,2,"3",true,5,nil}
|
取用数组元素
1 2 3 4 5
| print(a[0])
print(a[1])
|
获取数组长度
1 2 3 4 5 6 7 8 9
| print(#a)
b = {1,1,nil,1,1} print(#b)
|
遍历数组
1 2 3 4
| for i = 1, #a do print(a[i]) end
|
二维数组
1 2 3 4 5
| a = {{1,2,3},{4,5,6}} print(a[1][1])
print(a[2][2])
|
遍历
1 2 3 4 5 6
| 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 8 9 10 11 12 13 14 15 16
| aa = {[0] = 1,2,3,[-1]=4,5} print(aa[0])
print(#aa)
aa = {[1]=1,[2]=2,[4]=4,[5]=5} print(#aa)
aa = {[1]=1,[2]=2,[5]=5} print(#aa)
|
迭代器遍历
迭代器遍历,主要是用来遍历表的
用#得到表的长度可能不准确,所以常用迭代器遍历表
ipairs
1 2 3 4 5 6 7 8 9 10 11 12 13
| a = {[0]=1,2,[-1]=3,4,5}
for i,k in ipairs(a) do print(i..k) end
|
pairs
1 2 3 4 5 6 7 8 9 10 11
| for i,v in pairs(a) do print(i..v) end
|
table实现字典
字典的声明和使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| a = {["name"]="紫地丁",["age"]=3,["1"]=1}
print(a[name])
print(a.age)
a["sex"] = false print(a.sex)
a.age = 4; print(a.age)
|
字典的遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| for k,v in pairs(a) do print(k,v) end
for k in pairs(a) do print(k) print(a[k]) end
for _,v in paris(a) do print(v) end
|
table实现类
Lua中是默认没有面向对象的,需要我们自己去实现
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 47 48 49 50 51
| Student = { age = 3, name = "紫地丁", Up = function() print("我成长了") end, Learn = function(t) print(t.name) print("好好学习,天天向上") end }
Student.Up()
Student.sex = false print(Student.sex)
function Student.Speak() print("说话") end Student.Speak()
print(age)
Student.Learn(Student)
Student:Learn()
function Student:Speak2() print(self.name.."说话") end Student:Speak2()
|
table的公共操作
1 2
| t1 = {{age=1,name = "123"},{age = 2,name = "345"}} t2 = {name = "紫地丁",sex = true}
|
插入
1 2 3 4 5 6 7
| print(#t1)
table.insert(t1,t2) print(#t1)
print(t1[3].sex)
|
删除指定元素
1 2 3 4 5 6 7 8 9 10 11 12
| table.remove(t1) print(#t1)
print(t1[1].name)
print(t1[3])
table.remove(t1,1) print(t1[1].name)
|
排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| t2= {5,2,7,9,5} table.sort(t2) for _,v in pairs(t2) do print(v) end
table.sort(t2,function(a,b) if a>b then return true end end) for _,v in pairs(t2) do print(v) end
|
拼接
1 2 3 4
| tb = {"123","456","789"} a = table.concat(tb,";") print(a)
|
多Lua脚本的执行
全局变量和本地变量
我们之前所声明的变量大多是全局变量
如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| a = 1 b = "2"
for i = 1,2 do c = 3 end
print(c)
fun = function() tt = "123" end print(tt)
fun() print(tt)
|
用local关键字声明局部变量
1 2 3 4 5 6 7 8 9
| for i = 1,2 do local d = "3" end print(d)
local tt2 = "555" print(tt2)
|
多脚本执行
使用require关键字实现多脚本执行
test1.lua
1 2 3 4
| testA = "123" local testB = "456" print("test1")
|
test2.lua
1 2 3 4 5 6 7 8
|
require("test1") print(testA)
print(testB)
|
require也可以接收一个脚本的返回值
1 2 3
| print("test3") return 123;
|
1 2 3 4
| a = require("test3") print(a)
|
脚本的卸载
再次使用require
test1不会再执行
也就是说,执行过的脚本不会再执行
通过package.loaded[]判断脚本是否执行过
1 2
| print(package.loaded["test1"])
|
也可以通过package.loaded卸载脚本
1 2 3
| package.loaded["test1"] = nil print(package.loaded["test1"])
|
但是全局变量testA还在,唐老师没有讲这件事
大G表
xxxxxxxxxx4 1local testFun2 = xlua.get_generic_method(CS.Lesson12,”TestFun2”)2local testFun2_R = testFun2(CS.System.Int32)3–第一个参数传调用函数的对象,静态方法不用传4testFun2_R(obj,1)lua
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 47
| for k,v in pairs(_G) do print(k,v) end
|
加了local的变量,也就是本地变量不会存到大G表中
可以在大G表中存储键值对
1 2 3 4 5 6
| _G["a"] = 1 _G.b = 123 print(a)
print(b)
|
特殊用法
多变量赋值
1 2 3 4 5 6 7 8 9 10 11 12 13
| a,b,c = 1,2 print(a)
print(b)
print(c)
a,b = 3,4,5 print(a)
print(b)
|
多返回值
1 2 3 4
| function Test() return 10,20,30,40 end a,b,c = Test()
|
逻辑与或
and和or不仅可以连接boolean,任何东西都可以用来连接
在lua中,只有nil和false才认为是假
在不同变量的连接中,依然遵守短路规则
1 2 3 4 5 6 7 8
| print(4 and 2)
print(nil and 1)
print(true or 1)
print(false or 2)
|
可以用这种性质模拟三目运算
1 2 3 4
| x = 1 y = 2 biger = (x>y) and x or y print(biger)
|
协同程序
协程的创建
1 2 3 4 5 6 7 8 9 10 11 12
| fun = function() print(123) end
co = coroutine.create(fun)
print(type(co))
co2 = coroutine.wrap(fun) print(type(co2))
|
协程的运行
协程的创建方式不同,运行方式就不同
1 2 3 4 5
| coroutine.resume(co)
co2()
|
协程的挂起
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
| fun2 = function() local i =1 while true do print(i) i = i+1 coroutine.yield(i) end end
co3 = coroutine.create(fun2) coroutine.resume(co3) coroutine.resume(co3)
isok,temp = coroutine.resume(co3)
print(isok,temp)
co4 = coroutine.wrap(fun2) temp = co4()
print(temp)
temp = co4()
print(temp)
|
协程的状态
协程有三种状态:dead、suspended、running
1 2 3 4 5 6 7 8 9 10
| print(coroutine.status(co3))
print(co)
co4 = coroutine.create(function() print(coroutine.status(co4)) end) coroutine.resume(co4)
|
可通过coroutine.running得到正在运行的线程号
1 2 3 4 5 6
| co5 = coroutine.create(function() print(coroutine.running()) end) coroutine.resume(co5)
|
元表
元表概念
任何表变量都可以作为另一个表变量的元表,任何表变量都可以有自己的元表(爸爸)
当我们在有元表的表中进行一些操作时,会执行元表的内容
设置元表
1 2 3 4 5
| meta = {} myTable = {}
setmetatable(myTable,meta)
|
特定操作-tostring
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
| meta2 = { __tostring = function() return "我是中国人" end } print(meta2)
myTable2 = {} setmetatable(myTable2,meta2) print(myTable2)
meta3 = { __tostring = function(t) return t.name.."是中国人" end, __call = function(a) print(a) print(",紫地丁今年3岁了") end } myTable3 = { name = "紫地丁" } setmetatable(myTable3,meta3) print(myTable3)
|
特定操作-call
特定操作-运算符重载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| meta4 = { __add = function(t1,t2) return t1.age+t2.age end } myTable4 = {age = 1} setmetatable(myTable4,meta4) myTable5 = {age = 2} print(myTable+myTable)
其他运算符的关键字: + add - sub * mul / div % mod ^ pow == eq < lt <= le .. concat
如果要用条件运算符比较两个对象,这两个对象的元表要一致才能准确调用方法
|
特定操作-index和newIndex
当子表中找不到某一个属性时,会到元表中index指定的表去找索引
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| meta5 = {}
meta5.__index = {age = 1} myTable5 = {} setmetatable(myTable5,meta5)
print(myTable5.age)
meta6father= {} meta6father.__index = {age = 2} meta6 = {} meta6.__index = meta6 myTable6 = {} setmetatable(myTable6,meta6) setmetatable(meta6,meta6father) print(myTable6.age)
|
可以用rewget方法取消index的作用,找表自己的变量
1 2
| print(rawget(myTable6),"age")
|
当赋值时,如果赋值一个不存在的索引,那么会把这个值赋值到newindex所指的表中,不会修改自己
1 2 3 4 5 6 7 8 9 10 11 12
| meta7 = {} myTable7 = {} meta7.__newindex = {} setmetatable(myTable7,meta7) myTable7.age = 1 print(myTable7.age)
print(meta7.__newindex.age)
|
可以用rewset方法取消newindex的作用
1 2 3
| rawset(myTable7,"age",2) print(myTable.age)
|
获取元表
1 2
| print(getmetatable(myTable6))
|
Lua面向对象之封装
面向对象 类 都是基于table来实现的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Object = {} Object.id = 1
function Object:new() local obj = {} setmetatable(obj,self) self.__index = self return obj end
local myObj = Object:new() print(myObj.id)
|
Lua面向对象之继承
实现一个用于继承的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| function Object:subClass(className) _G[className] = {} local obj = _G[className] self.__index = self obj.base = self setmetatable(obj,self) end
Object:subClass("Person") local p1 = Person:new() print(p1.id)
p1.id = 100 print(p1.id)
local p2 = Person:new() print(p2.id)
|
Lua面向对象之多态
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
| 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 GameObject:subClass("Player") local p1 = Player:new() p1:Move()
function Player:Move() self.base:Move() print("移动") end p1:Move()
|
目前这种写法有很大问题
1 2 3 4 5
| local p2 = Player:new() p2:Move()
|
改写Player的Move方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function Player:Move() self.base.Move(self) print("移动") end p1:Move() p2:Move()
|
面向对象汇总
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 47 48 49 50 51 52 53 54 55 56 57
|
Object = {}
function Object:new() local obj = {} self.__index = self setmetatable(obj,self) return obj end
function Object:subClass(className) _G[className] = {} local obj = _G[className] obj.base = self 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)
GameObject:subClass("Player") function Player:Move() self.base.Move(self) print("移动") end local p1 = Player:new()
print(p1.posX) p1:Move() print(p1.posX)
|
Lua自带库
时间
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
| print(os.time)
print(os.time({year = 2014, month = 8,day = 14}))
local nowTime = os.date("*t") print(nowTime) for k,v in pairs(nowTime) do print(k,v) end print(nowTime.hour)
|
数学运算
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
| print(math.abs(-11))
print(math.deg(math.pi))
prit(math.cos(math.pi))
print(math.floor(23.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.random(200))
print(math.sqrt(4))
|
其他
1 2 3 4 5 6 7 8 9 10 11 12
| print(package.path)
package.path = package.path ..";C:\\" print(package.path)
|
垃圾回收
垃圾回收关键字:collectgarbage
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| print(collectgarbage("count"))
collectgarbage("collect") print(collectgarbage("count"))
|
Unity热更新开发中,尽量不要使用自动垃圾回收