Unity Lua教程

一、环境搭建

1、首先安装LuaForWindows,官网地址:https://github.com/rjpcomputing/luaforwindows/releases
2、安装Sublime Text,官网地址:https://www.sublimetext.com/download
3、如果大家下载不了的话,可以通过我提供的百度网盘进行下载:
链接:https://pan.baidu.com/s/1ojhEqgUoEtozdS3itTY1_Q
提取码:nw68

二、基本语法(lua语句 可以省略分号)

1、打印函数

print("hello world")

2、注释

--单行注释

--[[
第一种多行注释
]]

--[[
第二种多行注释
]]--

--[[
第三种多行注释
--]]

3、关键字
关键字
4、数据类型
数据类型
5、查看变量的类型

print(type(a))
--type的返回值为string类型

--lua中使用没有声明过的变量,不会报错,默认值是nil

6、字符串

s="521一生一世"
print(#s)  --获取字符串的长度,一个汉字占3个长度,英文字符占一个长度

--字符串多行打印
print("123\n123")
s=[[
521
1314
]]
print(s)

--字符串拼接
print("123".."456")  --通过两个..
print(string.format("小明今年%d岁了",18)
--%d:与数字拼接
--%a:与任何字符拼接
--%s:与字符配对

--别的类型转字符串
a=18
print(tostring(a))

--字符串提供的公共方法
str="aBcDe"
print(string.upper(str))  --小写转大写的方法
print(string.lower(str))  --大写转小写的方法
print(string.reverse(str))  --翻转字符串
print(string.find(str,"cDe"))  --字符串索引查找,Lua中索引下标从1开始
print(string.sub(str,3))  --截取字符串
print(string.rep(str,2))  --字符串重复
print(string.gsub(str,"cD","**"))  --字符串修改

a=string.byte("Lua",1)  --给指定位置的字符转成ASCII码
print(a)
print(striing.char(a))  --ASCII码转字符

7、运算符

--算术运算符
	-- + - * / % ^
	-- 没有自增自减 ++ --
	-- 没有复合运算符 += -= /= *= %=
	-- 字符串可以进行算术运算符操作,会自动转成number
	print("123"+1)  --结果为:124
	print("幂运算:"..2^5)

--条件运算符
	-- > < >= <= == ~=(不等于)

--逻辑运算符
	--and--or--not-- Lua中支持“短路”,只要第一个条件成立,后面的语句就不执行

--位运算符
	--Lua中不支持位运算

--三目运算符
	--Lua中不支持三目运算符

8、条件分支语句

--单分支
if 条件 then
	...
end

--双分支
if 条件 then
	...
else
	...
end

--多分支
if 条件 then
	...
elseif 条件 then  --elseif是连着写的,要不然会报错
	...
else
	...
end

--Lua中没有Switch语法,需要自己实现

9、循环语句

--while语句
while 条件 do
	...
end

--do while语句
repeat
	...
until 条件 --结束条件,满足条件退出

--for语句
for i=1,5 do  --1开始到5结束,i默认递增+1,如果要自定义增量,要这样写:i=1,5,2
	...
end

10、函数

--无参数无返回值,两种方法
function F1()  --一个在function后面取名字
	...
end

F2 = function()  --一个不取名字,用一个变量来存储
	...
end

--有参数,如果传入的参数和函数参数个数不匹配,不会报错,只会补空nil或者丢弃
function F3(a)
	...
end

--有一个返回值
function F4(a)
	return a
end

--有多个返回值,需要申明多个变量来接取,接少接多不影响,少了丢弃,多了为nil
function F5(a)
	return a,b,c
end

--函数类型就是function

--不支持函数的重载

--变长参数
function F6(...)
	arg = {
    
    ...}
	for i=1,#arg do
		print(arg[i])
	end
end
F6(1,"123",true,456)

--函数嵌套,函数里面申明函数
function F7()
	return function()
		print(123)
	end
end
F8 = F7()
F8()

--闭包,改变传入参数的生命周期
function F9(x)
	return function(y)
		return x + y
	end
end
F10 = F9(10)
print(F10(5))  --15

11、复杂数据类型table

--所有的复杂类型都是table

--数组的声明和使用
a = {
    
    1,2,"123",true,nil,"456"}
print(a[1])  --Lua中索引从1开始
print(#a)  --获取数组的长度,#是通用的获取长度的关键字,在打印长度的时候,第一个空后面的内容会被忽略

--数组的遍历
for i=1,#a do
	print(a[i])
end

--二维数组
b = {
    
    {
    
    1,2,3},{
    
    4,5,6}}
print(b[1][2])

--二维数组的遍历
for i=1,#b do
	c=b[i]
	for j=1,#c do
		print(c[j])
	end
end

--自定义索引
aa={
    
    [0]=1,2,3,[-1]=4}  --#aa打印结果为2,忽略小于等于0的索引
bb={
    
    [1]=1,[2]=2,[4]=4,[6]=6}  --有坑,#bb打印的结果为6,索引之间可以跳一个,缺的补nil
cc={
    
    [1]=1,[2]=2,[5]=5,[6]=6}  --#cc打印结果为2

--迭代器遍历,主要是用来遍历表的,一般不要用#来遍历表
a={
    
    [0]=1,2,[-1]=3,4,5,[5]=6}

--ipairs遍历,还是从1开始往后遍历的,小于等于0的值得不到
--只能找到连续索引的键,如果中间断序了,它也无法遍历出后面的内容
for i,k in ipairs(a) do
	print(i.."_"..k)  --1_2 2_4 3_5
end

--pairs它能够把所有的键都找到,通过键可以得到值
for i,v in pairs(a) do
	print(i.."_"..v)  --1_2 2_4 3_5 0_1 -1_3 5_6
end

--只遍历键
for i in pairs(a) do
	print(i)
end

12、字典

--字典的声明,字典是由键值对构成
a={
    
    ["name"]="张三",["age"]=18,["sex"]="男"}
print(a["name"])  --访问
print(a.name)  --虽然可以通过.成员变量的形式得到值,但是不能是数字
a["name"]="李四"  --修改
a["score"]=100  --新增
a["score"]=nil  --删除

--字典的遍历
for k,v in pairs(a) do
	print(k,v)
end

13、类和结构体

--Lua中默认是没有面向对象的,需要我们自己来实现

--类的声明,成员变量,成员函数
Student = {
    
    
	age=18,
	sex=true,
	Up=function()
		--这样写 这个age和表中的age没有任何关系 它是一个全局变量
		--print(age)

		--想要在表的内部函数中 调用表本身的属性或者方法
		--一定要指定是谁的 所以要使用 表名.属性 或者 表名.方法
		print(Studnet.age)

		print("Grow up!");
	end
}
print(Student.age)  --调用
Student.Up()

--申明表过后,在表外去申明表有的变量和方法
Student.name="张三"

Student.Learn=function(t)
	--第二种 能够在函数内部调用自己属性或者方法的 方法
	--把自己作为一个参数传进来 在内部 访问
	print(t.sex)
	
	print("好好学习,天天向上")
end
Student.Learn(Student)
Student:Learn()  --冒号调用方法 会默认把调用者 作为第一个参数传入方法中

function Student:Speak()  --冒号声明函数
	--Lua中 有一个关键字 self 表示默认传入的第一个参数
	print(self.name.."说话")
end	

14、表的公共操作

t1={
    
    {
    
    age=1,name="123"},{
    
    age=2,name="345"}}
t2={
    
    name="张三",sex=true}

--将t2插入t1
table.insert(t1,t2)

--移除最后一个索引的内容
table.remove(t1)

--移除指定位置的内容
table.remove(t1,1)

t3={
    
    4,8,1,3,6,9}
table.sort(t3)  --升序排序

table.sort(t3,function(a,b)
	if a>b then
		return true
	end
end)  --降序排序

--拼接
tb={
    
    "123","456","789"}
table.concat(tb,",")

15、多脚本执行

--全局变量和本地变量
--本地变量就是在变量前加local关键字
--不加local关键字的都是全局变量

--多脚本执行语法:require("脚本名")require('脚本名')
--如果是require加载执行的脚本 加载一次过后不会再被执行

--脚本卸载
--package.loaded["脚本名"]  --返回值是boolean 意思是 该脚本是否被执行
package.loaded["脚本名"]=nil  --卸载已经执行过的脚本

--大G表:是一个总表 它将我们申明的所有全局的变量都存储在其中
--写法:_G
_G["a"] = 1  --大G表中声明
print(a)

--如果想在另外一个脚本中访问该脚本中的本地变量,可以通过return返回,在另一个脚本中用变量接收即可

16、特殊用法

--多变量赋值,如果后面的值不够,会自动补空;如果后面的值多了,会自动省略
a,b,c=1,2,3

--多返回值
function Test()
	return 10,20,30,40
end
a,b,c=Test()  --多返回值时 你用几个变量接 就有几个值 如果少了 就少接几个 如果多了 就自动补空

--逻辑与 逻辑或
--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)  --2
print(true or 1)  --true
print(nil or 2)  --2

--Lua不支持三目运算符,模拟三目运算符
x=3
y=2
res=(x>y) and x or y

--(x>y) and x -> x  --x>y为真
--x or y -> x

--(x>y) and x -> (x>y)  --x>y为假
--(x>y) or y -> y

17、协同程序

--协程的创建 协程的本质是一个线程对象
--第一种方法:coroutine.create() 常用方式 返回的是一个thread
fun = function()
	print(123)
end
co = coroutine.create(fun)

--第二种方法:coroutine.wrap() 返回的是function
co2 = coroutine.wrap(fun)

--协程的运行
coroutine.resume(co)  --用第一种方法创建的协程的运行方法
co2()  --第二种

--协程的挂起
fun2 = function()
	local i=1
	while true do
		print(i)
		i = i + 1
		coroutine.yield(i)  --协程的挂起函数 可以有返回值
	end
end
co3 = coroutine.create(fun2)  
coroutinee.resume(co3)  --1
coroutinee.resume(co3)  --2
isOk,tempI = coroutinee.resume(co3)  --默认第一个返回值是协程是否启动成功,第二个是yield里面的返回值

co4 = coroutine.wrap(fun2)
co4()  --1
co4()  --2
print(co4)  --这种方式的协程调用 也可以有返回值 只是没有默认第一个返回值了

--协程的状态:coroutine.status(协程对象)
--有三种状态:dead 结束  suspended 暂停  running 进行中

--得到当前正在运行的协程的线程号
coroutine.running()

18、元表

--元表的概念
--任何表变量都可以作为另一个表变量的元表
--任何表变量都可以有自己的元表(爸爸)
--当我们子表中进行一些特定操作时 会执行元表中的内容

--设置元表
meta={
    
    }
myTable={
    
    }
--设置元表函数 第一个参数 子表 第二个参数 元表(爸爸)
setmetatable(myTable,meta)

--特定操作 __tostring 当子表要被当做字符串使用时 会默认调用这个元素中的tostring方法
meta2={
    
    
	__tostring = function(t)
		return t.name
	end
}
myTable2={
    
    
	name = "张三"
}
setmetatable(myTable2,meta2)
print(myTable2)

--特定操作 __call
meta3={
    
    
	__tostring = function(t)
		return t.name
	end,
	--__当子表被当做一个函数来使用时 会默认调这个__call中的内容
	__call = function()  --当希望传参数时 一定要记住 默认第一个参数 是调用者自己
		print("李四")
	end
}
myTable3={
    
    
	name = "张三"
}
setmetatable(myTable3,meta3)
print(myTable3)
myTable3()

--运算符重载
meta4={
    
    
	--相当于运算符重载 当子表使用+运算符时 会调用该方法
	__add = function(t1,t2)
		return t1.age + t2.age
	end
	-- 运算符-:__sub
	-- 运算符*:__mul
	-- 运算符/:__div
	-- 运算符%:__mod
	-- 运算符^:__pow
	
	-- 如果要用条件运算符来比较两个对象,这两个对象的元表一定要一致,才能准确调用方法
	-- 运算符==:__eq
	-- 运算符<:__lt
	-- 运算符<=:__le
	
	-- 运算符..:__concat
}
myTable4={
    
    age = 1}
setmetatable(myTable4,meta4)
myTable5={
    
    age = 2}
print(myTable4+myTable5) --3

--特定操作 __index 和 __newIndex
meta6={
    
    
	__index = {
    
    age = 1}
}
--meta6.__index = {
    
    age = 2}

--meta6={
    
    
--	age=1
--}
--meta6.__index = meta6

myTable6={
    
    }
setmetatable(myTable6,meta6)
--__index 当子表中找不到某一个属性时 会在元表中 __index指定的表去找索引 建议写在外面
print(myTable6.age)

print(getmetatable(myTable6))  --得到元表的方法

print(rawget(myTable6,"age"))  --rawget 当我们使用它时 会去找自己身上有没有这个变量

--newindex 当赋值时 如果赋值一个不存在的索引 那么会把这个值赋值到newIndex所指的表中 不会修改自己
meta7={
    
    }
meta7.__newindex={
    
    }
myTable7={
    
    }
setmetatable(myTable7,meta7)
myTable7.age=1
print(myTable7.age)  --nil
print(myTable.__newindex.age)  --1

rawset(myTable7,"age",2)  --该方法 会忽略newindex的设置 只会改自己的变量
print(myTable7.age)  --2

19、面向对象

-- 面向对象 类 其实都是基于table来实现的
-- 面向对象之封装
Object = {
    
    }
Object.id = 1
function Object:Test()
	print(self.id)
end
--冒号 是会自动将调用这个函数的对象 作为第一个参数传入的写法
function Object:new()
	--self 代表的是 我们默认传入的第一个参数
	local obj = {
    
    }
	--元表知识 __index 当找自己的变量 找不到时 就会去找元表当中 __index指向的内容
	self.__index = self

	obj.base = self  --子类 定义个base属性 代表父类
	
	setmetatable(obj,self)
	return obj
end

local myObj = Object:new()
print(myObj.id)  --1
myObj:Test()  --1
--对空表中 声明一个新的属性 叫做id
--myObj.id=2
--print(Object.id) --1

myObj.id = 2
myObj:Test()  --2

--面向对象之继承
function Object:subClass(className)
	-- _G知识点 是总表 所有声明的全局变量 都以键值对的形式存在其中
	_G[className]={
    
    }
	local obj = _G[className]
	self.__index = self
	setmetatable(obj,self)
end

Object:subClass("Person")
print(Person.id)  --1
local p = Person:new()
print(p.id)  --1

--面向对象之多态
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,self.posY)
end

GameObject:subClass("Player")
function Player:Move()
	self.base:Move()

	--我们如果要执行父类逻辑 就不能直接使用冒号调用 要通过.调用 然后自己传入第一个参数
	--self.base.Move(self)  --填坑
end

local p1 = Player:new()
p1:Move()  --1 1

--目前这种写法 有坑 不同对象使用的成员变量 居然是相同的成员变量 不是自己的
local p2 = Player:new()
p2:Move()  --2 2

20、面向对象总结

Object = {
    
    }

function Object:new()  -- 封装
	local obj = {
    
    }
	--给空对象设置元素 以及 __index
	self.__index = self
	setmetatable(obj,self)
	return obj
end

function Object:subClass(className)  -- 继承
	--根据大G表的特性 根据名字生成一张表 就是一个类
	_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
	print(self.posX,self.posY)
end

GameObject:subClass("Player")  --声明一个新的类 Player 继承 GameObject
function Player:Move()  --多态 重写了GameObject的Move方法
	--self.base.Move(self)  --base调用父类方法 用.自己传第一个参数
end

--实例化对象使用
local obj1 = GameObject:new()
print(obj1.posX)  --0
obj1:Move()
print(obj1.posX)  --1

local obj2 = GameObject:new()
print(obj2.posX)  --0
obj2:Move()
print(obj2.posX)  --1


local p1 = Player:new()
print(p1.posX)  --0
p1:Move()
print(p1.posX)  --1

local p2 = Player:new()
print(p2.posX)  --0
p2:Move()
print(p2.posX)  --1

21、自带库

--时间
print(os.time())  --系统时间

local nowTime = os.date("*t")  --详细时间
for k,v in pairs(nowTime) do
	print(k,v)
end
print(nowTime.year)

--数学运算
print(math.abs(-1))  --绝对值
print(math.deg(math.pi))  --弧度转角度
print(math.cos(math.pi))  --三角函数
print(math.floor(2.6))  -- 2 向上取整
print(math.ceil(5.2))  -- 6 向下取整
print(math.max(1,2))  -- 2 最大值
print(math.min(4,5))  -- 4 最小值
print(math.modf(1.2))  --小数分离 分成整数部分和小数部分
print(math.pow(2,5))  --幂运算
print(math.sqrt(4))  --开方
--随机数
math.randomseed(os.time)  --随机数种子
print(math.random(100))

--路径
print(package.path)  --lua脚本加载路径

22、Lua垃圾回收

--垃圾回收关键字:collectgarbage

--获取当前Lua占用内存数 K字节 用返回值*1024 就可以得到具体的内存占用字节数
print(collectgarbage("count"))

--进行垃圾回收
collectgarbage("collect")

--Lua中 有自动定时进行GC的方法
--Unity中热更新开发 尽量不要去用自动垃圾回收

猜你喜欢

转载自blog.csdn.net/qq_44887198/article/details/127682602