Lua脚本开发踩坑记录:从基础到协程的实际应用 这篇文章是我在游戏开发中使用Lua脚本时整理的一些笔记,从基础语法到协程应用,记录遇到的问题和解决方案。
Lua语言特性 核心特点 1 2 3 4 5 6 7 8 轻量级: 用标准C编写,仅百余KB,便于嵌入其他程序 可扩展: 由宿主语言(C/C++)提供功能,Lua调用 支持面向过程和函数式编程 自动内存管理 通用表(table)实现数组、哈希表、集合、对象 内置模式匹配和闭包(closure) 多线程支持(协同进程) 通过闭包和table支持面向对象关键机制
应用场景
游戏开发(Cocos2d-x、Love2D等)
独立应用脚本
Web应用脚本
数据库插件(MySQL Proxy、MySQL WorkBench)
安全系统(入侵检测)
基础语法 注释
全局变量 Lua默认变量为全局变量:
1 2 3 4 5 6 7 > print (b) > b = 10 > print (b) b = nil print (b)
变量存在条件:不等于nil即存在。
数据类型
类型
描述
nil
无效值,条件表达式中相当于false
boolean
false和true
number
双精度实浮点数
string
双引号或单引号表示的字符串
function
C或Lua编写的函数
userdata
C数据结构
thread
协同进程
table
关联数组,索引可为数字、字符串或表
类型检测 1 2 3 4 5 print (type ("Hello world" )) print (type (10.4 *3 )) print (type (print )) print (type (true )) print (type (nil ))
注意: nil比较时需加双引号
1 2 > type (X) == nil > type (X) == "nil"
布尔值 Lua将false和nil视为false,其他都为true(包括数字0):
1 2 3 if 0 then print ("0 is true" ) end
数字类型 1 2 3 4 print (type (2 )) print (type (2.2 )) print (type (0.2 )) print (type (2e+1 ))
字符串 长字符串:
1 2 3 4 5 6 7 8 html = [[ <html> <head></head> <body> <a href="http://www.runoob.com/">菜鸟教程</a> </body> </html> ]]
字符串自动转换:
1 2 3 > print ("2" + 6 ) > print ("2" + "6" ) > print ("error" + 1 )
字符串连接:
1 2 > print ("a" .. 'b' ) > print (157 .. 428 )
字符串长度:
1 2 3 > len = "www.runoob.com" > print (#len ) > print (#"www.runoob.com" )
表(Table) 表是Lua的核心数据结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 a = {} a["key" ] = "value" key = 10 a[key] = 22 a[key] = a[key] + 11 for k, v in pairs (a) do print (k .. " : " .. v) end
数组(默认从1开始):
1 2 3 4 5 tbl = {"apple" , "pear" , "orange" , "grape" } for key, val in pairs (tbl) do print ("Key" , key) end
函数 函数可作为参数传递:
1 2 3 4 5 6 7 8 9 10 function testFun (tab, fun) for k, v in pairs (tab) do print (fun(k, v)); end end tab = {key1="val1" , key2="val2" }; testFun(tab, function (key, val) return key.."=" ..val; end );
多返回值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function maximum (a) local mi = 1 local m = a[mi] for i, val in ipairs (a) do if val > m then mi = i m = val end end return m, mi end print (maximum({8 , 10 , 23 , 12 , 5 }))
可变参数:
1 2 3 4 5 6 7 8 9 10 11 function average (...) result = 0 local arg = {...} for i, v in ipairs (arg ) do result = result + v end print ("总共传入 " .. #arg .. " 个数" ) return result / #arg end print ("平均值为" , average(10 , 5 , 3 , 4 , 5 , 6 ))
变量与作用域 变量类型
全局变量 :默认情况下所有变量都是全局的
局部变量 :使用local声明,作用域为声明位置到语句块结束
表中的域 :表的字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 a = 5 local b = 5 function joke () c = 5 local d = 6 end joke() print (c, d) do local a = 6 b = 6 print (a, b) end print (a, b)
多变量赋值 1 2 3 4 5 a, b = 10 , 2 *x x, y = y, x a[i], a[j] = a[j], a[i]
变量与值数量不一致:
1 2 3 4 5 a, b, c = 0 , 1 print (a, b, c) a, b = a+1 , b+1 , b+2 print (a, b)
索引方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 t[i] t.i gettable_event(t, i) site = {} site["key" ] = "www.runoob.com" print (site["key" ]) print (site.key)
控制流程 if语句 1 2 3 4 5 6 7 8 9 if (布尔表达式1 ) then elseif (布尔表达式2 ) then elseif (布尔表达式3 ) then else end
while循环 1 2 3 while (condition) do statements end
for循环 数值for循环:
1 2 3 4 5 6 7 8 9 10 11 12 13 for var = exp1, exp2, exp3 do <执行体> end for i = 1 , f(x) do print (i) end for i = 10 , 1 , -1 do print (i) end
泛型for循环:
1 2 3 4 5 a = {"one" , "two" , "three" } for i, v in ipairs (a) do print (i, v) end
repeat…until循环 1 2 3 4 5 6 7 8 9 10 repeat statements until (condition)a = 10 repeat print ("a的值为:" , a) a = a + 1 until (a > 15 )
循环控制
语句
作用
break
退出当前循环
goto
跳转到标签位置
运算符 算术运算符
运算符
描述
实例
+
加法
A + B = 30
-
减法
A - B = -10
*
乘法
A * B = 200
/
除法
B / A = 2
%
取余
B % A = 0
^
乘幂
A^2 = 100
-
负号
-A = -10
关系运算符
运算符
描述
==
等于
~=
不等于
>
大于
<
小于
>=
大于等于
<=
小于等于
逻辑运算符
运算符
描述
and
逻辑与
or
逻辑或
not
逻辑非
1 2 3 4 5 6 A = true B = false print (A and B) print (A or B) print (not (A and B))
其他运算符
运算符
描述
实例
..
连接字符串
“Hello “ .. “World”
#
返回长度
#”Hello” = 5
运算符优先级(从高到低) 1 2 3 4 5 6 7 8 ^ not - (unary) * / % + - .. < > <= >= ~= == and or
字符串操作 常用字符串函数
函数
用途
string.upper(arg)
转大写
string.lower(arg)
转小写
string.gsub(mainString, findString, replaceString, num)
替换
string.find(str, substr, [init, [end]])
查找位置
string.reverse(arg)
反转
string.format(…)
格式化
string.char(arg)
数字转字符
string.byte(arg[, int])
字符转数字
string.len(arg)
长度
string.rep(string, n)
重复n次
字符串操作示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 > string .gsub ("aaaa" , "a" , "z" , 3 ); zzza 3 > string .find ("Hello Lua user" , "Lua" , 1 ) 7 9 > string .reverse ("Lua" ) auL > string .format ("the value is:%d" , 4 ) the value is:4 > string .char (97 , 98 , 99 , 100 ) abcd > string .byte ("ABCD" , 4 ) 68
模式匹配 1 2 3 4 5 6 7 8 9 for word in string .gmatch ("Hello Lua user" , "%a+" ) do print (word) end > = string .match ("I have 2 questions for you." , "%d+ %a+" ) 2 questions
表操作
函数
用途
table.concat(table [, sep [, start [, end]]])
连接数组元素
table.insert(table, [pos,] value)
插入元素
table.maxn(table)
最大正数key(Lua5.2已移除)
table.remove(table [, pos])
删除元素
table.sort(table [, comp])
排序
模块与包 创建模块 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 module = {}module .constant = "这是一个常量" function module.func1 () io .write ("这是一个公有函数!\n" ) end local function func2 () print ("这是一个私有函数!" ) end function module.func3 () func2() end return module
使用模块 1 2 3 4 5 6 7 8 9 require ("module" )print (module .constant)module .func3()local m = require ("module" )print (m.constant)m.func3()
协程(Coroutine) 协程特点
拥有独立的堆栈、局部变量和指令指针
与其他协程共享全局变量
任意时刻只有一个协程在运行
只有被挂起(suspend)时才会暂停
基本方法
方法
描述
coroutine.create()
创建协程,返回coroutine
coroutine.resume()
启动或恢复协程
coroutine.yield()
挂起协程
coroutine.status()
查看状态(dead/suspended/running)
coroutine.wrap()
创建协程,返回函数
coroutine.running()
返回正在运行的协程
协程示例 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 co = coroutine .create ( function (i) print (i); end ) coroutine .resume (co, 1 ) print (coroutine .status (co)) co = coroutine .wrap ( function (i) print (i); end ) co(1 ) co2 = coroutine .create ( function () for i = 1 , 10 do print (i) if i == 3 then print (coroutine .status (co2)) print (coroutine .running ()) end coroutine .yield () end end ) coroutine .resume (co2) coroutine .resume (co2) coroutine .resume (co2) print (coroutine .status (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 local newProductorfunction productor () local i = 0 while true do i = i + 1 send(i) end end function consumer () while true do local i = receive() print (i) end end function receive () local status , value = coroutine .resume (newProductor) return value end function send (x) coroutine .yield (x) end newProductor = coroutine .create (productor) consumer()
文件I/O 文件模式
模式
描述
r
只读,文件必须存在
w
只写,存在则清空,不存在则创建
a
追加写,文件内容保留
r+
可读写,文件必须存在
w+
可读写,存在则清空
a+
可读写,追加模式
b
二进制模式
简单模式 1 2 3 4 5 6 7 8 9 10 11 file = io .open ("test.lua" , "r" ) io .input (file)print (io .read ())io .close (file)file = io .open ("test.lua" , "a" ) io .output (file)io .write ("-- test.lua 文件末尾注释" )io .close (file)
元表用于定义两个表的操作行为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 setmetatable (table , metatable)getmetatable (table )mytable = setmetatable ({1 , 2 , 3 }, { __add = function (table1, table2) for i = 1 , #table2 do table .insert (table1, table2[i]) end return table1 end })
以上是我在游戏开发中使用Lua脚本时整理的一些经验,涵盖了基础语法到协程应用的各个方面。