Roblox Studio · 第 26 课

回合系统:开始、倒计时与结束

把游戏拆成等待、进行和结算三个状态,让后来加入的玩家也看见正确倒计时。

10–15 岁 75 分钟 状态机 / 回合 动手实践 测试与保存

Course overview

让一局游戏有等待、有开始,也有明确的结束。

一个小游戏不能永远停留在“正在进行”。回合系统把时间分成几个状态,让所有人知道现在能做什么。

本课先只做状态与倒计时,不加入得分。服务器安排阶段,客户端读取阶段和截止时间,后来加入的玩家也能看见当前状态。

01 · BEFORE

开始之前

理解服务器、LocalScript 与 Attributes。新建 Lesson26 Baseplate。

02 · MAKE

你的作品

循环运行的 8 秒准备、20 秒游戏、5 秒结算,屏幕显示阶段和剩余时间。

03 · CHECK

完成标志

两个客户端显示相同阶段;迟加入的玩家不会从 20 重新倒数。

启动 Server & Clients 测试的 Studio 控件截图
图 26-1:官方 Server & Clients 入口:用两个客户端检查它们是否跟随同一服务器回合。Roblox Creator Documentation · CC BY 4.0 · 原图未修改 · 许可说明

Learning route

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

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

1

01 · 先给每个阶段一个名字

我们用 Intermission、Playing、Results 三个字符串表示状态。它们像交通灯:每种状态允许的行为不同。

2

02 · 记录截止时刻,避免越数越慢

如果每次 task.wait(1) 后简单减 1,等待的细小误差会逐渐累积。我们改为记录“阶段在服务器时间的哪一刻结束”。

3

03 · 屏幕只计算还剩多少时间

客户端每隔 0.2 秒更新一次显示,用截止时间减当前时间,再向上取整。这样不会在阶段刚开始时少显示一秒。

4

04 · 计时显示不能代替规则检查

客户端即使显示零秒,也不能自行发奖。奖励、传送和是否接受收集,仍然由服务器当前状态决定。

5

05 · 同步测试要包含后来加入的人

两个窗口秒数可能受刷新节奏影响短暂差一格,重点是它们使用同一个截止时刻和服务器阶段,而不是各走各的倒计时。

Step by step

01 · 先给每个阶段一个名字

我们用 Intermission、Playing、Results 三个字符串表示状态。它们像交通灯:每种状态允许的行为不同。

本课只是回合时钟,Results 阶段不虚构分数或奖励。以后把收集功能接进来时,服务器应在 Playing 状态才允许计分。

  1. 创建 ServerScriptService > RoundClock(Script)。
  2. 创建 StarterPlayerScripts > RoundHud(LocalScript)。
  3. 在纸上画出准备 → 游戏 → 结算 → 准备的循环。

Step by step

02 · 记录截止时刻,避免越数越慢

如果每次 task.wait(1) 后简单减 1,等待的细小误差会逐渐累积。我们改为记录“阶段在服务器时间的哪一刻结束”。

GetServerTimeNow 提供可用于同步显示的服务器时间。task.wait 只控制检查频率,真正决定结束的是现在是否到达截止时刻。

  1. 把代码放入 RoundClock。
  2. 查看 Workspace 的 RoundState 与 RoundEndsAt 属性。
  3. 观察完整一轮大约 33 秒,再回到准备状态。
ServerScriptService > RoundClock(Script)Luau
local function runPhase(state, seconds)
    local deadline = workspace:GetServerTimeNow() + seconds
    workspace:SetAttribute("RoundEndsAt", deadline)
    workspace:SetAttribute("RoundState", state)
    print("Phase:", state)
    while workspace:GetServerTimeNow() < deadline do
        task.wait(0.1)
    end
end

while true do
    runPhase("Intermission", 8)
    runPhase("Playing", 20)
    runPhase("Results", 5)
end

Step by step

03 · 屏幕只计算还剩多少时间

客户端每隔 0.2 秒更新一次显示,用截止时间减当前时间,再向上取整。这样不会在阶段刚开始时少显示一秒。

如果属性还没传过来,就显示等待同步。它不自行启动另一个独立的 20 秒游戏计时器。

  1. 把代码放入 RoundHud。
  2. Play 后检查阶段中文与秒数。
  3. 只保留一份 RoundClock,避免两个服务器脚本互相覆盖属性。
StarterPlayer > StarterPlayerScripts > RoundHud(LocalScript)Luau
local player = game:GetService("Players").LocalPlayer
local gui = Instance.new("ScreenGui")
gui.Name = "RoundGui"
gui.ResetOnSpawn = false
local label = Instance.new("TextLabel")
label.Size = UDim2.fromOffset(300, 60)
label.Position = UDim2.new(0.5, -150, 0, 16)
label.TextSize = 22
label.Parent = gui
gui.Parent = player:WaitForChild("PlayerGui")
local names = {Intermission = "准备", Playing = "游戏进行中", Results = "本轮结束"}

while gui.Parent do
    local state = workspace:GetAttribute("RoundState")
    local deadline = workspace:GetAttribute("RoundEndsAt")
    if state and deadline then
        local remaining = math.max(0, math.ceil(deadline - workspace:GetServerTimeNow()))
        label.Text = (names[state] or state) .. " · " .. remaining .. " 秒"
    else
        label.Text = "等待回合同步…"
    end
    task.wait(0.2)
end

Step by step

04 · 计时显示不能代替规则检查

客户端即使显示零秒,也不能自行发奖。奖励、传送和是否接受收集,仍然由服务器当前状态决定。

接入任务脚本时,可以在处理函数开头检查 workspace:GetAttribute("RoundState") ~= "Playing" 并 return。但不要随意加到第 19 课后就称为完整回合游戏,还需要每轮重置任务记录。

  1. 列出每轮需要重置的数据:分数、已领物品、完成标记。
  2. 区分本轮结束与永久纪录,两种数据不应一起清零。

Step by step

05 · 同步测试要包含后来加入的人

两个窗口秒数可能受刷新节奏影响短暂差一格,重点是它们使用同一个截止时刻和服务器阶段,而不是各走各的倒计时。

服务器结束 Play 后循环会随测试停止。本课循环内部始终有等待,不会用空转占满电脑。

  1. 从准备开始观察一次完整阶段切换。
  2. 多人测试中检查两个客户端的阶段一致。
  3. 在游戏中途加入测试玩家,确认显示当前剩余时间。
  4. Stop 后保存,不留下多个测试会话。
检查回合脚本来源时使用的 Output 上下文截图
图 26-2:官方消息来源标签:回合由服务器控制,客户端只负责显示。用上下文标签定位是哪一端出错。Roblox Creator Documentation · CC BY 4.0 · 原图未修改 · 许可说明

Homework

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

1 改变节奏

把阶段改为 5、30、5 秒,计算一整轮的时长。

2 状态规则表

写出哪个阶段允许收集、哪个阶段展示结果、何时重置。

3 选做:阶段颜色

让 HUD 背景随阶段变化,同时保留中文文字,避免只靠颜色。

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

Exit questions

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

为什么保存截止时间而不是每秒减 1?

减少等待误差累积,也让后来加入的客户端能算出当前剩余时间。

谁决定回合结束?

服务器,客户端只显示状态。

每轮开始只清分数够吗?

不够,已领物品和完成记录等本轮数据也要一起重置。

这个例子已经包含结算奖励吗?

没有,它只实现阶段与同步时钟,奖励需要额外的服务器规则。

Explore further

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

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