主题
看不懂代码时查 Lua 速查
用小游戏里的变量、判断、函数和 table,理解当前这一步代码。
不用先把整页背完。主线第一次使用一个概念时会解释它;这里帮助你回查正在看的那一行。还没运行过代码,先做第一课。
本页标为“独立实验”的代码只使用 Lua 基础能力。在专用教程工程的 client/main.lua 一次替换一块,保存并确认同步后重新试玩;完成后恢复当前主线检查点。
从一行代码看起
| 写法 | 它在做什么 | 在小游戏里的意思 |
|---|---|---|
local score = 0 | 新建局部变量并保存初值 | 这局当前是 0 分 |
score = score + 1 | 先计算右边,再写回左边 | 当前分数增加 1 |
print(score) | 把值打印到输出 | 检查程序现在保存了多少分 |
-- 说明 | 从这里到行尾是注释 | 给读代码的人看,运行时跳过 |
"球体" | 字符串,需要成对英文引号 | 与场景对象的 Name 比较 |
nil | 目前没有值 | 查找没有找到,或对象尚未就绪 |
local 是 Lua 关键字,不能翻译;你自己起的变量名可以改,但同一变量的每处引用都要一起改。新手先用英文变量名与英文标点,日志文字可以用中文。
变量为什么能记住分数
示例类别:独立实验;运行端:client。
lua
-- @runtime client
-- [功能目标] 演示局部变量的定义、读取与多次累加赋值(模拟推球得分过程)
-- 1. 定义局部变量 score 并赋予初始值 0
local score = 0
print("[Lua练习] 开始:", score)
-- 2. 模拟第 1 次推球命中:在原值基础上累加 1
score = score + 1
print("[Lua练习] 命中一次:", score)
-- 3. 模拟达成连击额外奖励:在当前值基础上再累加 3
score = score + 3
print("[Lua练习] 再加三分:", score)输出依次为 0、1、4。赋值语句不是数学等式:score = score + 1 表示先取旧值、加 1、再保存。重新开始试玩会重新执行初值,普通变量不是永久存档。
自己改一处:把第二次的 + 3 改成 + 5,先预测最后一行,再运行。
参考答案
最后是 6。只修改第三次赋值的右侧,不修改初值;实验完成后改回 3。
赋值和比较不要混用
| 写法 | 含义 |
|---|---|
score = 3 | 把 3 保存到 score |
score == 3 | 判断 score 是否等于 3 |
score ~= 3 | 判断是否不等于 3 |
score > 0 / score <= 0 | 大小比较 |
and / or / not | 同时满足 / 任一满足 / 取反 |
示例类别:独立实验;运行端:client。
lua
-- @runtime client
-- [功能目标] 演示 if-else 多条件组合判断(and / 比较运算符)
local score = 2
local phase = "playing" -- 当前对局阶段:"playing" 或 "waiting"
-- 只有当对局处于进行中 且 分数尚未达到上限时,才允许继续得分
if phase == "playing" and score < 3 then
print("[Lua练习] 还可以继续得分")
else
print("[Lua练习] 当前不满足条件")
end把 score 改为 3 后,输出应进入 else。if 后的条件成立才执行 then 分支;end 结束这段判断,不要漏写。
Lua 中只有 false 和 nil 被当作假。数字 0 和空字符串 "" 都是真,因此判断“还没有得分”应写 score == 0,不能写 not score。
nil 为什么要先检查
查找对象可能暂时找不到。例如 ball 为 nil 时,直接读取 ball.Name 就会失败。主线中的 if not ball then ... return end 是说:“没有取得球,先说明原因并结束这段流程。”
判空之后还要确认具体类型。找到一个叫“球体”的节点,不代表它一定是 WorldUnit;名称正确、类型错误时,碰撞成员仍不可用。具体查找链见场景树。
函数是可以再次调用的一段逻辑
示例类别:独立实验;运行端:client。
lua
-- @runtime client
-- [功能目标] 演示函数传参、运算与 return 返回值
-- 定义加分计算函数:接收当前分与增加分,返回计算后的新分数
local function AddPoints(oldScore, points)
return oldScore + points
end
-- 调用函数并接收计算结果
local score = AddPoints(2, 3)
print("[Lua练习] 新分数:", score)oldScore 和 points 是参数,调用时分别收到 2 和 3。return 返回结果 5;它同时结束当前函数。只有写了 AddPoints(2, 3),函数里的逻辑才会执行,单独定义函数不会自动运行。
事件回调也是函数,只是由事件在条件发生时调用。把函数交给 Connect 是“开始等待事件”,不是“立刻发生一次碰撞”。事件断开和计时取消见事件与计时器。
作用域:变量在哪里能被使用
函数内创建的局部变量,通常只在那次函数调用里使用。想让多次碰撞共享同一份分数,就把分数保存在它们共同能访问的位置,而不是在碰撞函数里每次重新写 local score = 0。
示例类别:独立实验;运行端:client。
lua
-- @runtime client
-- [功能目标] 演示变量作用域(外层闭包变量在多次函数调用间持久保留)
-- count 声明在函数外层(文件/外层作用域),不会随函数结束而重置
local count = 0
local function CountOnce()
count = count + 1 -- 每次调用函数时修改同一份外层变量
print("[Lua练习] 次数:", count)
end
-- 连续调用两次,观察数值持续累加(1 -> 2)
CountOnce()
CountOnce()应依次输出 1、2。把 local count = 0 移进函数后会每次重置成 0,两次都输出 1。这能解释“明明碰了多次,分数为什么一直是 1”。
table 可以按名字,也可以按顺序存东西
示例类别:独立实验;运行端:client。
lua
-- @runtime client
-- [功能目标] 演示 table 作为键值字典(类似快照结构)与有序列表的读取方式
-- 1. 字典形式:存储命名属性(推球小游戏中的状态快照格式)
local snapshot = {
phase = "playing", -- 对局阶段
score = 3, -- 当前得分
}
-- 2. 数组形式:按顺序存储一组分值(索引从 1 开始)
local rewards = { 1, 3, 5 }
-- 读取方式对比:点号访问、中括号键访问与数组索引访问
print("[Lua练习] 阶段:", snapshot.phase)
print("[Lua练习] 分数:", snapshot["score"])
print("[Lua练习] 第一个奖励:", rewards[1])snapshot.phase 与 snapshot["phase"] 读取同一个字段。按顺序保存的列表从索引 1 开始。键来自变量时写 scores[userId];写成 scores.userId 则是在找字面名字为 userId 的字段。
table 是你自己的数据容器。{ x = 1, y = 2, z = 3 } 不会因此变成引擎的 Vector3;需要向公开位置字段传值时,使用该 API 要求的 Vector3(...) 或 Vector3.New(...)。
for 循环遍历一组数据
示例类别:独立实验;运行端:client。
lua
-- @runtime client
-- [功能目标] 演示 ipairs 遍历连续数组列表
local rewards = { 1, 3, 5 }
-- ipairs 会按 1, 2, 3 顺序依次取出序号 index 和对应元素 points
for index, points in ipairs(rewards) do
print("[Lua练习] 第几项与分值:", index, points)
end这里应打印三项。ipairs 适合连续的数组;pairs 可按键遍历配置或玩家分数字典,不保证键的遍历顺序。需要排行榜顺序时,不能把字典的打印顺序当作名次。
点号、冒号和构造器
| 写法 | 怎么理解 |
|---|---|
ball.Name | 读取这个对象的属性 |
ball:IsA("WorldUnit") | 调用对象的方法,冒号会带上当前对象 |
Vector3.New(0, 1, 0) | 按公开构造接口创建一个值 |
Vector3(0, 1, 0) | 该类型支持的等价直接构造写法 |
Hud.Init() | 本教程普通模块定义的点号函数,按定义调用 |
点号与冒号不能随意交换。尤其不要把构造器写成 Vector3:New(...),也不要改成小写 .new(...)。不是每个 Data 都能自行构造,事件输入和返回结果往往需要从公开 API 获得。
require 只负责加载模块
模块放在 data、common、client 或 server 中,引用路径包含目录前缀,例如 require("data.round_config")。不要带 .lua 后缀,也不要跨端引用 client/server 文件。
一个模块 return Hud 后,调用方拿到的是 table;如果它只定义 Init,还需要显式调用才能初始化。工程完整例子看模块目录与入口。
字符串、数字与日志
"3" 是字符串,3 是数字。把值显示到 UI 时可用 tostring;把文本解析为数字时用 tonumber,但失败会返回 nil,不能直接参与计算。
字符串拼接用 ..,例如 "分数:" .. tostring(score)。输出多项可用 print("当前值:", score),通常更适合临时排错。每次先看当前试玩中的第一条错误,不被后续连带报错带走。
不要从普通 Lua 教程直接搬进来的内容
SE 使用受控运行环境,不提供常见桌面 Lua 的 io、os、package、debug 库。不能通过写文件、读取系统目录或加载外部包替代游戏 API。需要持久化时使用存储专题,需要延后执行时查Task 与计时器。
带着刚查清的语法返回主线,继续当前步骤即可。
