Roblox 遊戲開發最佳實踐
與框架無關的標準,用於編寫簡潔、高效、輕量且節省資源的 Luau 程式碼。這些規則適用於任何架構(單一腳本、基於模組、Knit、Actor 架構、ECS 等)——它們規範的是 每個腳本如何撰寫,而非專案如何組織。
目標,按優先順序排列: 正確 → 安全(伺服器權威) → 高效(CPU/記憶體/網路) → 可讀 → 一致。
參考資料導航
僅載入當前情況所需的內容:
| 情況 | 閱讀 |
|---|---|
| 撰寫新的 Script/LocalScript/ModuleScript | references/templates.md |
| 具有自身慣例的現有程式碼庫(自適應模式) | references/adaptive-mode.md |
| 專案使用社群函式庫(ProfileStore, Packet, Trove, Knit, Fusion, ...) | references/community-libraries.md |
| 熱循環、記憶體、網路流量、渲染、效能分析 | references/performance.md |
| 數據儲存、RemoteEvent/RemoteFunction、清理、物件池、輸入、反模式 | references/patterns.md |
| 購買、反外掛、Remote 驗證深度 | references/security-monetization.md |
| UI/UX、跨平台、測試、除錯、遙測 | references/ui-ux-testing.md |
| 已知遊戲類型(模擬、FPS、闖關、RPG、賽車、恐怖、社交) | references/genres.md |
用戶權限
此技能是指導方針,而非強制命令——完全控制權始終歸用戶所有:
- 用戶的明確指示優先於此技能中的任何慣例。如果某個指示與「不可協商的執行時規則」衝突,請簡要說明一次風險,然後遵循用戶的決定。
- 絕不要僅憑此技能就執行用戶未要求的操作:不得進行未經請求的重構、重新組織、創建檔案或「順手」清理。可以推薦,但不要行動。
建議性調用(無特定任務)
用戶可能僅將此技能作為一個常駐提醒來調用(例如「使用最佳實踐」、「從現在開始遵循此技能」),而沒有具體的編碼任務。在這種情況下:
- 不要 開始分析程式碼庫或詢問模式/函式庫設定的問題。簡短確認標準已生效,然後停止。
- 將這些規則作為後續會話中所有 Luau 工作的活躍指導方針。
- 延遲解析模式選擇和社群函式庫檢查——在遇到第一個實際的編碼/審查任務時,且僅需執行該任務所需的部分。
監督等級(確認頻率)
用戶控制代理在行動前需要詢問多少次。三個等級:
| 等級 | 代碼 | 行為 |
|---|---|---|
| 受監督 | !ask | 在每個有意義的決定前確認:慣例選擇、要創建/修改的檔案列表、任何偏離此技能的行為,以及在編寫程式碼之前。用戶會看到並批准所有內容。 |
| 平衡(預設) | !bal | 僅在真正需要時詢問:實際的歧義、與「不可協商的執行時規則」衝突,或影響廣泛/具破壞性的更改。否則直接進行。 |
| 自主 | !go | 不要詢問;做出合理的、最佳實踐的決定,並在最終摘要中記錄所有假設。僅對破壞性/不可逆的行動停下來確認。 |
等級設定方式:
- 會話宣告——用戶以任何措辭聲明(如「監督模式」、「全程監督」、「別問太多」、「隨意」)→ 在整個會話期間有效,直到變更。
- 行內代碼——在提示詞中的任何位置使用
!ask/!bal/!go→ 僅針對該提示詞覆蓋會話等級。
優先順序: 行內代碼 > 會話宣告 > 平衡預設值。絕不要詢問用戶他們想要哪個等級——沒有宣告 即 等同於選擇了平衡。明確的用戶指示(用戶權限)優先於等級本身。
對本技能確認點的影響:
| 確認點 | 受監督 | 平衡 | 自主 |
|---|---|---|---|
| 預設/自適應模式問題 | 總是詢問 | 如果程式碼庫存在則詢問一次 | 推斷;報告假設 |
| 自適應慣例確認(第二步) | 等待批准 | 等待批准 | 作為報告呈現;繼續進行 |
| 社群函式庫檢查 | 詢問 | 詢問一次 / 檢測 | 透過 require() 調用檢測 |
| 與不可協商規則衝突 | 詢問 | 詢問 | 在摘要中警告;選擇安全的選項 |
| 審查模式:風格重構 | 提議,等待 | 提議,等待 | 仍然只提議(用戶權限——不變) |
模式選擇(在首次編碼任務之前)
此技能以兩種模式之一運行。在編寫任何程式碼之前確定模式:
- 預設模式——完全按照下文所述應用本技能的慣例(章節佈局、命名、順序)。在以下情況使用:用戶要求這樣做、專案是新的/空的、或現有程式碼沒有值得保留的一致慣例。
- 自適應模式——首先研究專案現有的編碼結構和慣例,展示您的發現以及提議的改編慣例,獲得用戶確認,然後按照已確認的慣例編寫程式碼。通用規則(不可協商的執行時規則、語言和風格安全項目、效能/模式參考中的所有內容)仍然完全適用——僅 風格/結構性 慣例進行調整。
如何決定:
- 如果用戶明確指定了模式(例如,「使用預設」、「遵循此專案結構」、「先研究我們的程式碼」)→ 遵從它。
- 否則,如果存在具有明顯慣例的現有程式碼庫 → 向用戶詢問一次:「使用此技能的預設慣例,還是應該先研究您專案的現有結構並適應它(在您確認後)?」
- 如果無法詢問(自主運行)→ 新檔案使用預設模式;對於編輯現有檔案,匹配該檔案現有的風格,並在您的摘要中記錄此假設。
自適應模式流程(分析檢查表、確認格式、優先順序規則):請參閱 references/adaptive-mode.md。
社群函式庫檢查(模式選擇的一部分)
同時確定一次專案是否使用了取代內建 API 的社群函式庫——詢問用戶(「此專案是否使用諸如 ProfileStore/ProfileService 處理數據、Packet/ByteNet 處理網路、Trove/Maid 處理清理、Knit/Flamework、Fusion/React-lua 等社群函式庫?」),或者在自主運行時,透過掃描 require() 調用進行檢測。如果使用了任何函式庫,請閱讀 references/community-libraries.md,並將重疊的內建模式委託給該函式庫——函式庫慣例在其所負責的領域獲勝;不可協商的執行時規則仍然透過它們得到強制執行。
審查/重構模式
當被要求 審查或整理現有程式碼(而非編寫新程式碼)時:
- 違反不可協商的執行時規則和已棄用 API 的行為 → 作為發現報告(並在要求時修復)。按範圍應用規則——其中寫入的例外(週期性循環、冷路徑分配、小型狀態快照)不視為違規。
- 章節佈局/命名偏差以及對微不足道的私有函數缺少文件註解 → 提議作為小的建議進行重構,不要默默地重寫,也不要將其報告為違規;由用戶決定。
- 絕不要重新格式化與請求無關的程式碼;檔案內的一致性優於與此技能的一致性。
- 在將某個 API 標記為錯誤/不存在之前,請針對目標環境進行驗證(請參閱環境與規模)——絕不要僅憑記憶標記。
環境與規模
- 首先檢測專案環境: Studio 原生(透過 Studio/MCP 工具工作;路徑為 Instance 路徑) vs Rojo/檔案系統(透過檔案工作;require 可能使用路徑別名和映射到服務的
src/佈局)。相應地匹配您讀取、寫入和引用腳本的方式。 - 在使用較新的 API 之前進行驗證(
BindToSimulation、UIShadow、輸入操作系統、結構化的LogService...)——檢查它們在目標環境中是否存在,而不是假設;如果不存在,則回退到穩定的等效項。官方文檔(create.roblox.com — 引擎 API 參考)是主要權威;API 轉儲/ReflectionService 或快速的 Studio 內測試可以解決文檔尚未涵蓋的問題。Roblox 持續發布新 API——您的訓練知識中缺失並不證據該 API 不存在。 - 根據腳本規模調整儀式感。 小型腳本(< ~40 行)可能僅使用三個頂層標題而無子章節;僅當某個章節有足夠的內容需要它們時才添加二級標題。絕不要發出空的佔位符標題。純數據/類型模組(配置表、物品目錄、共享類型定義——無執行時邏輯)完全免除三章節佈局;以最易讀的方式組織其內容。
腳本章節佈局(強制性)
每個腳本必須按此順序分為三個頂層章節:
-- // 變數 // --
-- // 函數 // --
-- // 初始化 // --
章節標題層次
五個巢狀層級。僅在章節確實需要細分時才使用更深層級:
-- // 第一層 // -- 頂層章節(僅限 變數 / 函數 / 初始化)
-- | 第二層 | -- 標準子章節(服務、模組、私有、公開、...)
-- [ 第三層 ] -- 子章節內的分組
-- { 第四層 } -- 罕見的細粒度分組
-- / 第五層 / -- 最罕見,最後的手段
1. -- // 變數 // --
按此固定順序的子章節(省略任何為空的):
| 子章節 | 內容 |
|---|---|
-- | 服務 | -- | 透過 game:GetService() 取得的 Roblox 服務,每行一個,僅使用實際用到的 |
-- | 模組 | -- | require() 調用,按源位置排序:ServerScriptService → ServerStorage → ReplicatedStorage → Workspace,最後是腳本相對路徑的 require(script.Parent.X)。Packages/ServerPackages 資料夾按其包含的服務排序。僅適用於腳本可合法達到的位置(客戶端腳本跳過伺服器位置) |
-- | 物件 | -- | 指向 Instance 的引用(模型、資料夾、Remote、UI)。可選——僅在需要時使用 |
-- | 設定 | -- | 在腳本中使用的常數和可調整數值。使用 UPPER_SNAKE_CASE |
-- | 狀態管理 | -- | 可變的執行時狀態變數(計數器、快取、標誌、連接表) |
2. -- // 函數 // --
- ModuleScript 將函數分為
-- | 私有 | --(僅在該腳本內部使用,local function)和-- | 公開 | --(暴露在返回的表上)。私有函數在前。 - Script/LocalScript 通常跳過私有/公開的拆分——只需在章節標題下列出函數(如果腳本很大,使用二級標題按主題分組)。
- 每個函數都必須有文件註解,總是包裹在一個位於函數正上方的
--[[ ... ]]塊中——即使描述只有一行。結構,按此固定順序:描述 → 參數 → 返回。- 描述 — 技術性、簡潔、清晰的英文。它必須描述函數的 一般用途/契約,而非其當前的實現:切勿提及或討論函數體內特定的功能、API、演算法或程式碼路徑。以這種方式撰寫,當函數內容改變時,描述仍然保持有效和相關。
@param/@return— 僅在它們提供了簽名之外的信息時才包含(不明確的含義、單位、約束、nil 行為);當這些信息顯而易見時則完全省略。
--[[
對角色應用傷害並處理導致的死亡狀態。
@param damage 生命值傷害量;必須為正數
@return 當傷害是致命時返回 true
]]
local function applyDamage(humanoid: Humanoid, amount: number): boolean
...
end
反面示例(過於特定於實現——只要主體改變就會失效):從 Humanoid.Health 減去 amount,然後如果生命值降至零則觸發布娃娃模組。
- 按依賴關係對函數進行排序,使被依賴者位於依賴者之上(callee above caller)——Luau 無論如何都需要局部變數如此。
3. -- // 初始化 // --
所有 執行 的部分:函數調用、事件連接、循環。此處不包含函數定義。當腳本處理多個關注點時,使用二級子章節按上下文進行分組:
-- // 初始化 // --
-- | 玩家事件 | --
Players.PlayerAdded:Connect(onPlayerAdded)
Players.PlayerRemoving:Connect(onPlayerRemoving)
-- | Remote | --
purchaseRemote.OnServerEvent:Connect(onPurchaseRequest)
-- | 啟動 | --
loadWorldState()
完整的註釋模板(Script、LocalScript、ModuleScript):請參閱 references/templates.md。
語言和風格規則
- 每個腳本以
--!strict開頭(或僅在 strict 不實用時使用--!nonstrict)。對公開函數簽名、設定常數和狀態表進行類型註解。 - 命名:
PascalCase用於服務和 required 的模組表;camelCase用於局部變數、函數和 Instance 引用(purchaseRemote、coinLabel);UPPER_SNAKE_CASE用於設定常數。模組公開方法使用PascalCase(Inventory.AddItem),私有函數使用camelCase。 - 始終使用
game:GetService()——絕不要使用game.Workspace風格的直接索引(例外:workspace全域變數是允許的)。 - 絕不使用已棄用的 API:
wait()/spawn()/delay()→ 使用task.wait()/task.spawn()/task.delay();Instance.new(class, parent)雙參數形式 → 先設置屬性,最後設置父級;:connect()/:wait()小寫 → 使用:Connect()/:Wait();BodyVelocity/BodyGyro→ 使用約束(LinearVelocity、AlignOrientation)。 - 使用
pcall和重試策略來保護外部/產生的調用(DataStore、MarketplaceService、HttpService、TeleportService)。絕不要讓未經保護的產生效能崩潰玩家流程。 - 每個 ModuleScript 只負責一個職責。