第 9 章完成了第一個 Script-driven 行為。
玩家碰到水晶時,水晶會暫時消失,Output 會印出:
PlayerName collected Crystal_01
這代表事件成立、Script 執行、server 能判斷碰到水晶的是玩家。
但 Output 不是遊戲狀態。
Output 是開發者看的。
玩家需要的是可累積、可顯示、可被後續系統使用的狀態。
這一章要把「碰到水晶」推進成「玩家的水晶數增加」。
本章成果:
leaderstats。leaderstats 裡有一個 IntValue,名稱為 Crystals。Crystals.Value 增加 1。本章仍然不做永久保存。
玩家離開遊戲後,數值不會保存。DataStore 會在第 18 章處理。
本章也不做自訂 HUD。UI 會在第 11 章處理。
現在只要讓 server 管理最基本的玩家狀態。

圖 10-1 玩家狀態會跨越多個服務與模組;清楚的階層能避免資料邏輯散落在 Workspace 物件中。
本章依據以下 Roblox 官方文件撰寫:

圖 10-2 圖中是常見 client Script 容器;本章建立 leaderstats 與權威玩家狀態時,邏輯應放在 server,而不是只存在玩家端的 LocalScript。
官方 record-and-display player data 教學從 coin collection 延伸到玩家資料與 leaderboard。它介紹了更完整的 ModuleScript 做法。
本書這一章先採用較輕的版本:
leaderstats + IntValue
原因是本章讀者剛開始讀 Script。如果立刻加入 ModuleScript、require()、資料存取函式、Leaderboard module,會一次跨太多概念。
先把玩家狀態看見。
再談架構。
官方教學也說明,Roblox 會識別名為 leaderstats 的 Folder,並將裡面的 value objects 顯示在內建 leaderboard 上。
這讓我們不用先建立自訂 UI,就能看到數值變化。
第 9 章完成後,你應該有:
Workspace
AdventureIsland
Collectibles
Crystal_01
Crystal_02
Crystal_03
ServerScriptService
CrystalCollectionService
目前 CrystalCollectionService 只做:
touch -> print -> hide -> wait -> restore
這一章要改成:
player joins -> create leaderstats.Crystals
touch crystal -> Crystals.Value += 1
print debug message -> hide -> wait -> restore
你會第一次看到 Player 物件底下長出狀態:
Players
Wolke
leaderstats
Crystals
這不是自訂 GUI。
這是 Roblox 內建 leaderboard 機制。
它適合初學時確認 server 真的在替玩家維護數值。
請在 Assistant 輸入:
【Ch10 主任務|建立玩家狀態】
我正在製作 Roblox Studio 專案 AI Adventure Island。
目前 ServerScriptService 裡有 CrystalCollectionService。
它會處理 Workspace.AdventureIsland.Collectibles 裡 Crystal_01 到 Crystal_03 的 Touched 行為。
請幫我把第 9 章的水晶收集 prototype 升級成玩家狀態:
要求:
1. 仍然只使用 server Script,放在 ServerScriptService。
2. 玩家加入時,建立 player.leaderstats Folder。
3. 在 leaderstats 裡建立 IntValue,名稱為 Crystals,初始值為 0。
4. 玩家碰到水晶時,除了原本 Output print、透明、CanTouch=false、5 秒後恢復之外,也要讓該玩家的 Crystals.Value 增加 1。
5. 如果玩家已經有 leaderstats 或 Crystals,請不要重複建立。
6. 保留 Players:GetPlayerFromCharacter()。
7. 保留每顆水晶自己的 debounce,不要讓一顆水晶影響其他水晶。
8. 不要建立 DataStore、RemoteEvent、自訂 HUD、背包、任務系統或 ModuleScript。
完成後請:
- 貼出完整 CrystalCollectionService。
- 逐段解釋 PlayerAdded、leaderstats、IntValue、Crystals.Value。
- 說明 Playtest 時我要如何確認 leaderboard 顯示與數值增加。
這個 prompt 的重點是「升級既有 Script」。
不是重新生成所有東西。
如果 Assistant 想建立完整 PlayerData module,也先不要接受。那是正確方向,但不是這章的節奏。
本章只需要:
PlayerAdded
leaderstats
IntValue
Crystals.Value += 1
Assistant 可能產生類似這段 Script:
local Players = game:GetService("Players")
local Workspace = game:GetService("Workspace")
local adventureIsland = Workspace:WaitForChild("AdventureIsland")
local collectibles = adventureIsland:WaitForChild("Collectibles")
local RESPAWN_SECONDS = 5
local CRYSTAL_STAT_NAME = "Crystals"
local function setupLeaderstats(player)
local leaderstats = player:FindFirstChild("leaderstats")
if not leaderstats then
leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
end
local crystals = leaderstats:FindFirstChild(CRYSTAL_STAT_NAME)
if not crystals then
crystals = Instance.new("IntValue")
crystals.Name = CRYSTAL_STAT_NAME
crystals.Value = 0
crystals.Parent = leaderstats
end
return crystals
end
local function addCrystal(player, amount)
local crystals = setupLeaderstats(player)
crystals.Value += amount
return crystals.Value
end
local function onCrystalTouched(crystal, otherPart)
if not crystal.CanTouch then
return
end
local character = otherPart.Parent
local player = Players:GetPlayerFromCharacter(character)
if not player then
return
end
crystal.CanTouch = false
crystal.Transparency = 1
local newTotal = addCrystal(player, 1)
print(player.Name .. " collected " .. crystal.Name .. ". Crystals: " .. newTotal)
task.wait(RESPAWN_SECONDS)
crystal.Transparency = 0
crystal.CanTouch = true
end
Players.PlayerAdded:Connect(setupLeaderstats)
for _, player in Players:GetPlayers() do
setupLeaderstats(player)
end
for _, crystal in collectibles:GetChildren() do
if crystal:IsA("BasePart") then
crystal.CanTouch = true
crystal.Touched:Connect(function(otherPart)
onCrystalTouched(crystal, otherPart)
end)
end
end
這段比第 9 章多了幾個新概念:
setupLeaderstats(player)
Instance.new("Folder")
Instance.new("IntValue")
Players.PlayerAdded:Connect(...)
Players:GetPlayers()
crystals.Value += amount
你不用一次背下所有 API。
先看懂資料流:
玩家加入 -> 建立 Crystals
玩家碰到水晶 -> 找到 Player
Player -> 找 leaderstats.Crystals
Crystals.Value + 1
leaderboard 顯示變化
Players 是一個 service,代表目前連到 server 的玩家集合。
每位玩家進入遊戲時,server 會有對應的 Player 物件。
在 Explorer 裡,Playtest 時可以看到:
Players
PlayerName
第 9 章用過:
local Players = game:GetService("Players")
local player = Players:GetPlayerFromCharacter(character)
這行是從角色 Model 找回 Player。
第 10 章新增:
Players.PlayerAdded:Connect(setupLeaderstats)
這代表:
當玩家加入時,呼叫 setupLeaderstats(player)。
在單人 Playtest 裡你可能只看到自己一個 Player。
但你要從這章開始習慣:Roblox 遊戲預設是多人。
玩家狀態應該掛在各自的 Player 底下,而不是用一個全域數字代表所有人。
leaderstats 是 Roblox 內建 leaderboard 的約定名稱。
官方教學說明,當你在 Player 底下建立一個名為 leaderstats 的 Folder,Roblox 會把其中的 value objects 顯示在內建 leaderboard。
所以這段很重要:
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
注意大小寫。
它必須叫:
leaderstats
不是:
Leaderstats
LeaderStats
leaderStats
stats
如果名稱錯了,Folder 仍然會存在,但 leaderboard 不一定會用它顯示。
本章不需要自訂 UI,因為 Roblox 內建 leaderboard 已經足夠讓你確認數值變化。
第 11 章才會開始做 HUD。
IntValue 是一種 value object。
它的用途是把一個整數存在 Instance 裡。
本章建立:
local crystals = Instance.new("IntValue")
crystals.Name = "Crystals"
crystals.Value = 0
crystals.Parent = leaderstats
這代表玩家有一個整數狀態:
Crystals = 0
當玩家收集水晶時:
crystals.Value += 1
就會變成:
Crystals = 1
再收集一次:
Crystals = 2
IntValue 適合這章,因為水晶數量是整數。
如果你之後要記錄小數,例如經驗倍率,可以用 NumberValue。
如果要記錄文字,例如目前任務名稱,可以用 StringValue。
但本章只用 IntValue。
這段程式看起來有點長:
local leaderstats = player:FindFirstChild("leaderstats")
if not leaderstats then
leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
end
為什麼不直接每次都建立新的?
因為玩家只需要一個 leaderstats。
如果每次碰到水晶都建立一次,Explorer 會變得混亂,leaderboard 也可能出錯。
所以先用:
FindFirstChild("leaderstats")
檢查它是否已存在。
不存在才建立。
Crystals 也一樣:
local crystals = leaderstats:FindFirstChild(CRYSTAL_STAT_NAME)
if not crystals then
-- create IntValue
end
這是很常見的 AI 生成後整理重點。
你要要求 Assistant 不要盲目重複建立 Instance。
這行處理新玩家:
Players.PlayerAdded:Connect(setupLeaderstats)
但 Script 啟動時,可能已經有玩家存在。
所以範例裡又加了:
for _, player in Players:GetPlayers() do
setupLeaderstats(player)
end
這是一個穩健寫法。
在 Studio Playtest 裡通常影響不大,但它建立了好的習慣:
處理未來加入的玩家,也處理現在已存在的玩家。
當你之後寫更複雜的系統,例如任務、背包、UI 初始化,這個習慣很重要。
現在進入 Play mode。
測試步驟:
1. 打開 Explorer。
2. 展開 Players。
3. 展開你的 Player。
4. 確認有 leaderstats。
5. 展開 leaderstats。
6. 確認有 Crystals,Value = 0。
7. 打開 Output。
8. 走去碰 Crystal_01。
9. 確認 Crystals.Value 變成 1。
10. 確認 leaderboard 顯示 Crystals = 1。
11. 碰 Crystal_02,確認數值變成 2。
預期 Output:
PlayerName collected Crystal_01. Crystals: 1
PlayerName collected Crystal_02. Crystals: 2
如果 Output 有訊息,但 leaderboard 沒變,檢查:
leaderstats 名稱是否正確。Crystals 是否是 IntValue。Crystals.Parent 是否是 leaderstats。leaderstats.Parent 是否是 player。如果 leaderboard 有 Crystals 但數值不增加,檢查:
addCrystal(player, 1) 是否被呼叫。crystals.Value += amount 是否存在。player 是否正確由 GetPlayerFromCharacter() 找到。如果每次碰水晶都回到 1,檢查:
IntValue。FindFirstChild("Crystals")。如果 leaderboard 沒出現:
【Ch10 修正 1|顯示 leaderboard 數值】
我 Playtest 後 Players.PlayerName 底下有資料,但 Roblox 內建 leaderboard 沒有顯示 Crystals。
請檢查 CrystalCollectionService:
1. Folder 名稱是否精確為 leaderstats。
2. leaderstats 是否 parent 到 player。
3. Crystals 是否是 IntValue。
4. Crystals 是否 parent 到 leaderstats。
5. 不要建立自訂 HUD。
6. 不要新增 DataStore、RemoteEvent 或 ModuleScript。
如果數值不增加:
【Ch10 修正 2|修復水晶計數增加】
我碰到水晶時 Output 有印出 collected,但 Crystals.Value 沒有增加。
請檢查:
1. onCrystalTouched 是否有取得 player。
2. 是否有呼叫 addCrystal(player, 1)。
3. addCrystal 是否會找到或建立 leaderstats.Crystals。
4. 是否有執行 crystals.Value += amount。
5. 請保持本章範圍,只修正 player state,不要做 DataStore。
如果 Assistant 加太多架構:
【Ch10 修正 3|簡化玩家狀態版本】
這章只需要 leaderstats + IntValue 入門版本。
請移除或不要使用:
- PlayerData ModuleScript
- Leaderboard ModuleScript
- DataStoreService
- RemoteEvent
- custom HUD
- inventory / quest system
請保留:
- Players.PlayerAdded
- setupLeaderstats(player)
- leaderstats Folder
- Crystals IntValue
- CrystalCollectionService 中收集水晶時 Crystals.Value += 1
如果多顆水晶互相干擾:
【Ch10 修正 4|分離每顆水晶狀態】
收集 Crystal_01 後,其他水晶也無法被收集。
請檢查 debounce 是否是每顆水晶各自管理。
要求:
1. 不要使用單一全域 debounce 阻擋所有水晶。
2. 每顆水晶收集後只停用自己的 CanTouch。
3. 其他水晶仍然可以被收集。
4. 保持 Crystals.Value 正確增加。
完成本章後,整理目前狀態:
ServerScriptService
CrystalCollectionService
Players
PlayerName
leaderstats
Crystals
在你的筆記或章節紀錄中寫下:
State name: Crystals
Type: IntValue
Owner: Player
Created by: CrystalCollectionService
Updated by: onCrystalTouched
Visible through: Roblox built-in leaderboard
Persistence: none yet
特別標記:
Persistence: none yet
因為這章的玩家狀態只存在於當前 server session。
如果玩家離開、server 關閉、重新 Playtest,數值會歸零。
這不是 bug。
這是本章刻意邊界。
DataStore 會在後面處理。
現在先確定:
這一章,你把第 9 章的 Output prototype 變成玩家狀態。
你學到:
Players service 管理目前連線的玩家。PlayerAdded 可用來初始化玩家狀態。leaderstats 是 Roblox 內建 leaderboard 的約定 Folder 名稱。IntValue 可以存放整數狀態。Crystals.Value += 1 讓收集行為變成可累積數值。Player 底下,而不是用一個全域數字代表所有玩家。到這裡,AI Adventure Island 已經有第一個玩家可見的數值。
下一章,我們會處理 UI:不是只讓 Roblox 內建 leaderboard 顯示數字,而是讓遊戲畫面用 HUD、任務提示或回饋文字,把玩家狀態轉成更好的遊戲體驗。
嗨!我是 Wolke,曾任 Google Developer Expert(GDE,2019–2023) 與 LINE API Expert。
我熱衷於研究 AI Agent、n8n 自動化工作流與全端開發架構,致力於將 AI 技術轉化為真正能落地的生產力工具。
如果你喜歡這篇文章,歡迎透過以下方式與我交流:
📚 技術著作
《實用的 Gemini API 開發點子書》:帶你運用 Gemini App、Google AI Studio、Gemini CLI 與 Antigravity IDE,打造 AI Agent 與實用產品。
📝 技術部落格
歡迎追蹤我的 Medium,我會持續分享 Agentic Automation、架構設計與實際開發的踩坑心得。
🎤 技術講座與合作
我持續受邀至技術社群及研討會,分享 AI Agent、自動化工作流、DevOps 與全端開發實戰。
我曾於 DevOpsDays Taipei 2026 主講「不再只是寫腳本!讓 AI 代理人成為你的 SRE 最佳夥伴」工作坊。
如果你的企業、社群或學校正在尋找相關主題講者,歡迎私訊聯繫,洽談講座與工作坊合作!
🎮 我的 Roblox 遊戲
🎁 免費贈送 OpenAI 或 Claude AI 額度
為了鼓勵大家實際動手打造自己的 Roblox 體驗,我每個月會開放:
參加方式:
確認完成後,我會邀請你加入並設定 50 點額度。名額有限,歡迎把握機會!