Roblox ゲーム開発のベストプラクティス は何をしますか?
クリーンで効率的、軽量かつリソース節約型の Luau コードを記述するためのフレームワーク非依存の標準。これらのルールは、シングルスクリプト、モジュールベース、Knit、アクターベース、ECS など、あらゆるアーキテクチャに適用されます。これらはプロジェクトの構造ではなく、各スクリプトの記述方法を規定します。
優先順位(重要度順): 正確性 → 安全性(サーバー権限の維持) → 効率性(CPU/メモリ/ネットワーク) → 可読性 → 一貫性。
リファレンスルーティング
状況に応じて必要なものだけを読み込みます:
| 状況 | 読むもの |
|---|---|
| 新しい Script/LocalScript/ModuleScript を書く場合 | references/templates.md |
| 独自の規則を持つ既存のコードベース(アダプティブモード) | references/adaptive-mode.md |
| プロジェクトがコミュニティライブラリを使用している場合(ProfileStore、Packet、Trove、Knit、Fusion など) | references/community-libraries.md |
| ホットループ、メモリ、ネットワークトラフィック、レンダリング、プロファイリング | references/performance.md |
| データストア、リモート、クリーンアップ、プーリング、入力、アンチパターン | references/patterns.md |
| 購入、アンチエクスプロイト、リモート検証の深さ | references/security-monetization.md |
| UI/UX、クロスプラットフォーム、テスト、デバッグ、テレメトリー | references/ui-ux-testing.md |
| ジャンルが既知の場合(シミュレーター、FPS、obby、RPG、レーシング、ホラー、ソーシャル) | references/genres.md |
ユーザー権限
このスキルはガイダンスであり、強制ではありません。常にユーザーが完全な制御権を持ちます。
- ユーザーの明示的な指示は、このスキルのすべての規則に優先します。指示が非交渉のランタイムルールと矛盾する場合は、リスクを一度だけ簡単に述べ、その後ユーザーの決定に従います。
- このスキルのみに基づいて、ユーザーが依頼していないアクション(要求されていないリファクタリング、再構築、ファイル作成、「ついでに」のクリーンアップなど)を決して実行しないでください。推奨はしても、行動はしないでください。
アドバイザリー起動(特定のタスクなし)
ユーザーは、具体的なコーディングタスクなしで、単に念のためのリマインダーとしてこのスキルを呼び出す場合があります(「ベストプラクティスを使って」、「このスキルを今から適用して」など)。その場合:
- コードベースの分析を開始したり、モードやライブラリのセットアップについて質問したりしないでください。 基準が有効になったことを簡単に確認し、停止します。
- これらのルールを、セッション内の後続のすべての Luau 作業におけるアクティブなガイダンスとして保持します。
- モード選択とコミュニティライブラリのチェックは、遅延して解決します。最初の実際のコーディング/レビュータスクの時点で、そのタスクに必要な部分のみを解決します。
監督レベル(確認頻度)
ユーザーは、エージェントが行動する前に確認を求める頻度を制御します。3 つのレベルがあります:
| レベル | トークン | 動作 |
|---|---|---|
| 監督下 | !ask | 重要な決定の前に毎回確認:規則の選択、作成/変更するファイルのリスト、このスキルからの逸脱、コードを書く前。ユーザーはすべてを確認し承認します。 |
| バランス型(デフォルト) | !bal | 実際に必要な場合のみ確認:真の曖昧さ、非交渉ランタイムルールとの衝突、広範囲に影響する/破壊的な変更。それ以外はそのまま進みます。 |
| 自律型 | !go | 確認しません。賢明なベストプラクティスの決定を行い、すべての前提を最終サマリーに記録します。破壊的/不可逆的なアクションの場合のみ停止します。 |
レベルの設定方法:
- セッション宣言 — ユーザーが任意の言葉で宣言します(「監督モードにして」、「あまり質問しないで」、「自由にして」など)。変更されるまでセッション全体に有効です。
- インライントークン — プロンプト内のどこかに
!ask/!bal/!goがある場合、そのプロンプトに対してのみセッションレベルをオーバーライドします。
優先順位: インライントークン > セッション宣言 > バランス型デフォルト。ユーザーにどのレベルを希望するか尋ねないでください。宣言がないことは、バランス型が選択されたことを意味します。明示的なユーザー指示(ユーザー権限)はレベル自体よりも優先されます。
このスキルの確認ポイントへの影響:
| 確認ポイント | 監督下 | バランス型 | 自律型 |
|---|---|---|---|
| デフォルト/アダプティブモードの質問 | 常に質問 | コードベースが存在する場合、一度質問 | 推測し、その前提を報告 |
| アダプティブ規則の確認(ステップ 2) | 承認を待つ | 承認を待つ | レポートとして提示し、そのまま進む |
| コミュニティライブラリのチェック | 質問 | 一度質問 / 検出 | require() で検出 |
| 非交渉ルールとの衝突 | 質問 | 質問 | サマリーで警告し、安全な選択肢を選ぶ |
| レビューモード:スタイル上の再構築 | 提案し、待つ | 提案し、待つ | 提案のみ(ユーザー権限 — 変更なし) |
モード選択(最初のコーディングタスクの前)
このスキルは、2 つのモードのいずれかで動作します。コードを書き始める前にモードを決定します。
- デフォルトモード — 以下に記述されているこのスキルの規則をそのまま適用します(セクションレイアウト、命名、順序)。以下の場合に使用します:ユーザーがそれを要求した場合、プロジェクトが新規/空の場合、または既存のコードに保存する価値のある一貫した規則がない場合。
- アダプティブモード — まずプロジェクトの既存のコーディング構造と規則を調査し、調査結果と適応した規則の提案を提示し、ユーザーの確認を得てから、確認された規則に従ってコードを記述します。普遍的なルール(非交渉ランタイムルール、言語とスタイルの安全性項目、パフォーマンス/パターンリファレンスのすべて)は完全に適用されます。適応されるのはスタイル/構造の規則のみです。
決定方法:
- ユーザーが明示的にモードを指定した場合(例:「デフォルトを使って」、「このプロジェクトの構造に従って」、「まず私たちのコードを調べて」)→ それに従います。
- そうでない場合、表示可能な規則を持つ既存のコードベースがある場合 → ユーザーに一度質問します:「このスキルのデフォルト規則を使用しますか、それともまずプロジェクトの既存の構造を調査し、(あなたの確認を得て)適応する必要がありますか?」
- 質問が不可能な場合(自律実行) → 新しいファイルの場合はデフォルトモード。既存のファイルの編集の場合は、ファイルの既存のスタイルに合わせ、その前提をサマリーに記録します。
アダプティブモードの手順(分析チェックリスト、確認形式、優先順位ルール):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 ツール経由で作業。パスはインスタンスパス) vs Rojo/ファイルシステム(ファイル経由で作業。
requireはパスエイリアスを使用する場合があり、src/レイアウトはサービスにマッピングされます)。スクリプトの読み取り、書き込み、参照の方法をこれに合わせます。 - 新しい API を使用する前に検証します(
BindToSimulation、UIShadow、入力アクションシステム、構造化されたLogServiceなど)。ターゲット環境に存在することを確認し、存在しない場合は安定した同等のものにフォールバックします。公式ドキュメント(create.roblox.com — Engine API Reference)が主要な権威です。 API ダンプ/ReflectionService または Studio での簡単なテストで、ドキュメントが追いついていない部分を確認します。Roblox は継続的に新しい API をリリースします。トレーニング知識に含まれていないことは、API が存在しないという証拠にはなりません。 - スクリプトの規模に応じて儀式を調整します。 小さなスクリプト(約 40 行未満)は、3 つのトップレベルヘッダーのみでサブセクションは不要です。セクションに十分なコンテンツがある場合にのみ、レベル 2 以上のヘッダーを追加します。空のプレースホルダーヘッダーを決して出力しないでください。純粋なデータ/型モジュール(設定テーブル、アイテムカタログ、共有型定義 — ランタイムロジックなし)は、3 セクションレイアウトから免除されます。最も読みやすい方法でコンテンツをグループ化します。
スクリプトセクションレイアウト(必須)
すべてのスクリプトは、以下の順序で正確に 3 つのトップレベルセクションに分割されます:
-- // 変数 // --
-- // 関数 // --
-- // 初期化 // --
セクションヘッダー階層
5 つのネストレベル。セクションを本当に分割する必要がある場合にのみ、より深いレベルを使用します:
-- // レベル 1 // -- トップレベルセクション(変数 / 関数 / 初期化のみ)
-- | レベル 2 | -- 標準サブセクション(サービス、モジュール、プライベート、パブリックなど)
-- [ レベル 3 ] -- サブセクション内のグループ化
-- { レベル 4 } -- まれな、詳細なグループ化
-- / レベル 5 / -- 最もまれな、最後の手段
1. -- // 変数 // --
この固定順序のサブセクション(空のものは省略):
| サブセクション | 内容 |
|---|---|
-- | サービス | -- | game:GetService() による Roblox サービス。1 行に 1 つ、実際に使用されるもののみ |
-- | モジュール | -- | require() 呼び出し。ソースの場所順:ServerScriptService → ServerStorage → ReplicatedStorage → Workspace、最後にスクリプト相対の require(script.Parent.X)。Packages/ServerPackages フォルダーは、それを含むサービスとしてソートされます。スクリプトが法的に到達できる場所のみが適用されます(クライアントスクリプトはサーバーの場所をスキップします) |
-- | オブジェクト | -- | インスタンスへの参照(モデル、フォルダー、リモート、UI)。オプション — 必要な場合のみ |
-- | 設定 | -- | スクリプト全体で使用される定数と調整可能な値。UPPER_SNAKE_CASE |
-- | 状態管理 | -- | 可変のランタイム状態変数(カウンター、キャッシュ、フラグ、接続テーブル) |
2. -- // 関数 // --
- ModuleScript は関数を
-- | プライベート | --(このスクリプト内でのみ使用、local function)と-- | パブリック | --(返されるテーブルで公開)に分割します。プライベートが最初に来ます。 - Script/LocalScript は通常、プライベート/パブリックの分割をスキップします。関数をセクションヘッダーの下にリストするだけです(スクリプトが大きい場合は、レベル 2 ヘッダーを使用してトピックごとにグループ化します)。
- すべての関数に doc コメントを付けます。常に
--[[ ... ]]ブロックで関数の直上に配置します(説明が 1 行の場合でも)。この固定順序で構成します:説明 → パラメータ → 戻り値。- 説明 — 技術的で簡潔、明確な英語。関数の一般的な目的/契約を説明する必要があり、現在の実装ではありません。本文内の特定の機能、API、アルゴリズム、コードパスについて言及/議論しないでください。このように記述することで、説明は関数の内容が変更されても有効で関連性を保ちます。
@param/@return— シグネチャがすでに示している以上の情報を追加する場合(自明でない意味、単位、制約、nil の動作)にのみ含めます。自明な場合は完全に省略します。
--[[
キャラクターにダメージを適用し、結果として生じる死亡状態を処理します。
@param amount ヒットポイント単位のダメージ量。正の値である必要があります
@return ダメージが致命的であった場合は true
]]
local function applyDamage(humanoid: Humanoid, amount: number): boolean
...
end
アンチ例(実装に特異的すぎる — 本文が変更されるとすぐに壊れます):Humanoid.Health から amount を減算し、ヘルスがゼロになった場合にラグドールモジュールをトリガーします。
- 依存関係が最初に来るように関数を順序付けます(呼び出し元の上に呼び出し先)。これはローカル関数の Luau の要件です。
3. -- // 初期化 // --
実行されるすべてのもの:関数呼び出し、イベント接続、ループ。ここに関数定義はありません。スクリプトが複数の関心事を配線する場合、レベル 2 サブセクションを使用してコンテキストごとにグループ化します:
-- // 初期化 // --
-- | プレイヤーイベント | --
Players.PlayerAdded:Connect(onPlayerAdded)
Players.PlayerRemoving:Connect(onPlayerRemoving)
-- | リモート | --
purchaseRemote.OnServerEvent:Connect(onPurchaseRequest)
-- | 起動 | --
loadWorldState()
完全な注釈付きテンプレート(Script、LocalScript、ModuleScript):references/templates.md を参照。
言語とスタイルのルール
- すべてのスクリプトを
--!strictで開始します(--!nonstrictは、strict が非現実的な場合のみ)。パブリック関数のシグネチャ、設定定数、状態テーブルに型注釈を付けます。 - 命名:
PascalCase(サービスと require されたモジュールテーブル)、camelCase(ローカル変数、関数、インスタンス参照(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)2 引数形式 → 最初にプロパティを設定し、親を最後に設定します。:connect()/:wait()小文字 →:Connect()/:Wait()。BodyVelocity/BodyGyro→ 制約(LinearVelocity、AlignOrientation)。 - 外部/ yield する呼び出し(
DataStore、MarketplaceService、HttpService、TeleportService)をpcallと再試行ポリシーで保護します。保護されていない yield がプレイヤーフローをクラッシュさせないようにします。 - ModuleScript ごとに 1 つの責任。