mcpField
R
react-three-game

react-three-game

github.com/prnthh开发1143 次安装3 周前更新
代码仓库

关于 react-three-game

react-three-game 是面向 Web 游戏创作的工具包,以称为 prefab 的 JSON 场景文档为核心。开发者和 Agent 可用它编辑场景、添加可复用组件,或将场景内容渲染到应用自己的画布中。

主要功能

  1. 01通过运行中的编辑器 API 编辑场景
  2. 02直接修改 prefab JSON
  3. 03添加可复用自定义组件
  4. 04用 Runtime 组件运行场景脚本
  5. 05用 PrefabRoot 渲染场景
  6. 06加载并激活 prefab 分块

适用场景

  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 的游戏创作工具箱。预制体是一份 JSON 场景文档:节点描述对象,组件为对象提供设置和行为。Agent 可以通过运行中的编辑器操作、直接编辑这些文档,或在应用源代码中添加组件。请选择适合当前任务和可用文件的方式。

选择工作方式

任务或情境方式
调整可见场景并检查摆放位置使用编辑器 API
修改已保存的场景、生成内容或检查文件差异直接编辑预制体 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。每个页面只支持一个启用了 Agent 功能的编辑器;页面导航后请重新获取它。

进行一项更改并检查

写入操作需要编辑模式。从合适的现有对象着手,并保留场景层级和视觉惯例。

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 并检查结果。每次更新都是一个撤销步骤。

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() 会列出方法。请阅读面向 Agent 的编辑器场景中相关章节,了解捕获选项、组件编辑、摆放、下载、打包或原子批处理。在此检出版本中,请使用 docs/public/editor-scene-for-agents.md;已部署的文档可能未及时更新。

单项编辑会自动使用当前版本。使用 expectedRevision 可基于先前读取的版本进行协调;validate() 和 batch() 需要该参数。如果受保护的写入发生冲突,请重新读取并制定计划。只有在相互依赖的更改必须一起生效时才优先使用批处理,不要为每次编辑都使用。

直接编辑预制体 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>
共享资源或访问预制体

在画布内部使用应用的 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 会在需要时提供自己的运行时。这些 Provider 不会提供游戏循环。

无需使用编辑器或 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。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 hooks。字段详情请参阅 Runtime 架构;需要使用 hooks 时,请使用源代码组件。

自定义组件

可复用行为、React hooks 和新视觉组件请使用应用程序源代码。

仅设置

必须提供 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);
设置与行为

添加 View 以使用 React hooks 和实时节点对象。返回 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()
可序列化编辑预制体变更
视图动画在 R3F useFrame 中使用 Refs/实时 Three 对象
游戏模拟宿主拥有的状态和 tick 循环
由 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);

返回一个新的自有几何体;不要修改源几何体。修改器按组件顺序运行。可选的第三个参数会将节点缩放值作为 context.scale 提供。

声明资源并共享材质

在由资源驱动的组件定义中,声明其视图所用的每个资源:

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

资源类型包括 model、texture、sound 和 prefab;路径遵循 basePath。自定义共享材质使用 useSharedMaterialResource,并保持不可变。

针对性参考

只阅读任务所需的参考资料:

评论

支持 Markdown