Skip to content

看不懂代码时查 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 与计时器。

带着刚查清的语法返回主线,继续当前步骤即可。