开始之前
理解函数返回值、table 与条件判断。新建 Lesson20 Baseplate。
Roblox Studio · 第 20 课
用 ModuleScript 与 require 共享奖励计算,把规则和场景操作分开并写出小测试。
Course overview
当两个机关都需要“每颗晶石值多少分”的规则,复制代码会产生一个新问题:改了这里,却忘记那里。ModuleScript 可以把共同规则集中起来。
这一课不做复杂背包系统。我们先写一个只计算、不改变场景的模块,再用 assert 检查它的边界。
理解函数返回值、table 与条件判断。新建 Lesson20 Baseplate。
一个 RewardRules 模块与一个调用它的测试 Script。
0、1、3 颗晶石分别得到 0、10、40 分;非法输入不进入奖励计算。

Learning route
每完成一小步,就测试一次。先说出你预计会看到什么,再用 Play 和 Output 检查,不用急着一次写完所有代码。
在 ServerScriptService 建立 ModuleScript:RewardRules,再建立普通 Script:RewardDemo。模块不会自己启动,它要被 require 后使用。
RewardRules 是我们返回给调用者的工具表。rewardFor 接收晶石数量,返回分数,不直接修改 Coins 或场景。
require 得到的不是模块实例本身,而是模块返回的值。这里返回 table,所以能够调用 Rules.rewardFor(3)。
assert 是“这件事必须成立”的检查,不满足就抛出错误。这里用 pcall 包住故意传入 -1 的实验,能看到失败又不让整段测试突然结束。
如果每颗晶石改为 20 分,你应只修改模块中的计算,并同步修改测试的预期。调用者仍然使用同一个函数名。
Step by step
在 ServerScriptService 建立 ModuleScript:RewardRules,再建立普通 Script:RewardDemo。模块不会自己启动,它要被 require 后使用。
这里的奖励规则属于服务器,所以模块放在 ServerScriptService。ReplicatedStorage 可以存共享展示代码,但放在那里不等于客户端就变得可信。

Step by step
RewardRules 是我们返回给调用者的工具表。rewardFor 接收晶石数量,返回分数,不直接修改 Coins 或场景。
先验证数字、整数和范围,再计算。三颗的奖励是 3×10,再加 10 分完成奖励,所以是 40。注意 0 颗也应该是合法输入。
local RewardRules = {}
function RewardRules.rewardFor(count)
assert(typeof(count) == "number", "count must be a number")
assert(count == count and count >= 0 and count <= 3, "count out of range")
assert(math.floor(count) == count, "count must be an integer")
local reward = count * 10
if count == 3 then reward += 10 end
return reward
end
return RewardRules
Step by step
require 得到的不是模块实例本身,而是模块返回的值。这里返回 table,所以能够调用 Rules.rewardFor(3)。
同一执行环境中重复 require 同一模块通常会得到缓存结果,不会每次从头执行。但服务器与不同客户端不是共享一份运行内存,不要拿模块当跨网络通信工具。
local Rules = require(script.Parent:WaitForChild("RewardRules"))
assert(Rules.rewardFor(0) == 0, "Zero crystals")
assert(Rules.rewardFor(1) == 10, "One crystal")
assert(Rules.rewardFor(3) == 40, "Completion bonus")
print("All reward checks passed")
local ok, message = pcall(function()
return Rules.rewardFor(-1)
end)
print("Negative input accepted?", ok) -- 预期 false
if not ok then warn(message) end
Step by step
assert 是“这件事必须成立”的检查,不满足就抛出错误。这里用 pcall 包住故意传入 -1 的实验,能看到失败又不让整段测试突然结束。
count == count 看起来奇怪,是为了拒绝特殊数字 NaN,因为 NaN 不等于自己。本课不需要背它的数学来源,只要知道外部输入不能直接相信。
Step by step
如果每颗晶石改为 20 分,你应只修改模块中的计算,并同步修改测试的预期。调用者仍然使用同一个函数名。
模块划分不是越多越好。先把重复且稳定的规则抽出来,不用把每一行都拆成一个文件。
Homework
列出 0、1、2、3 颗对应分数,补上缺少的 2 颗测试。
另写一个 Demo Script 调用同一模块,不复制奖励公式。
把单颗奖励和完成奖励提为模块内部常量,让规则更容易读。
Exit questions
不会,它通常需要被 require。
本课返回装有 rewardFor 函数的 table。
不能,它不是跨网络通信,执行环境彼此独立。
让规则与场景操作分开,便于独立测试和复用。
Explore further
正文代码可以直接复制。先确认脚本类型、放置位置,以及这段代码是完整版本还是小实验;不要把同一脚本的多个版本叠加运行。界面参考图已标注来源,图中的对象名可能与本课不同,请以操作步骤为准。