luanti Lua 编码规范
Lua 编码规范
Lua 是一门简洁、极简、优雅、高效、可嵌入、表现力强的脚本语言。想要高效开发 Luanti 模组与游戏,必须熟练掌握 Lua。
Lua 非常灵活——甚至对很多人来说过于灵活。
Lua 给你能力,你来构建机制。
——《Programming in Lua》第一版
本文介绍 Luanti 中使用的机制与编码规范。
学习资源
本文不教编程基础或 Lua 基础,推荐以下资源:
- 强烈推荐:Programming in Lua (PIL)
在线版针对 Lua 5.0,但大部分内容适用于 5.1 - lua-users.org 教程
- 常备手册:Lua 5.1 参考手册
- Lua 5.1 速查(小抄)
整数(Integers)
Lua 5.1 只有一种数字类型:64 位 IEEE754 浮点数。
除非特别说明,Luanti 只接受有限数字:
- 不能是 ±无穷大(
math.huge/-math.huge) - 不能是 NaN(
0/0)
满足 math.floor(number) == number 的值被视为整数。
从 −2⁵³ 到 2⁵³ 的所有整数都能精确表示,称为安全整数,请尽量使用这个范围。
超出后数字不再连续(例如 2^53 + 1 无法精确表示)。
⚠️ 重要
Luanti 底层 C++ 经常会把 Lua 数字转为 32 位有符号整数,PUC Lua 5.1 本身也会这么做(比如 math.random)。
因此建议把数值控制在 −2³¹ ~ 2³¹−1 以内更安全。
不要向需要整数的函数传入小数(包括 math.random)。
四舍五入行为未定义,请手动用 math.floor / math.ceil / math.round。
字符串(Strings)
Lua 字符串是字节串,理论上可使用任意编码,但 Luanti 的标准是 UTF‑8。
处理 Unicode 时默认按 UTF‑8 处理。
注意:core.compress 这类函数不处理文本,而是把字符串当作二进制数据。
不要混淆这两种用法。
表(Tables)
表是 Lua 最核心的“万能”数据结构。
表将 键 → 值 映射,一组键值叫一个条目(entry)。
示例:
1 | {a = 1, ["b"] = 2, [3] = true} |
键:"a"、"b"、3
值:1、2、true
列表(List / Sequence)
从 1 开始的连续整数键,不能有洞(nil 后面跟非 nil)。
合法:
1 | {1, 2, 3} |
不合法:
1 | {nil, 2} |
集合(Set)
以键作为元素、值固定为 true 的表。
示例:
1 | {spam = true, ham = true} |
下面这不是集合:
1 | {spam = true, ham = false} |
命名空间(Namespace)
以名称为键、存放 API 函数/表的全局表叫命名空间。
例如 core 就是一个命名空间。
强烈建议:
- 给你的模组 API 加命名空间
- 其他所有变量都用
local
示例:
1 | mymath = {} |
表结构常见错误
一定要分清列表和单表。
错误(会被当成空列表):
1 | {name = "tex.png", backface_culling = false} |
正确(是包含一个元素的列表):
1 | { |
面向对象编程(OOP)
Lua 本身没有类,通过元表(metatable) 实现基于原型的 OOP。
Luanti 就是这么做的。
通常只使用 __index 元方法实现原型继承。
向量是例外:重载了算术运算符。
构造器
返回类实例的函数,例如:
VoxelManip(...)VoxelArea:new(...)vector.new(...)
由于历史原因,构造器写法不统一:
Class:new(...)Class.new(...)Class(...)
实例与方法
实例可以是表或 userdata。
- 实例变量:实例自己的字段
- 方法:以
instance:method(...)调用的函数(语法糖:instance.method(instance, ...))
类变量(坑点预警)
类变量是所有实例共享的,通过 __index 查找。
如果类变量是引用类型(表),修改它会影响所有实例!
典型坑:Lua 实体
1 | -- 错误:会修改所有同类型实体的默认纹理! |
这是最常见的全局污染式 Bug。