mcpField
R
roblox-architecture

roblox-architecture

github.com/el4cteo開発61599 回インストール2 週間前に更新
リポジトリ

roblox-architecture について

roblox-architectureは、Robloxゲームのサーバー側・クライアント側のコードと状態を整理する開発スキルです。プロジェクト構成、ライフサイクル処理、タグ付きコンポーネント、状態共有、ストリーミングを説明します。

主な機能

  1. 01サーバー側とクライアント側のコード配置
  2. 02Rojo形式のプロジェクト構成
  3. 03サービスとコントローラーの起動
  4. 04プレイヤーとキャラクターのライフサイクル処理
  5. 05タグ付きコンポーネントと属性の利用
  6. 06状態共有とストリーミングの指針

活用シーン

  1. 01Robloxプロジェクトの初期構成の計画
  2. 02サーバー側とクライアント側のシステム整理
  3. 03ストリーミングされるワールド要素とプレイヤー状態の処理

インストール

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

$npx skills add https://github.com/el4cteo/roblox-skills --skill roblox-architecture

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

同梱ファイルreferences/ecosystem.mdreferences/streaming.md

詳細

Roblox ゲームアーキテクチャ

一段落でわかるモデル

すべての Roblox ゲームは、サーバー(権威を持ち、Script を実行し、すべてを把握し、DataStore を所有する) と、プレイヤーごとに 1 つのクライアント(LocalScript / クライアント Script を実行し、レンダリングし、入力を読み取る) で構成されます。 サーバーの DataModel はクライアントへ自動的にレプリケートされます。クライアント側の変更は レプリケートされません。ただし、クライアントがネットワーク所有権を持つパーツの物理(既定では自身のキャラクター)、 自身の Humanoid の状態・アニメーション、およびリモート呼び出しは例外です。すべてのクライアントが 侵害されていると想定してください(roblox-security を参照)。すべての機能は次のように設計します: クライアントが要求する → サーバーが検証して判断する → 状態がレプリケートされる → クライアントがレンダリングする。

何をどこに置くか

コンテナ実行・表示される場所置くもの
ServerScriptServiceサーバーのみサーバー Script とサーバー専用の ModuleScript(ゲームロジック、データ、アンチチート)。
ServerStorageサーバーのみサーバー専用アセット: マップ、NPC テンプレート、クローンするツール、機密性の高い設定。
ReplicatedStorage両方共有モジュール(型、設定、純粋なユーティリティ)、RemoteEvent、クライアントがクローンするアセット。
ReplicatedFirstクライアント、最初ローディング画面のみ。他のすべてより先にレプリケートされます。
StarterPlayer.StarterPlayerScriptsクライアント(一度 PlayerScripts にコピーされる)クライアントのエントリポイントとコントローラー。
StarterPlayer.StarterCharacterScriptsクライアント(スポーンごとにキャラクターへコピーされる)キャラクター単位のクライアントスクリプト(必要なことはまれ)。
StarterGuiクライアント(PlayerGui にコピーされる)ScreenGui。死亡時にリセットしたくない場合は ResetOnSpawn = false を設定します。
Workspace両方(クライアントへストリーミングされる)3D ワールドのみ。コードやテンプレートを置く場所ではありません。
Lighting、SoundService、Teams、TextChatService両方それぞれの設定オブジェクト。

ルール:

  • ReplicatedStorage 内のコードはエクスプロイターがダウンロードできます。サーバーロジック、機密情報、 管理者リスト、アンチチートのしきい値を決して置かないでください。
  • Script.RunContext: Legacy(既定: 動作はコンテナに依存)、Server、Client、または Plugin。非 Legacy の RunContext では、Script はコンテナに関係なく実行されます(例: ReplicatedStorage 内の Client Script はすべてのクライアントで実行される)— そのため、クローンするテンプレートに そのようなスクリプトを意図せず含めてはいけません。Legacy の Script は ReplicatedStorage / ServerStorage では実行されません。

プロジェクト構成(Rojo スタイル、推奨)

src/
  server/            -> ServerScriptService.Server   (init.server.luau = entry point)
    Services/        -> one ModuleScript per system (DataService, CombatService, ...)
  client/            -> StarterPlayer.StarterPlayerScripts.Client (init.client.luau)
    Controllers/     -> one ModuleScript per client system (InputController, UIController, ...)
  shared/            -> ReplicatedStorage.Shared (types, config, pure logic, network definitions)
Packages/            -> ReplicatedStorage.Packages (Wally/pesde dependencies)
ServerPackages/      -> ServerScriptService.ServerPackages

各サイドに 1 つのエントリスクリプトを置き、それ以外はすべて ModuleScript にします。これにより、 決定的な読み込み順序、テスト可能なモジュール、依存関係を接続する単一の場所が得られます。Roblox 自身の 推奨もこれと同等です: ServerScriptService に 1 つの Script(RunContext = Server)と、 ReplicatedStorage に 1 つの Script(RunContext = Client)を置き、それぞれがモジュールを require して その start() を呼び出します。ツールのセットアップ: roblox-tooling。

サービスのブートストラップ(フレームワーク不要)

--!strict
-- ServerScriptService/Server/init.server.luau
type Service = {
	init: ((self: Service) -> ())?, -- sync setup; may reference other services; must not yield
	start: ((self: Service) -> ())?, -- runs after every init; may yield / connect events
}

local servicesFolder = script:WaitForChild("Services")
local services: { [string]: Service } = {}

for _, module in servicesFolder:GetChildren() do
	if module:IsA("ModuleScript") then
		services[module.Name] = (require :: any)(module)
	end
end

for name, service in services do
	if service.init then
		debug.setmemorycategory(name)
		service:init()
		debug.resetmemorycategory()
	end
end

for name, service in services do
	if service.start then
		task.spawn(function()
			debug.setmemorycategory(name)
			service:start()
		end)
	end
end
  • サービス同士は単純な require で参照する(循環なし)か、init で依存関係を受け取ります。
  • クライアントはこれを Controller で鏡写しにします。名前は対称に保ちます(ShopService / ShopController)。
  • このために Knit のようなフレームワークは不要です。プロジェクトがすでに使用している場合は、その規約に従ってください。

プレイヤーとキャラクターのライフサイクル

--!strict
local Players = game:GetService("Players")

local function onCharacterAdded(player: Player, character: Model)
	local humanoid = character:WaitForChild("Humanoid") :: Humanoid
	humanoid.Died:Once(function()
		print(`{player.Name} died`)
	end)
end

local function onPlayerAdded(player: Player)
	-- Load data first (see roblox-data-stores), then set up character handling.
	if player.Character then
		task.spawn(onCharacterAdded, player, player.Character)
	end
	player.CharacterAdded:Connect(function(character)
		onCharacterAdded(player, character)
	end)
end

local function onPlayerRemoving(player: Player)
	-- Save data, release session locks, clear every table keyed by this player.
	print(`{player.Name} left`)
end

Players.PlayerAdded:Connect(onPlayerAdded)
Players.PlayerRemoving:Connect(onPlayerRemoving)
for _, player in Players:GetPlayers() do -- players who joined before this script connected
	task.spawn(onPlayerAdded, player)
end

game:BindToClose(function()
	-- Server shutdown: flush saves for remaining players (max ~30 s total).
end)
  • コードの開始時にすでに存在するプレイヤー・キャラクターを必ず処理してください。
  • キャラクターはリスポーンします: キャラクターに紐づくものは CharacterAdded で再作成する必要があります。
  • クライアントでは、キャラクターの子孫がまだ存在しない場合があります(ストリーミング): WaitForChild を使ってください。

CollectionService タグを使用したコンポーネント

インスタンスにタグを付け(Studio の Tags セクションまたは CollectionService:AddTag を使用)、コードで動作を追加します。 各モデルにスクリプトをコピーするよりも拡張しやすく、インスタンスがストリーミングで読み込まれたり、読み込み解除されたりすると追加・削除シグナルが発火するため、ストリーミングにも対応できます。

--!strict
local CollectionService = game:GetService("CollectionService")

local TAG = "KillBrick"
local cleanups: { [Instance]: () -> () } = {}

local function onAdded(instance: Instance)
	if not instance:IsA("BasePart") then
		return
	end
	local connection = instance.Touched:Connect(function(hit)
		local humanoid = hit.Parent and hit.Parent:FindFirstChildOfClass("Humanoid")
		if humanoid then
			humanoid.Health = 0
		end
	end)
	cleanups[instance] = function()
		connection:Disconnect()
	end
end

local function onRemoved(instance: Instance)
	local cleanup = cleanups[instance]
	if cleanup then
		cleanup()
		cleanups[instance] = nil
	end
end

CollectionService:GetInstanceAddedSignal(TAG):Connect(onAdded)
CollectionService:GetInstanceRemovedSignal(TAG):Connect(onRemoved)
for _, instance in CollectionService:GetTagged(TAG) do
	task.spawn(onAdded, instance)
end

IntValue/StringValue の子ではなく、属性(instance:GetAttribute("Damage")、 GetAttributeChangedSignal)でコンポーネントを設定します。属性はサーバーからクライアントへレプリケートされ、コストが低く、Properties パネルに表示されます。

状態の共有

必要なもの使用するもの
クライアントから参照できる、インスタンスまたはプレイヤーごとの小さな値インスタンスまたは Player の属性(サーバーが設定)。
イベントとリクエストRemoteEvent/UnreliableRemoteEvent(roblox-networking を参照)。
大きな状態、またはプレイヤーごとの非公開状態(インベントリ、クエスト)サーバー所有のテーブルと、そのプレイヤーだけにスナップショット/差分を送信するリモート。
同じ側での通信モジュール関数の呼び出し、または型付き Signal モジュール。BindableEvent、_G、shared は使用しません。
永続データroblox-data-stores。サーバー間の通信には、MessagingService/MemoryStore を使用します。

ストリーミング(新しいプレイスではデフォルトで有効)

Workspace.StreamingEnabled が有効な場合、クライアントには近くのパーツだけが表示されます。クライアントコードでは、次の点に注意してください。

  • Workspace 内のものには WaitForChild(到着しない可能性がある場合はタイムアウト付き)を使うか、nil チェックを行います。
  • パーツをまとめてスクリプトで処理する必要があるモデルには ModelStreamingMode = Atomic を使用します。 モデルを常に存在させる必要がある場合に限り、メモリを消費する Persistent を使用します。
  • ストリーミングで読み込み解除されたパーツに対する GetChildren、レイキャスト、プロパティの読み取りは、不完全または古い情報として扱います。ワールド全体のクエリはサーバーで実行します。
  • キャラクターをテレポートする前に、サーバーで player:RequestStreamAroundAsync(position) を使って目的地を先行ストリーミングします。
  • スコープ、レース、フリーカメラでは、player.FrustumStreaming = Enum.FrustumStreamingMode.Automatic を設定すると、半径を広げる代わりにカメラに映るものがストリーミングされます。

パターンの一覧と Workspace の推奨設定については、references/streaming.md を参照してください。

アンチパターン

  • パーツやモデル内に、多数の単独の Script を配置する(タグと単一のサービスを使用します)。
  • イベントや deltaTime を使った Heartbeat の代わりに、ポーリングループ(while task.wait(0.1) do check() end)を使用する。
  • ゲームの状態をクライアントに任せる、または毎フレームリモートを発火する。
  • _G/shared のグローバル、循環 require、モジュールのトップレベルでの yield。
  • クライアントからマップを Workspace に複製する(クライアントだけのコピーにはサーバーからの更新が反映されません)。
  • プレイヤーごとのデータをキャラクターに保存する(キャラクターは死亡時に破棄されます)。

シグナル、クリーンアップ、ネットワーク、UI、ECS、データに関するライブラリエコシステムについては、references/ecosystem.md を参照してください。

関連スキル

roblox-networking、roblox-security、roblox-data-stores、roblox-tooling、roblox-performance、roblox-game-systems。

コメント