单行注释:--
多行注释:--[[
注释内容
]]
Lua教程 | 菜鸟教程
【唐老狮】Unity热更新之Lua语法
1.变量
1.1 作用域
-
全局变量: 没添加关键字 无论再哪里声明的都是全局变量(全局变量会被存放再_G表中)
-
局部变量:加入关键字local的就是本地变量(出来循环、函数、脚本和协程后会被销毁)
1.2 使用
- 声明时 不需要声明变量类型 会自动判断类型 (类似C#的var)
- 变量可以随便赋值,会自动识别类型
- 使用type函数可以得到当前变量的存储数据的类型,函数返回值是string类型
- 使用没声明的变量不会报错,默认值是nil
1.3 简单变量类型
- nil: 表示无效值
- 给变量赋值为nil,也有解除占用、删除的作用,会被资源回收
- number :表示双精度类型的实浮点数(C#中的int float double 都属于number)
- string : 字符串(lua不分char 和 string)
- 字符串长度获取关键字
#
- 多行字符串的定义 1.插入转义字符\n 2.[[…]] 双中括号内可以接收回车进行换行输入
- 字符串拼接关键字
..
,也可以使用格式化函数string.format("%d",num)
(类似C语言) - 其他类型转字符串 tostring()
- 字符串长度获取关键字
- boolean : 布尔值(true、false) 只有 false 和 nil 看作是 false,其他的都为 true
1.4 复杂变量类型
1.4.1 function(函数)
-
定义
function 函数名() ...... end
函数名 = function() ...... end
-
使用
- 如果传入的参数或返回值个数不匹配,多了丢弃,少了补nil
- 支持多返回值 在函数调用前面声明多个变量接取即可
- 支持可变长参数 关键字是
...
。(原理是把参数当作表,在函数内使用前最好定义表提取出来) - 不支持函数重载,先声明的函数会被后声明的函数覆盖
- 函数嵌套:在函数的返回值里面返回一个匿名函数,利用函数也是变量的特性,这样可以改变传入参数的生命周期
--闭包 改变传入参数的生命周期 function F9(x) return function(y) return x+y end end f10 = F9(10) --f10 = function(x) return x + 10 end print(f10(5)) --输出15 因为 f10(5) = function(5) return 5 + 10 end
1.4.2 table(表)
Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字或者是字符串。
- 使用
- 表支持自定义索引,默认索引从1开始,使用自定义索引如果和默认索引冲突,自定义索引的变量会被默认的覆盖
#
是获取表长度的关键字- 表的长度取决于表内元素的索引,会从1开始往后数,如果自定义索引不在范围内则不会被计算在长度内
- 表中的nil会影响表的长度,遇到连续两个nil会认为表已经结束
- 表内函数
- 表内声明
变量名 = function() ...... end
- 表外声明
表名.函数名 = function() ...... end
(没有默认传参)表名:函数名 = function() ...... end
(默认传参 是第一个 参数就是调用的表,多态的关键,使用self关键字表示)
- 公共方法
- 插入
table.insert(表名1,表名2)
- 删除指定元素
table.remove(表名1,下标)
表名一:目标表 下标:是移除内容的索引(默认移除最后一个) - 排序
table.sort(表名,function(x,y) ...... end )
参数一:目标表 参数二: 排序规则函数 (默认排序) - 拼接
table.concat(表一,拼接字符串,下标一,下标二)
参数一:目标表 , 参数二: 拼接时连接处添加的字符串 , 参数三:是目标表的开始下标 , 参数四 是目标表拼接的结束下标
- 表内声明
1.4.3 userdata(自定义类型)
userdata是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据(通常是struct和指针) 存储到Lua变量中调用。
1.4.4 thread(线程)
-
在 Lua 里,最主要的线程是协同程序(coroutine)。它跟线程(thread)差不多,拥有自己独立的栈、局部变量和指令指针,可以跟其他协同程序共享全局变量和其他大部分东西。
-
线程跟协程的区别:线程可以同时多个运行,而协程任意时刻只能运行一个,并且处于运行状态的协程只有被挂起(suspend)时才会暂停。
-
协程的本质是一个线程类型的变量
创建函数 coroutine.create(函数)
coroutine.wrap()
返回值类型 返回一个类型为线程的协程变量 返回一个类型为函数的协程变量 使用 coroutine.resume()
直接调用函数 使用函数返回值 默认第一个返回值是协程是否启动成功,第二个是挂起函数的返回值 没用协程是否成功启动的返回值 - 挂起函数
coroutine.yield(返回值)
yield里面填协程的返回值
- 挂起函数
--协程状态查看:
coroutine.running(协程对象) --返回正在运行的协程的线程号
coroutine.status(协程对象) --返回协程对象的状态
--[[
dead:结束状态 代表协程全部执行完
suspended: 暂停状态 协程被挂起
running: 进行中 在协程中才会出现
]]
2.运算符
2.1 算数运算符
有 | 没有 |
---|---|
+ , - , * , / , % , ^ (幂运算)` | ++ , -- (自增自减) , += , -= (复合运算符) |
2.2 关系运算符
有 <
, >
, >=
, <=
, ==
, ~=
(不等于)
2.3 逻辑运算符
有 且:and
, 或:or
否:not
lua中保留了“短路”概念 : 例如 在做逻辑判断and的时候 如果前面条件为false 则后面条件不判断了,or同理
2.4 其他运算符
..
: 字符串连接
#
: 返回字符串或表的长度
lua中不支持位运算和三目运算符
但可以利用逻辑运算符的特性来模拟三目运算符
例如: (条件) and 结果1 or 结果2
相当于 (条件) ? 结果1 : 结果2
因为如果 条件为真 则直接为结果一 true and 结果1 … 后面会被省略 直接输出结果一
而如果条件为假 则 (条件) and 结果1 or 结果2
= >false or 结果2
直接输出结果二
3.条件分支语句
单分支 | 多分支 |
---|---|
if 条件 then ...... end | if 条件 then ...... else ...... end |
- | if 条件 then ...... elseif 条件2 then ...... end |
4.循环语句
调用 | 特点 | |
---|---|---|
while | while 条件 do … end | 该条件是进入条件 |
repeat | repeat … until 条件 | 该条件是结束条件 |
for | for 变量名 = 初始值, 结束值 , 步进 do … end | 步进可不填 默认是1 |
ipairs (迭代器) | for 变量名1,变量名2 in ipairs(表名) do … end | 原理是通过#获取表长度进行变量(获取表长度会被影响不准确)。变量一是表变量的索引,变量二是表变量的值 |
pairs (迭代器) | for 变量名1,变量名2 in pairs(表名) do … end | pairs可以变量表的全部键值对 , 顺序是先默认下标(1到n) 再到自定义索引 |
5.元表
Lua 提供了元表(Metatable),允许我们改变 table 的行为,每个行为关联了对应的元方法。
任何表变量都可以作为另一表变量的元表 ,任何表变量都可以有自己的元表
--元表函数
setmetatable(table,metatable) --对指定table设置元表(meatatblen) 如果元表(metatable)中存在 __metatablen 键值,setmetatable会失败
getmetatable(table) --返回对象的元表(metatable)
特定操作 | 使用场景 | 注意事项 |
---|---|---|
__tostring | 当子表要被当作字符串使用时,会默认调用元表中的__tostring方法 | 子表调用元表的__tostring方法时 会默认把自己当作第一个参数传入 |
__call | 当子表要被当作函数使用时,会默认调用元表中的__call方法 | 子表调用元表的__call方法时 会默认把自己当作第一个参数传入,传参是从第二个开始 |
__index | 当子表中找不到某一个属性时,会到元表中指定的__index表去找。如果元表也没有,回到元表的元表找 | __index表的指定 最好写在表外面,写在表内可能会被跳过 |
rawget() | 使用这个函数,只会去找自己身上的变量不会去找元表的__index | — |
__newIndex | 当对表内元素赋值时,如果赋值一个不存在的索引,则会把这个值赋到元表中指定的__newIndex表中 | — |
rawset() | 使用这个函数,只会对自身表的属性赋值,不会去找元表的__newIndex | — |
运算符重载 | 当子表使用某一运算符时,会调用对应重载方法 + , - , * , / , % , ^ , == , < , <= , .. | 没有小于和小于等于,只能用大于,大于等于 |
6.特性
6.1 脚本执行(模块与包)
可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。
Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行。
6.1.1脚本调用
require("脚本路径")
(如果和当前文件同层级 就是脚本名本身)
- 效果上和函数调用类似,也可以添加返回值,但只能有一个返回值
- 加载执行过,未被卸载就不会再被执行
6.1.2脚本卸载
package.loaded["路径名"]
返回值是boolean 脚本是否被执行
给package.load["路径名"]
赋值为nil或false 即可卸载脚本
6.2 面向对象(模仿)
- 封装(利用函数的冒号传参和元表可以把常用方法封装到方法表中)
Object = {} --所有对象的基类(元表)
-- 封装 把新建表方法封装到Object中
function Object:new()
local obj = {}
--给新实例化出来的对象 设置元表(相当于根据模板属性和方法实例化对象)
self.__index = self
setmetatable(obj,self)
return obj
end
- 继承(利用元表来实现 表到表之间的继承关系)
-- 继承 利用元表实现表之间的继承
function Object:subClass(className)
--根据名字和父类(Object) 生成一张表 相当于根据父类生成子类
_G[className] = {}
local obj = _G[className]
obj.base = self --把自身元表(父类)保存起来
self.__index = self
setmetatable(obj,self)
end
- 多态(在表的继承中使用变量base存储父类表本身,再根据传参即可实现
相同的方法
不同的执行逻辑
)
-- 根据父类声明新的类
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) --GameObject.posX
obj:Move() --定义了obj.posX 并赋值为 GameObject.posX+1
print(obj.posX)
local obj2 = GameObject:new()
print(obj2.posX) --GameObject.posX
obj2:Move() --定义了obj2.posX 并赋值为 GameObject.posX+1
print(obj2.posX)
-- 声明了一个新的类 Player 继承 GameObject
GameObject:subClass("Player")
Player.posX = 60
Player.posY = 60
-- 多态 重写了 GameObject的Move方法
function Player:Move()
-- base调用父类方法 用.传自己作为第一个参数 而不是固定使用base(因为自己没有base的话会使用父类的base)
self.base.Move(self)
end
print("---------------多态实例化---------------")
local p1 = Player:new()
print(p1.posX) --Player.posX
p1:Move() --p1.posX 并赋值为 Player.posX+1
print(p1.posX)
local p2 = Player:new()
print(p2.posX) --Player.posX
p2:Move() --p2.posX 并赋值为 Player.posX+1
print(p2.posX)
6.3 垃圾回收
关键字: collectgarbage
解除占用(不会用到对象)的变量会被回收
collectgarbage("collect")
: 做一次完整的垃圾收集循环collectgarbage("count")
: 以 K 字节数为单位返回 Lua 使用的总内存数。 这个值有小数部分,所以只需要乘上 1024 就能得到 Lua 使用的准确字节数(除非溢出)。collectgarbage("restart")
: 重启垃圾收集器的自动运行。collectgarbage("setpause")
: 将 arg 设为收集器的 间歇率。 返回 间歇率 的前一个值。collectgarbage("setstepmul")
: 返回 步进倍率 的前一个值。collectgarbage("step")
: 单步运行垃圾收集器。 步长"大小"由 arg 控制。 传入 0 时,收集器步进(不可分割的)一步。 传入非 0 值, 收集器收集相当于 Lua 分配这些多(K 字节)内存的工作。 如果收集器结束一个循环将返回 true 。collectgarbage("stop")
: 停止垃圾收集器的运行。 在调用重启前,收集器只会因显式的调用运行。