mcpField
R
roblox-luau

roblox-luau

github.com/gamedev-skills開発5.4k6.8k 回インストール1 日前に更新
リポジトリ

roblox-luau について

Roblox のゲームスクリプトを Luau で書くためのリファレンススキルです。エンジン API、実行コンテキスト、クライアントとサーバー間の通信を扱い、Studio プロジェクトの実践パターンとチェック方法を示します。

主な機能

  1. 01Roblox サービスと Instance
  2. 02Script、LocalScript、ModuleScript の役割
  3. 03イベント処理と接続のクリーンアップ
  4. 04クライアント・サーバー間の Remotes と検証
  5. 05Studio テストと静的解析

活用シーン

  1. 01Roblox オブジェクトの作成と設定
  2. 02サーバーまたはクライアントの配置先選択
  3. 03イベント駆動のゲームロジック実装
  4. 04サーバーでのクライアント要求の検証

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/gamedev-skills/awesome-gamedev-agent-skills --skill roblox-luau

インストール後、Claude Code で/roblox-luauを入力して読み込まれたか確認します。

同梱ファイルreferences/client-server.md

詳細

Roblox Luau スクリプティング

Luau で Roblox のエクスペリエンスをスクリプト化します。サービス、Instance、イベント、サーバーとクライアントの分割、境界を越えた安全な通信を扱います。現在の Roblox エンジンと Studio を対象としています。

使用する場面

  • Roblox スクリプトを作成するときに使用します。サービスの取得、インスタンスの作成と親への設定、イベントの接続、サーバーとクライアントのどちらで実行するかの判断、RemoteEvent/RemoteFunction 通信の配線などが該当します。
  • プロジェクトに Script/LocalScript/ModuleScript オブジェクト、.rbxl(x) のプレース、または Rojo の *.project.json があり、コードで game:GetService(...) を呼び出している場合に使用します。

使用しない場面: セッションをまたぐデータの永続化 → roblox-datastores。リモートプロトコルのアーキテクチャ、不正行為への対策、レート制限、高頻度のレプリケーション、複数クライアントによる不正利用のテスト → roblox-networking。Roblox API に関係しない一般的な Lua の質問。エンジンに依存しない入力/セーブのアーキテクチャ → input-systems/save-systems。

基本的なワークフロー

  1. game:GetService("Name") でサービスを取得します。 よく使うもの:Players、Workspace、ReplicatedStorage(クライアントとサーバーで共有)、ServerScriptService(サーバー専用コード)、ServerStorage、RunService、UserInputService(クライアント)。
  2. コードがどこで実行されるかを把握します。 Script はサーバーで実行され、LocalScript はクライアント(StarterPlayerScripts、StarterGui、またはプレイヤーのキャラクター内)で実行されます。ModuleScript は require して使う共有コードです。
  3. 意図を持ってインスタンスを作成します。 local p = Instance.new("Part") を使い、プロパティを設定してから、p.Parent を最後に設定します(親に設定するとレプリケーションが発生します)。
  4. イベントに応じて処理します。 Players.PlayerAdded、part.Touched、RunService.Heartbeat などのシグナルに :Connect で接続します。リークを防ぐため、不要になったら接続を解除します。
  5. Remotes でクライアントとサーバーの境界を越えます。ただし、クライアントを決して信用しないでください。 クライアントは RemoteEvent:FireServer(...) でリクエストし、サーバーが検証して適用します。すべてのゲーム状態の正当な管理者はサーバーです。
  6. Studio でテストします。 Play/Play Here/サーバー+クライアントの Start を使い、Output ウィンドウとサーバー/クライアントの表示切り替えでコードの実行場所を確認します。
  7. プロジェクトにツールがすでにある場合は、Studio で確認する前に静的チェックを実行します。 ルートの selene.toml に std = "roblox" を追加してから、selene . を実行します。 Rojo プロジェクトでは、次のコマンドでソースマップを一度書き出してから、 rojo sourcemap default.project.json --output sourcemap.json を実行し、 luau-lsp analyze --sourcemap=sourcemap.json src を実行します。Roblox の型定義を --definitions で渡してください(フラグの一覧は luau-lsp analyze --help で確認できます)。Studio Script Sync プロジェクトにはソースマップがない場合があります。

パターン

1. サーバースクリプト:プレイヤーの参加に応じて処理する(leaderstats)
-- ServerScriptService/Leaderboard.server.luau  (a Script = runs on the server)
local Players = game:GetService("Players")

local function onPlayerAdded(player: Player)
    local stats = Instance.new("Folder")
    stats.Name = "leaderstats"          -- this name makes it show on the leaderboard

    local coins = Instance.new("IntValue")
    coins.Name = "Coins"
    coins.Value = 0
    coins.Parent = stats

    stats.Parent = player               -- parent LAST
end

Players.PlayerAdded:Connect(onPlayerAdded)
2. インスタンスを作成して設定する
local Workspace = game:GetService("Workspace")

local part = Instance.new("Part")
part.Size = Vector3.new(4, 1, 4)
part.Position = Vector3.new(0, 10, 0)
part.Anchored = true                    -- won't fall under gravity
part.BrickColor = BrickColor.new("Bright blue")
part.Parent = Workspace                 -- set Parent last so it replicates once, fully
3. イベントを接続する(リークを防ぐために接続を解除する)
local debounce = false
local connection
connection = part.Touched:Connect(function(hit: BasePart)
    local character = hit.Parent
    local humanoid = character and character:FindFirstChildOfClass("Humanoid")
    if not humanoid or debounce then return end
    debounce = true
    humanoid.Health -= 10
    task.wait(1)                        -- task.wait, NOT the deprecated wait()
    debounce = false
end)

-- Later, when the part is removed or the round ends:
-- connection:Disconnect()
4. RemoteEvent でクライアントからサーバーへ通信する(サーバーで検証する!)
-- ReplicatedStorage: create a RemoteEvent named "BuyItem" (in Studio or via code).
-- CLIENT (LocalScript): request a purchase. The client can lie — this is only a request.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local buyItem = ReplicatedStorage:WaitForChild("BuyItem")  -- wait: may not have replicated yet
buyButton.MouseButton1Click:Connect(function()
    buyItem:FireServer("sword")        -- send the item id only; never the price/result
end)
-- SERVER (Script): the ONLY place the transaction is decided.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local buyItem = ReplicatedStorage:WaitForChild("BuyItem")
local PRICES = { sword = 100, shield = 75 }

buyItem.OnServerEvent:Connect(function(player: Player, itemId)
    -- TRUST NOTHING from the client. Validate types and values.
    if type(itemId) ~= "string" then return end
    local price = PRICES[itemId]
    if not price then return end                         -- unknown item
    local coins = player.leaderstats.Coins
    if coins.Value < price then return end               -- can't afford
    coins.Value -= price                                 -- server applies the change
    grantItem(player, itemId)
end)
5. RunService を使ったフレームごとのループ
local RunService = game:GetService("RunService")
-- Heartbeat fires every frame AFTER physics; dt is seconds since the last step.
RunService.Heartbeat:Connect(function(dt)
    spinner.CFrame *= CFrame.Angles(0, math.rad(90) * dt, 0)  -- 90deg/sec, frame-independent
end)
6. ModuleScript で共有コードを使う
-- ReplicatedStorage/GameConfig (a ModuleScript) — usable by server and client.
local GameConfig = {}
GameConfig.MaxHealth = 100
function GameConfig.damageFor(weapon: string): number
    return ({ sword = 25, bow = 15 })[weapon] or 0
end
return GameConfig
local GameConfig = require(game:GetService("ReplicatedStorage"):WaitForChild("GameConfig"))
print(GameConfig.MaxHealth)

落とし穴

  • クライアントを信頼することは悪用につながります → クライアントはRemoteEvent/RemoteFunctionに任意の引数を送信できます。サーバーで引数の型と範囲をすべて検証し、体力、通貨、インベントリはサーバー側で権威を持って管理してください。
  • LocalScriptは配置した場所では実行されません → LocalScriptが実行されるのはStarterPlayerScripts、StarterCharacterScripts、StarterGui、またはツール内です。WorkspaceやServerScriptServiceでは実行されません。サーバーのScriptはServerScriptService/Workspaceに配置します。
  • 非推奨のグローバル関数 → 古いwait()/spawn()/delay()(スケジューリングが劣り、スロットリングも発生します)ではなく、task.wait/task.spawn/task.delayを使ってください。
  • プロパティを設定する前に親を設定する → インスタンスが最終状態で一度だけレプリケーションされるよう、プロパティを先に設定し、最後にParentを設定してください。
  • 参加直後にクライアント側でnilになる → オブジェクトは時間をかけてストリーミング/レプリケーションされます。クライアントで直接インデックスを参照するのではなく、parent:WaitForChild("Name")を使ってください。
  • 接続を切断しないままにする → 長期間存続する:Connectハンドラーはリークを引き起こし、破棄されたオブジェクトに対しても実行される可能性があります。接続を保存して:Disconnect()するか、適切な場合はInstance:GetAttributeChangedSignal/:Onceを使ってください。
  • RemoteEventで足りる場面でRemoteFunctionを使う → RemoteFunctionは戻り値を待つ間ブロックされるため、悪意のあるクライアントや応答の遅いクライアントによってサーバーが停滞する可能性があります。本当に応答が必要な場合を除き、一方向のRemoteEventを優先してください。
  • 古い型チェッカーを前提にする -> Luauの新しい型ソルバーは、2025年11月に一般公開(Studio Betaを終了)されました。nocheck/非厳格モードの場所では自動的に有効になり、厳格モードの場所ではワークスペースプロパティUseNewLuauTypeSolverでオプトインします。また、LuauTypeCheckModeで場所のデフォルトのチェックモードを設定します。編集時にのみ実行され(実行時のコストはありません)、型の警告を振り分ける際は、古い動作ではなく、その場所で実際に有効になっているソルバーを基準にしてください。

参考資料

  • クライアント/サーバーモデル全体(レプリケーション、RemoteFunctionとRemoteEventの違い、:WaitForChildのタイミング、同じコンテキスト内でのメッセージングに使うBindableEvent、属性、CollectionServiceタグ、:Once/接続のクリーンアップ)については、references/client-server.mdを参照してください。

関連スキル

  • roblox-datastores — プレイヤーデータをセッション間で永続化します(サーバー専用)。
  • roblox-networking — 本番環境向けのリモートコントラクト、サーバー検証、レート制限、レプリケーション、ストリーミング、予測、マルチクライアントテスト。
  • save-systems — エンジンに依存しない永続化の概念。
  • game-ai/input-systems — Luauで実装できる、移植性の高いAIと入力のパターン。

コメント