mcpField
R
react-three-game

react-three-game

github.com/prnthh開発1143 回インストール3 週間前に更新
リポジトリ

react-three-game について

react-three-gameは、prefabと呼ばれるJSONシーンドキュメントを中心としたWebゲーム制作ツールキットです。開発者やエージェントがシーンを編集し、再利用可能なコンポーネントを追加したり、アプリ独自のキャンバスにシーンを描画したりできます。

主な機能

  1. 01実行中のエディターAPIでシーン編集
  2. 02prefab JSONを直接編集
  3. 03再利用可能なカスタムコンポーネントを追加
  4. 04Runtimeコンポーネントでシーンスクリプトを実行
  5. 05PrefabRootでシーンを描画
  6. 06prefabチャンクを読み込んで有効化

活用シーン

  1. 01実行中のシーンでオブジェクト配置を調整
  2. 02シーンJSONの変更を生成またはレビュー
  3. 03再利用可能な動作やビジュアルコンポーネントを追加
  4. 04既存のキャンバスにprefabシーンを組み込む

インストール

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

$npx skills add https://github.com/prnthh/react-three-game-skill --skill react-three-game

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

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

agents/
openai.yaml
rules/
ADVANCED_PHYSICS.md
LIGHTING.md
PERFORMANCE.md
RUNTIME_INTEGRATIONS.md

詳細

React Three Game

React Three Game は、Web 向けのゲーム制作ツールボックスです。prefab は JSON のシーンドキュメントです。ノードがオブジェクトを記述し、コンポーネントが設定や動作を与えます。エージェントは、実行中のエディターを操作したり、ドキュメントを直接編集したり、アプリケーションのソースにコンポーネントを追加したりできます。タスクと利用可能なファイルに合った方法を選んでください。

作業方法を選ぶ

タスクまたは状況方法
表示中のシーンを調整して配置を確認するエディター API を使う
保存済みのシーンを変更する、コンテンツを生成する、またはファイルの差分を確認するprefab JSON を直接編集する
再利用可能な動作、フック、新しいレンダリング機能を追加するソースにカスタムコンポーネントを追加する
シーンと一緒に小さなスクリプトを保存するRuntime コンポーネントを使う
アプリ独自のキャンバスコンテンツと並べて JSON をレンダリングするPrefabRoot を埋め込む
ゲームプレイ、物理演算、ライティング、読み込みを接続する状態とリソースおよび目的別リファレンス

これらの方法は組み合わせることができます。ソースにコンポーネントを追加し、JSON でインスタンスを設定してから、エディターまたはビューアーで確認します。独自の規約を作る前に、既存のスキーマと近接するアプリケーションコードを参照してください。

シーンを編集する

接続してオブジェクトを見つける

PrefabEditor を含むフレーム内で JavaScript を実行します。

const scene = window.scene;
scene.info(); // Check mode and how this scene can be saved.
const { node } = scene.find({ query: 'north wall' }); // Requires one match.
await scene.setMode({ mode: 'edit' });
scene.look({ id: node.id });

編集に必要な情報だけを読み取ります。

scene.search({ query: 'wall', limit: 20 }); // Choose an ID when find is ambiguous.
scene.get({ id: node.id, resolved: true }); // Full properties and defaults.
scene.components({ names: ['Material'] }); // Supported settings for a type.

コンポーネントインスタンスのキーはノードに属しており、型名と異なる場合があります。

window.scene が見つからない場合は、フレームを確認し、エディターがマウントされるまで待ちます。agentTools={false} は公開を無効にします。showUI={false} はパネルを非表示にするだけです。ビューアーのみの場合、window API はありません。1 ページでサポートされるエージェント対応エディターは 1 つだけです。ナビゲーション後は再取得してください。

変更を 1 つ加えて確認する

書き込みには Edit モードが必要です。適切な既存オブジェクトから始め、シーンの階層と視覚的な規約を維持してください。

await scene.setMode({ mode: 'edit' });
scene.update({ id: node.id, transform: { position: [2, 1, 0] } });
scene.look({ id: node.id });
const image = await scene.capture({ helpers: false });

アセットを読み込んでから、image.dataUrl を表示して結果を確認します。更新 1 回につき、取り消し操作は 1 ステップです。

scene.undo(); // Reverse an authored edit when needed.
await scene.setMode({ mode: 'play' }); // Check animation or physics.
// Observe the behavior before continuing.
await scene.setMode({ mode: 'edit' });
await scene.reset(); // Restore live objects from current JSON; keep edits/history.

モードを変更するだけでは、ライブ状態はリセットされません。

結果を保存する
if (scene.info().saveMethod === 'save') {
  await scene.save(); // Host's onSaveScene callback.
} else {
  const json = scene.exportJSON(); // Write this string to the scene's source file.
}

エクスポートだけでは保存されません。変更した内容、確認した内容、保存先を報告してください。

より具体的な操作を見つける

scene.help() はメソッドを一覧表示します。キャプチャオプション、コンポーネントの編集、配置、ダウンロード、パッキング、アトミックな一括処理については、エージェント向けエディターシーンの該当セクションを参照してください。このチェックアウトでは、docs/public/editor-scene-for-agents.md を使用してください。デプロイ済みのドキュメントは古い場合があります。

個々の編集では、現在のリビジョンが自動的に使用されます。以前の読み取り結果を基準に調整する場合は expectedRevision を使用してください。validate() と batch() では指定が必須です。ガード付きの書き込みが競合した場合は、再読み込みして計画を立て直してください。依存する変更をまとめて反映する必要がある場合は、一括処理を優先してください。すべての編集に使う必要はありません。

prefab JSON を直接編集する

アプリが実際に読み込むファイルを見つけます。このリポジトリでは、シーンは docs/public/prefabs にあります。

// In the consuming app: follow this import (or the PrefabInstance URL).
import level from './level.json';

// Edit level.json directly; no running editor is needed.
// Preserve node IDs, component keys, and unrelated fields.
// Check component schemas in source before adding properties.
// Check JSON syntax, unique IDs, and component registration in the app.
// Reload the editor/viewer, inspect the result, and review the file diff.

// File edits do not update an already-open editor's in-memory document.
// Reload before continuing API work so a stale save cannot overwrite the file.

アプリのキャンバスに JSON をレンダリングする

ユーザー独自の JSX やゲームプレイシステムと並べて JSON をレンダリングします。マウントする前にカスタム登録をインポートし、レンダー間で data の値が変わらないようにしてください。

GameCanvas を使う場合
import './components'; // Application component registrations.
import { GameCanvas, PrefabRoot } from 'react-three-game/viewer';
import type { Prefab } from 'react-three-game/core';
import level from './level.json';

<GameCanvas>
  <ambientLight intensity={1} />
  <PrefabRoot data={level as Prefab} />
  <mesh position={[3, 0, 0]}>
    <boxGeometry />
    <meshStandardMaterial color="orange" />
  </mesh>
</GameCanvas>
既存の R3F Canvas を使う場合

アプリ既存の WebGPU セットアップを維持してください。必要に応じて、最小限のレンダラーコールバックを以下に示します。

import { Canvas } from '@react-three/fiber';
import { WebGPURenderer } from 'three/webgpu';
import { PrefabRoot } from 'react-three-game/viewer';
import type { Prefab } from 'react-three-game/core';
import './components';
import level from './level.json';

<Canvas gl={async ({ canvas }) => {
  const renderer = new WebGPURenderer({ canvas });
  await renderer.init();
  return renderer;
}}>
  <PrefabRoot data={level as Prefab} />
  {/* Existing JSX, lights, controls, and gameplay systems stay here. */}
</Canvas>
リソースを共有する、または prefab にアクセスする場合

キャンバス内で、アプリの Gameplay コンポーネントを使用します。

import { SceneRuntime, PrefabRoot } from 'react-three-game/viewer';

<SceneRuntime>
  <PrefabRoot id="level" data={level}>
    <Gameplay /> {/* Can use usePrefab() for this document. */}
  </PrefabRoot>
  <PrefabRoot id="props" data={propsPrefab} />
  {/* Sibling userland content shares runtime resources, not a prefab context. */}
</SceneRuntime>

GameCanvas はすでに SceneRuntime を提供しています。スタンドアロンの PrefabRoot は、必要に応じて独自のランタイムを提供します。これらのプロバイダーはゲームループを提供しません。

エディターや window API は必要ありません。視覚的な編集を行うには、react-three-game/editor から PrefabEditor をマウントしてください。詳しくはエディターガイドを参照してください。

プレハブの規則

以下の注釈付き JSON には、説明用のコメントが含まれています。.json ファイルではコメントを省略してください。

{
  "root": {
    "id": "world", // IDs are unique within this prefab.
    "children": [{
      "id": "box", // Keep IDs stable across edits.
      "components": {
        // Instance keys (left) can differ from registered type names (right).
        "transform": {
          "type": "Transform",
          "properties": {
            "position": [0, 1, 0], // Local to the parent; Y is up.
            "rotation": [0, 0, 0], // XYZ Euler radians.
            "scale": [1, 1, 1]     // Scaling also affects children and colliders.
          }
        },
        "geometry": {
          "type": "Geometry",
          "properties": { "geometryType": "box", "args": [2, 1, 1] }
        },
        "material": { "type": "Material", "properties": { "name": "stone", "color": "#999999" } }
      }
    }]
  }
}

components 内の共通アセットエントリー:

// Model assets retain their embedded materials; a sibling Material won't override them.
"model": { "type": "Model", "properties": { "filename": "/models/tree.glb" } }

// For primitive meshes: texture is the color map, normalMapTexture the normal map.
"surface": { "type": "Material", "properties": { "texture": "/textures/stone.jpg" } }

// Compose another scene document. Relative asset URLs resolve against basePath.
"building": { "type": "PrefabRef", "properties": { "url": "/prefabs/building.json" } }

既知のアセット URL を使用してください。リモートアセットには CORS が必要です。ブラウザーで読み込みを確認してください。 各シーンで有効な Camera と Fog はそれぞれ 1 つにしてください。CameraFollow のターゲットにはローカルノードを指定します。 そのオフセットはワールド空間です。編集モードではエディターのカメラ操作を使用します。

スクリプトによる動作

JSON またはエディター API を使って、ノードの components の下に次のエントリーを追加します。

"spin": {
  "type": "Runtime",
  "properties": {
    "data": { "speed": 1 },
    "setup": "const y = object.rotation.y; return () => { object.rotation.y = y; };",
    "update": "object.rotation.y += data.speed * delta;"
  }
}

setup は任意のクリーンアップ関数を返し、update は秒単位の delta を受け取ります。どちらも準備完了後、Play で有効な間に実行されます。コードを変更すると setup が再実行されますが、データを変更しても再実行されません。setup で作成したコールバック内では、現在の入力を取得するために context.data を使用してください。

スクリプトは state を共有し、nodeId、node、object、data、prefab、events、context を受け取ります。React フックを使わず、信頼されたページコードとして実行されます。フィールドの詳細は Runtime スキーマを参照してください。フックが必要な場合は、ソースコンポーネントを使用してください。

カスタムコンポーネント

再利用可能な動作、React フック、新しいビジュアルコンポーネントには、アプリケーションソースを使用してください。

設定のみ

name と properties は必須です。View は任意です。設定だけでは動作は実行されません。

import { registerComponent, type Component } from 'react-three-game/viewer';

const Health: Component<{ max: number }> = {
  name: 'Health',
  properties: { max: { default: 100 } },
};
registerComponent(Health);
動作を伴う設定

React フックとライブのノードオブジェクトを使用するには、View を追加します。合成を維持するには children を返してください。

import { useFrame } from '@react-three/fiber';
import { registerComponent, useNode, useGameObject,
  type Component, type ComponentViewProps } from 'react-three-game/viewer';

type SpinProps = { speed: number };
function SpinView({ properties, enabled, children }: ComponentViewProps<SpinProps>) {
  const object = useGameObject();
  const { editMode, preparing } = useNode();
  // Animate the live object; document writes are for authored edits.
  useFrame((_, delta) => {
    if (enabled && !editMode && !preparing && object.transform) {
      object.transform.rotation.y += properties.speed * delta;
    }
  });
  return <>{children}</>;
}
const Spin: Component<SpinProps> = {
  name: 'Spin', View: SpinView,
  // Defaults feed both the view and the generated inspector.
  properties: { speed: { default: 1, step: 0.1 } },
};
registerComponent(Spin);
ノードにアタッチする

エディターまたはビューアーをマウントする前に登録モジュールをインポートし、ノードの components の下にエントリーを追加します。インスタンスキーはノード内でローカルです。type は登録名と一致させます。

"spin": { "type": "Spin", "properties": { "speed": 2 } }
プロパティとレンダリングの規約

コンポーネント定義内のフィールド:

properties: {
  speed: { default: 1 },                       // Numbers infer their type.
  label: { type: 'string', default: 'Box' },    // Other values specify a type.
  mode: {
    type: 'select', default: 'walk',
    options: [{ value: 'walk', label: 'Walk' }, { value: 'run', label: 'Run' }],
  },
},
// View receives resolved defaults; don't duplicate them in the view.
// Ordinary fields get an inspector automatically.

// Omit slot for ordinary behavior. When supplying a render-graph part:
slot: 'geometry', // Or 'object' / 'material'; each slot is exclusive on its node.
// Geometry gets an implicit mesh; object views supply their own render object.
// The View must implement attachment and preserve children where appropriate.

カスタムインスペクターを使う場合は、エディター専用モジュール内で次のようにします。

import { registerComponentEditor } from 'react-three-game/editor';

registerComponentEditor(Spin, SpinInspector); // App-defined component and inspector.
// Keep this module out of runtime imports.

状態とリソース

必要なもの使用するもの
現在のノードオブジェクトuseGameObject()
ノード/編集状態useNode()
ドキュメントとローカルオブジェクトusePrefab()
共有シーン/モードuseScene()
シリアライズ可能な編集Prefab の変更
ビューのアニメーションR3F useFrame 内の Refs/ライブ Three オブジェクト
ゲームプレイのシミュレーションホストが所有する状態とティックループ
URL に基づくチャンクPrefabInstance
チャンクの読み込みと有効化

キャンバス内で:

import { PrefabInstance } from 'react-three-game/viewer';

// Mount to load, compile, and activate automatically.
<PrefabInstance id="courtyard" url="/prefabs/courtyard.json" />
// onStatus: preparation status/errors. onActivate: gameplay is active.
// Unmount to release. Use static only when the chunk will remain immutable.
ジオメトリの変更
const Stretch: Component<{ amount: number }> = {
  name: 'Stretch',
  properties: { amount: { default: 1 } },
  modifyGeometry: (source, { amount }) => source.clone().scale(1, amount, 1),
};
registerComponent(Stretch);

新しく所有するジオメトリを返し、ソースには変更を加えないでください。モディファイアーはコンポーネントの順序で実行されます。任意の第 3 引数は、ノードのスケールを context.scale として渡します。

アセットの宣言とマテリアルの共有

アセットを使用するコンポーネント定義では、ビューが使用する各リソースを宣言します。

dependencies: ({ filename }) => [{ kind: 'model', path: filename }],

種類は model、texture、sound、prefab で、パスは basePath に従います。カスタムの共有マテリアルには useSharedMaterialResource を使用し、変更しないでください。

用途に応じたリファレンス

タスクに必要なリファレンスだけを参照してください。

コメント