前面幾章已經讓 AI Adventure Island 有了場景、可蒐集的水晶、HUD、RemoteEvent,以及一套可以拿去問 Assistant 的除錯方法。接下來要把這些零件組成第一條真正的遊戲流程:玩家和 NPC 接任務,蒐集指定物品,回報任務,取得獎勵。
這一章的重點不是「寫很多劇情」。任務系統的核心是狀態。玩家現在是尚未接任務、正在進行、可以回報,還是已完成?只要狀態定義清楚,NPC 對話、HUD 文字、獎勵發放都會變得可控。
完成本章後,你會得到第一條完整任務線:
Gold 獎勵。你也會學到三個新的設計觀念:
ProximityPromptService:集中管理世界中的互動 prompt。ModuleScript:把任務資料從流程程式中抽出來。
圖 14-1 任務系統要把目標、進度、回饋與獎勵串成清楚流程,程式實作前先確認玩家能理解下一步。
本章使用下列 Roblox 官方文件作為 context:
這些文件提供本章需要的四個基礎:
ProximityPrompt 可以放在 NPC、Part、Attachment 或 Model 下,讓玩家靠近後按鍵互動。ModuleScript 可以回傳一個 table,用來存放任務資料與共用設定。leaderstats 可以用 IntValue 顯示玩家資料,例如 Crystals 與本章新增的 Gold。目前島上已經有一個 NPC_QuestGiver_Placeholder,也有 3 顆可蒐集的水晶。這些元件目前只是存在於同一個場景裡,還沒有形成一個可理解的目標。
我們要把這些元件串成第一條任務:
Guide: Can you collect 3 crystals for me?
Player collects crystals.
Guide: Thank you. Here is 25 gold.
這看起來像對話,其實背後是四個狀態:
NotStarted -> InProgress -> ReadyToTurnIn -> Completed
不要用一個 HasQuest = true 就結束。任務流程通常至少要知道「還沒接」、「接了但未完成」、「可以回報」、「已完成」。如果只用布林值,後面加入多步驟任務、獎勵、重複提示時會很快失控。
先請 Assistant 建立最小可玩的任務系統。
【Ch14 主任務|建立任務系統】
我們要繼續開發 Roblox Studio 專案「AI Adventure Island」。
現有專案背景:
- Workspace.AdventureIsland 裡有一個名為 NPC_QuestGiver_Placeholder 的 NPC。
- 玩家已經可以蒐集水晶。
- Server 程式已經會建立 leaderstats.Crystals。
- StarterGui.AdventureHUD 裡有可顯示任務目標與提示文字的 TextLabel。
- ReplicatedStorage.Remotes 已經存在。
- 目前先不要加入 DataStore。
請建立第一套任務系統:
1. 為 NPC_QuestGiver_Placeholder 新增或更新 ProximityPrompt。
2. 在 server 使用 ProximityPromptService 處理這個 prompt。
3. 在 ServerStorage 建立名為 QuestDefinitions 的 ModuleScript。
4. 定義一個名為 CollectCrystalsForGuide 的任務:
- RequiredCrystals = 3
- RewardGold = 25
- ObjectiveText = "替嚮導蒐集 3 顆水晶"
- ReadyText = "回去找嚮導"
- CompletedText = "任務完成"
5. 建立或更新 leaderstats,讓每位玩家都有初始值為 0 的 Gold。
6. 在 server 使用 GuideQuestState attribute 儲存每位玩家的任務狀態。
7. 只能使用以下狀態:
- NotStarted
- InProgress
- ReadyToTurnIn
- Completed
8. 在 ReplicatedStorage.Remotes 新增名為 QuestUpdate 的 RemoteEvent。
9. 當玩家和 Guide 交談時:
- NotStarted:改成 InProgress,並將任務目標文字傳送到玩家的 HUD。
- InProgress 且少於 3 顆水晶:傳送訊息,告訴玩家還需要幾顆水晶。
- InProgress 且至少有 3 顆水晶:完成任務、給予 25 Gold、改成 Completed,並傳送任務完成文字。
- Completed:傳送感謝訊息,但不要再次給予 Gold。
10. 更新現有的 AdventureHUD LocalScript,讓它監聽 QuestUpdate 並顯示訊息。
請維持簡單的 prototype 規模。不要重寫水晶蒐集程式,也不要信任 client 對任務是否完成的判斷。
這個 prompt 明確限制了 Assistant 的工作範圍。它不是叫 Assistant 「做一個任務系統」,而是告訴它:
這種 prompt 比較長,但對工程型任務很重要。當你要讓 AI 生成一套「會持續成長」的系統時,prompt 不能只描述畫面結果,也要描述資料歸屬與責任邊界。
Assistant 可能會建立或修改這些物件:
ServerStorage
└── QuestDefinitions (ModuleScript)
ServerScriptService
├── PlayerStatsService 或既有 player stats script
└── QuestService (Script)
ReplicatedStorage
└── Remotes
└── QuestUpdate (RemoteEvent)
Workspace
└── AdventureIsland
└── NPCs
└── NPC_QuestGiver_Placeholder
└── GuideQuestPrompt (ProximityPrompt)
StarterGui
└── AdventureHUD
└── AdventureHUDController (LocalScript)
QuestDefinitions 可能長得像這樣:
local QuestDefinitions = {
CollectCrystalsForGuide = {
RequiredCrystals = 3,
RewardGold = 25,
ObjectiveText = "Collect 3 crystals for the Guide",
ReadyText = "Return to the Guide",
CompletedText = "Quest complete",
},
}
return QuestDefinitions
這是 ModuleScript 最適合初學者理解的用法:先不要把它做成複雜服務,只把「任務資料」集中放在一個 table 裡。
QuestService 的核心邏輯可能像這樣:
local Players = game:GetService("Players")
local ProximityPromptService = game:GetService("ProximityPromptService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local remotes = ReplicatedStorage:WaitForChild("Remotes")
local questUpdate = remotes:WaitForChild("QuestUpdate")
local questDefinitions = require(ServerStorage:WaitForChild("QuestDefinitions"))
local QUEST_ID = "CollectCrystalsForGuide"
local QUEST_STATE_ATTRIBUTE = "GuideQuestState"
local function getQuestDefinition()
return questDefinitions[QUEST_ID]
end
local function sendQuestMessage(player, message)
questUpdate:FireClient(player, message)
end
local function getStat(player, statName)
local leaderstats = player:FindFirstChild("leaderstats")
if not leaderstats then
return nil
end
return leaderstats:FindFirstChild(statName)
end
local function ensureQuestState(player)
local state = player:GetAttribute(QUEST_STATE_ATTRIBUTE)
if not state then
player:SetAttribute(QUEST_STATE_ATTRIBUTE, "NotStarted")
return "NotStarted"
end
return state
end
local function completeQuest(player, quest)
local gold = getStat(player, "Gold")
if gold then
gold.Value += quest.RewardGold
end
player:SetAttribute(QUEST_STATE_ATTRIBUTE, "Completed")
sendQuestMessage(player, quest.CompletedText .. " +" .. quest.RewardGold .. " Gold")
end
local function handleGuideQuest(player)
local quest = getQuestDefinition()
local state = ensureQuestState(player)
local crystals = getStat(player, "Crystals")
if state == "NotStarted" then
player:SetAttribute(QUEST_STATE_ATTRIBUTE, "InProgress")
sendQuestMessage(player, quest.ObjectiveText)
return
end
if state == "InProgress" then
local crystalCount = crystals and crystals.Value or 0
if crystalCount >= quest.RequiredCrystals then
completeQuest(player, quest)
else
local remaining = quest.RequiredCrystals - crystalCount
sendQuestMessage(player, "You still need " .. remaining .. " more crystals.")
end
return
end
if state == "ReadyToTurnIn" then
completeQuest(player, quest)
return
end
if state == "Completed" then
sendQuestMessage(player, "Thanks again for helping the island.")
end
end
Players.PlayerAdded:Connect(function(player)
ensureQuestState(player)
end)
ProximityPromptService.PromptTriggered:Connect(function(promptObject, player)
if promptObject.Name ~= "GuideQuestPrompt" then
return
end
handleGuideQuest(player)
end)
這段程式還不是最終架構,但已經比「把所有東西寫在 NPC 底下的一個 Script」更清楚。它把四件事拆開了:
QuestDefinitions。QuestService。leaderstats。QuestUpdate 通知 client。如果 Assistant 沒有自動替玩家新增 Gold,可以用修正 prompt 補上:
請更新現有的玩家數值設定,讓每位玩家都有 leaderstats.Gold,型別為 IntValue,初始值為 0。
不要移除 leaderstats.Crystals,也不要替它重新命名。
這項邏輯必須保留在 server。
HUD 的 LocalScript 則只需要顯示 server 傳來的文字:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remotes = ReplicatedStorage:WaitForChild("Remotes")
local questUpdate = remotes:WaitForChild("QuestUpdate")
local screenGui = script.Parent
local objectiveLabel = screenGui:WaitForChild("ObjectiveLabel")
local hintLabel = screenGui:WaitForChild("HintLabel")
questUpdate.OnClientEvent:Connect(function(message)
objectiveLabel.Text = message
hintLabel.Text = message
end)
這裡要注意一件事:HUD 只顯示結果,不決定結果。玩家有沒有完成任務、要不要拿獎勵,必須由 server 判斷。
ProximityPrompt 是任務的入口。玩家靠近 Guide,畫面出現按鍵提示,按下後觸發互動事件。

圖 14-2 把 ProximityPrompt 放在 Guide 模型的 Attachment 下,可精準控制提示出現的位置;同時保留清楚名稱方便 QuestService 尋找。
建議讓 Assistant 設定這些屬性:
Name = GuideQuestPrompt
ObjectText = Guide
ActionText = Talk
KeyboardKeyCode = E
HoldDuration = 0
MaxActivationDistance = 10
RequiresLineOfSight = false
ObjectText 告訴玩家互動對象是誰,ActionText 告訴玩家會做什麼。MaxActivationDistance 太大會讓提示很吵,太小則玩家找不到互動點。對初版 NPC 來說,10 studs 是容易測試的距離。
前面第 8 章可以直接在單一物件上接 prompt.Triggered。本章開始改用 ProximityPromptService.PromptTriggered,原因是任務系統會長大。
現在只有 Guide,後面會有商店、門、傳送點、寶箱。如果每個 prompt 都放自己的 Script,Explorer 會變得很分散。集中到 QuestService 或互動管理服務,可以讓你清楚看到「玩家按下 prompt 後,server 做了什麼」。
QuestDefinitions 不執行任務流程,只描述任務資料。
RequiredCrystals = 3
RewardGold = 25
ObjectiveText = "Collect 3 crystals for the Guide"
這種資料不應該散落在多個 if statement 裡。當你之後要把需求改成 5 顆水晶,或把獎勵改成 50 Gold,只要改一個地方。
本章使用玩家 attribute 保存任務狀態:
player:SetAttribute("GuideQuestState", "InProgress")
Attribute 適合保存簡單狀態字串。這裡先不用 DataStore,因為我們還在建立玩法原型;關閉 Play Solo 後進度消失是可以接受的。等任務流程穩定後,再進入資料保存。
狀態要用清楚的字串,而不是模糊的變數名稱:
NotStarted
InProgress
ReadyToTurnIn
Completed
ReadyToTurnIn 在這個最小版本中可以不一定被長時間保存,因為玩家和 Guide 對話時,如果水晶已足夠就直接完成。不過保留這個狀態名稱很有價值。後面如果你想讓 HUD 在玩家蒐集到第 3 顆水晶時立刻改成「Return to the Guide」,就可以把狀態改為 ReadyToTurnIn。
QuestUpdate 是 server 對 client 的通知管道。
questUpdate:FireClient(player, "Collect 3 crystals for the Guide")
這不是讓 client 回報任務完成的通道。client 不能說「我完成了,請給我獎勵」。client 只能接收 server 已經判斷好的任務文字。
這和第 12 章的原則一致:client 可以請求,server 必須驗證。
第 10 章已經建立 leaderstats.Crystals。本章新增 Gold,讓任務完成後有明確回饋。
Gold 也是 IntValue:
local gold = Instance.new("IntValue")
gold.Name = "Gold"
gold.Value = 0
gold.Parent = leaderstats
這個數值會在 Roblox 預設 leaderboard 上顯示。後面的商店章節可以直接沿用它,讓任務獎勵接上購買流程。
進入 Play Solo 後,依照下面順序測試。
第一輪:尚未蒐集水晶
Talk prompt。Collect 3 crystals for the Guide。第二輪:蒐集水晶
Gold 應增加 25。第三輪:防止重複領獎
Gold 不應再次增加。這一輪很重要。任務系統最常見的 bug 不是不能完成,而是完成後可以反覆領獎。
如果測試時發現重複給獎,使用這個 prompt:
【Ch14 修正 1|阻止任務重複獎勵】
Guide 任務目前可以重複完成,而且每次都會給予 Gold。
預期結果:
- GuideQuestState 變成 Completed 後,再和 Guide 交談只會顯示感謝訊息。
- Gold 不應再次增加。
目前行為:
- 任務完成後,Gold 仍會再次增加。
請只檢查 QuestService。
請加入防護條件:如果 GuideQuestState 已經是 Completed,completeQuest 就不能再次給予獎勵。
不要修改 QuestDefinitions、水晶蒐集功能或 HUD。
如果 prompt 沒有出現,使用這個 prompt:
【Ch14 修正 2|顯示 Guide 任務提示】
在 Play Solo 中,Guide NPC 沒有顯示 ProximityPrompt。
預期結果:
- NPC_QuestGiver_Placeholder 應該有一個名為 GuideQuestPrompt 的 ProximityPrompt。
- 這個 prompt 的 ObjectText 應顯示「嚮導」,ActionText 應顯示「交談」。
請檢查:
- prompt 的 parent 是否為 BasePart、Attachment,或已設定 PrimaryPart 的 Model。
- MaxActivationDistance 是否合理。
- Enabled 是否為 true。
只修正 prompt 的放置位置與 properties。
如果 HUD 沒有更新,使用這個 prompt:
【Ch14 修正 3|同步任務 HUD 訊息】
QuestService 已經輸出訊息或觸發 QuestUpdate,但 AdventureHUD 沒有顯示任務訊息。
預期結果:
- QuestUpdate.OnClientEvent 會更新 ObjectiveLabel 與 HintLabel。
請只檢查:
- ReplicatedStorage.Remotes.QuestUpdate
- StarterGui.AdventureHUD.AdventureHUDController
- AdventureHUD 底下的 label 名稱
不要修改 server 的任務狀態邏輯。
修正 prompt 的重點是限制範圍。任務系統牽涉 server、client、NPC、HUD、玩家資料。如果你只說「任務壞了,幫我修」,Assistant 很可能重寫太多東西。
完成本章後,Explorer 應該維持這種分工:
ServerStorage
└── QuestDefinitions
ServerScriptService
├── PlayerStatsService
├── CrystalCollectionService
├── GuideHintService
└── QuestService
ReplicatedStorage
└── Remotes
├── RequestGuideHint
└── QuestUpdate
StarterGui
└── AdventureHUD
└── AdventureHUDController
如果 Assistant 把 QuestDefinitions 放在 ReplicatedStorage,也不是一定錯。若 client 也需要讀取任務名稱、描述、圖示,放在 ReplicatedStorage 可以共享。但本章的任務判斷都在 server,為了降低初學者誤把 client 當權威的機率,先放 ServerStorage 比較清楚。
整理時請檢查:
QuestService 不應該放在 NPC 下面。LocalScript 不應該直接加 Gold。GuideQuestState。QuestDefinitions 不應該 require QuestService。QuestService 可以 require QuestDefinitions。這些規則看起來嚴格,但它們能避免專案在第 20 章之後變成一團互相依賴的腳本。
本章把前面學過的互動、事件、玩家資料與 HUD 串成第一條完整任務線。
你建立了:
QuestDefinitions ModuleScript。QuestService。QuestUpdate RemoteEvent。Gold 獎勵。更重要的是,你開始用狀態機理解遊戲流程。
NotStarted -> InProgress -> ReadyToTurnIn -> Completed
只要這條線清楚,任務就不再只是「NPC 說一句話」。它會變成一個能被測試、能被修正、能被擴充的系統。
下一章會繼續沿著這條任務線前進:把任務從一次性流程擴充成可以控制關卡進度的系統,例如解鎖新區域、開門、觸發下一段探索路線。
嗨!我是 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 點額度。名額有限,歡迎把握機會!