关于 react-three-game
react-three-game 是面向 Web 游戏创作的工具包,以称为 prefab 的 JSON 场景文档为核心。开发者和 Agent 可用它编辑场景、添加可复用组件,或将场景内容渲染到应用自己的画布中。
主要功能
- 01通过运行中的编辑器 API 编辑场景
- 02直接修改 prefab JSON
- 03添加可复用自定义组件
- 04用 Runtime 组件运行场景脚本
- 05用 PrefabRoot 渲染场景
- 06加载并激活 prefab 分块
适用场景
- 01调整实时场景中的物体位置
- 02生成或审查场景 JSON 改动
- 03添加可复用行为或视觉组件
- 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.yamlrules/ADVANCED_PHYSICS.mdLIGHTING.mdPERFORMANCE.mdRUNTIME_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,并保持不可变。
针对性参考
只阅读任务所需的参考资料: