Roblox Studio · 第 28 课

调试实验室:亲手找出三个 bug

练习读 Output、加断点、观察变量和使用 assert,把“好像能玩”变成可重复的测试。

10–15 岁 75 分钟 Output / 断点 / assert 动手实践 测试与保存

Course overview

发现 bug 时,不乱改十处,而是一步步缩小范围。

调试不是证明自己写错了,而是寻找“电脑实际做了什么”和“我希望它做什么”之间的差别。每位开发者都会遇到它。

这一课在副本中研究三个常见问题:找错对象、边界写错、事件连接重复。你会记录预期、实际和最小修改,让问题可以被别人复现。

01 · BEFORE

开始之前

完成条件、事件与模块课。新建 Lesson28,只放一个实验 Script,避免旧代码干扰。

02 · MAKE

你的作品

三份可解释的修复记录,以及一段能够自动检查边界的测试代码。

03 · CHECK

完成标志

能读第一条报错的脚本名与行号;修复后重新验证正常、边界与失败情况。

Script Editor 左侧设置断点的官方截图
图 28-1:官方断点截图:点击代码左侧行号旁设置断点,观察程序何时到达这一行。Roblox Creator Documentation · CC BY 4.0 · 原图未修改 · 许可说明

Learning route

先准备,再动手;看到结果后,记得保存。

每完成一小步,就测试一次。先说出你预计会看到什么,再用 Play 和 Output 检查,不用急着一次写完所有代码。

1

01 · 给问题写一句能复现的描述

“金币坏了”太模糊。“进入游戏,碰一次 Coin,Coins 从 0 变成 2”就能帮助别人找到问题。

2

02 · 找不到对象:先看路径,不加等待

在 Workspace 创建 Coin,然后在 DebugLab 中运行下面这个故意出错的小例子。Cion 拼错了,FindFirstChild 返回 nil,下一行访问 Color 就会报错。

3

03 · 没报错,也可能算错了

把 >= 写成 > 不会产生语法报错,但会拒绝“刚好达到门槛”的玩家。用三个数字测试,比只试一次更容易发现它。

4

04 · 一次操作两次回应:检查连接位置

如果按钮每次点击让数字增加两次,先搜索是否有两份脚本,或同一个事件被 Connect 两次。更新界面的 render 函数不应该每次都重新连接事件。

5

05 · 用断点暂停在关键一步

在 Script Editor 行号旁设置断点,Play 时让程序停在比较或赋值前。用 Watch 或变量悬停查看 energy,而不是凭想象猜它是多少。

6

06 · 让 Output 和场景保持干净

无意义的每帧 print 会淹没真正问题;不断 Instance.new 却不清理的对象会越积越多。先测量与定位,再决定优化什么。

Step by step

01 · 给问题写一句能复现的描述

“金币坏了”太模糊。“进入游戏,碰一次 Coin,Coins 从 0 变成 2”就能帮助别人找到问题。

每轮调试只改一个原因。改完回到同一个起点重复操作,才能判断修复是否有效。

  1. 记录:操作步骤、预期结果、实际结果。
  2. 打开 Output,只先看最早与你的脚本相关的红色错误。
  3. 点击脚本来源或行号,定位到代码。

Step by step

02 · 找不到对象:先看路径,不加等待

在 Workspace 创建 Coin,然后在 DebugLab 中运行下面这个故意出错的小例子。Cion 拼错了,FindFirstChild 返回 nil,下一行访问 Color 就会报错。

修复是把名字改为 Coin,并在必要时明确检查对象是否存在。给错名字增加等待并不能变出一个对象。

  1. 只在 Lesson28 实验副本里运行这段故意错误代码。
  2. 看到错误后记录行号,再改正 Cion 为 Coin。
  3. 重新 Play,确认 Coin 变成绿色。
ServerScriptService > DebugLab · 故意错误的实验,修复后再保留Luau
local coin = workspace:FindFirstChild("Cion") -- 故意拼错
coin.Color = Color3.fromRGB(80, 220, 130)

Step by step

03 · 没报错,也可能算错了

把 >= 写成 > 不会产生语法报错,但会拒绝“刚好达到门槛”的玩家。用三个数字测试,比只试一次更容易发现它。

下面是修复后的完整测试。assert 会在条件不成立时停止并说明哪个情况失败。这里没有场景依赖,适合独立运行。

  1. 替换 DebugLab,运行正确版本,看到 All boundary checks passed。
  2. 把函数里的 >= 临时改成 >,再运行,观察等于门槛的测试失败。
  3. 修复后再运行一次,别只看红线消失。
ServerScriptService > DebugLab · 修复后的边界测试Luau
local function canEnter(energy)
    return energy >= 5
end
assert(canEnter(4) == false, "Below threshold")
assert(canEnter(5) == true, "Exactly at threshold")
assert(canEnter(6) == true, "Above threshold")
print("All boundary checks passed")

Step by step

04 · 一次操作两次回应:检查连接位置

如果按钮每次点击让数字增加两次,先搜索是否有两份脚本,或同一个事件被 Connect 两次。更新界面的 render 函数不应该每次都重新连接事件。

需要长期重复开启和关闭功能时,保留连接对象,并在不需要时 Disconnect。普通按钮通常只在脚本启动时连接一次就够了。

  1. 回看第 16 课,确认 Activated:Connect 在 render 外面。
  2. 给回调加入一次 print,观察一次点击到底执行几次。
  3. 不要用更长冷却掩盖重复连接;应修复重复的来源。

Step by step

05 · 用断点暂停在关键一步

在 Script Editor 行号旁设置断点,Play 时让程序停在比较或赋值前。用 Watch 或变量悬停查看 energy,而不是凭想象猜它是多少。

调试结束后移除或禁用断点,再正常运行一次。否则下次测试突然暂停,会让你误以为游戏卡住。

  1. 在边界测试函数的 return 行设断点。
  2. 运行并查看参数依次是 4、5、6。
  3. 继续执行直到测试完成,再清除断点。
  4. 若当前版本界面不同,按 Debugger、Breakpoints、Watch 等工具名寻找。
Watch 窗口查看变量的官方截图
图 28-2:官方 Watch 窗口:暂停后查看关键变量的实际值,把猜测变成可检查的证据。Roblox Creator Documentation · CC BY 4.0 · 原图未修改 · 许可说明

Step by step

06 · 让 Output 和场景保持干净

无意义的每帧 print 会淹没真正问题;不断 Instance.new 却不清理的对象会越积越多。先测量与定位,再决定优化什么。

本课的验收是可重复的结果,不是宣称“完全没有 bug”。记录你测了什么、没测什么,才方便下一次继续。

  1. 删除临时的高频输出,保留有用的阶段或失败消息。
  2. 多跑几轮,观察是否不断新增同名对象。
  3. 写下三个已修复问题及对应验证步骤。

Homework

轮到你来改一改,做出自己的版本。

1 一张 bug 卡

写清复现步骤、预期、实际、原因、修改与再次测试结果。

2 边界测试扩展

把门槛改为 8,对 7、8、9 各加一条测试。

3 选做:审查旧作

检查之前项目有没有重复脚本、没等待的循环或未清理的连接。

完成标准:先在 Playtest 中验证作品,再点击 Stop,保存为“姓名缩写_Lesson28”或对应 Homework 副本;不要覆盖上一课的可运行版本。

Exit questions

先自己回答,再展开看看。

没有红色报错就代表逻辑正确吗?

不代表,例如边界比较写错可能正常运行却给出错误结果。

为什么先读第一条相关错误?

后面的错误可能只是前一个问题引起的连锁反应。

重复回调可以只靠加冷却修好吗?

冷却可能掩盖现象,仍应找出重复脚本或重复 Connect。

调试结束后要检查什么?

清除断点,重新正常运行,并重复原来的复现步骤。

Explore further

需要查资料时,从这里出发。

正文代码可以直接复制。先确认脚本类型、放置位置,以及这段代码是完整版本还是小实验;不要把同一脚本的多个版本叠加运行。界面参考图已标注来源,图中的对象名可能与本课不同,请以操作步骤为准。