react-three-game

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

react-three-game

react-three-game

Instructions for the agent to follow when this skill is activated.
当该技能激活时,Agent需要遵循的说明。

When to use

适用场景

generate 3D scenes, games and physics simulations in React.
在React中生成3D场景、游戏和物理模拟。

Agent Workflow: JSON → GLB

Agent工作流:JSON → GLB

Agents can programmatically generate 3D assets:
  1. Create a JSON prefab following the GameObject schema
  2. Load it in
    PrefabEditor
    to render the Three.js scene
  3. Export the scene to GLB format using
    exportGLB
    or
    exportGLBData
tsx
import { useRef, useEffect } from 'react';
import { PrefabEditor, exportGLBData } from 'react-three-game';
import type { PrefabEditorRef } from 'react-three-game'

const jsonPrefab = {
  root: {
    id: "scene",
    children: [
      {
        id: "cube",
        components: {
          transform: { type: "Transform", properties: { position: [0, 0, 0] } },
          geometry: { type: "Geometry", properties: { geometryType: "box", args: [1, 1, 1] } },
          material: { type: "Material", properties: { color: "#ff0000" } }
        }
      }
    ]
  }
};

function AgentExporter() {
  const editorRef = useRef<PrefabEditorRef>(null);

  useEffect(() => {
    const timer = setTimeout(async () => {
      const sceneRoot = editorRef.current?.rootRef.current?.root;
      if (!sceneRoot) return;
      
      const glbData = await exportGLBData(sceneRoot);
      // glbData is an ArrayBuffer ready for upload/storage
    }, 1000); // Wait for scene to render
    
    return () => clearTimeout(timer);
  }, []);

  return <PrefabEditor ref={editorRef} initialPrefab={jsonPrefab} />;
}
Agent可通过编程方式生成3D资源:
  1. 遵循GameObject schema创建JSON预制件
  2. PrefabEditor
    中加载该预制件以渲染Three.js场景
  3. 使用
    exportGLB
    exportGLBData
    将场景导出为GLB格式
tsx
import { useRef, useEffect } from 'react';
import { PrefabEditor, exportGLBData } from 'react-three-game';
import type { PrefabEditorRef } from 'react-three-game'

const jsonPrefab = {
  root: {
    id: "scene",
    children: [
      {
        id: "cube",
        components: {
          transform: { type: "Transform", properties: { position: [0, 0, 0] } },
          geometry: { type: "Geometry", properties: { geometryType: "box", args: [1, 1, 1] } },
          material: { type: "Material", properties: { color: "#ff0000" } }
        }
      }
    ]
  }
};

function AgentExporter() {
  const editorRef = useRef<PrefabEditorRef>(null);

  useEffect(() => {
    const timer = setTimeout(async () => {
      const sceneRoot = editorRef.current?.rootRef.current?.root;
      if (!sceneRoot) return;
      
      const glbData = await exportGLBData(sceneRoot);
      // glbData是可用于上传/存储的ArrayBuffer
    }, 1000); // 等待场景渲染完成
    
    return () => clearTimeout(timer);
  }, []);

  return <PrefabEditor ref={editorRef} initialPrefab={jsonPrefab} />;
}

Core Concepts

核心概念

Asset Paths and Public Directory

资源路径与公共目录

All asset paths are relative to
/public
and omit the
/public
prefix:
json
{
  "texture": "/textures/floor.png",
  "model": "/models/car.glb",
  "font": "/fonts/font.ttf"
}
Path
"/any/path/file.ext"
refers to
/public/any/path/file.ext
.
所有资源路径均相对于
/public
目录,且需省略
/public
前缀
json
{
  "texture": "/textures/floor.png",
  "model": "/models/car.glb",
  "font": "/fonts/font.ttf"
}
路径
"/any/path/file.ext"
对应
/public/any/path/file.ext

GameObject Structure

GameObject结构

Every game object follows this schema:
typescript
interface GameObject {
  id: string;
  disabled?: boolean;
  components?: Record<string, { type: string; properties: any }>;
  children?: GameObject[];
}
每个游戏对象均遵循以下schema:
typescript
interface GameObject {
  id: string;
  disabled?: boolean;
  components?: Record<string, { type: string; properties: any }>;
  children?: GameObject[];
}

Prefab JSON Format

预制件JSON格式

Scenes are defined as JSON prefabs with a root node containing children:
json
{
  "root": {
    "id": "scene",
    "children": [
      {
        "id": "my-object",
        "components": {
          "transform": { "type": "Transform", "properties": { "position": [0, 0, 0] } },
          "geometry": { "type": "Geometry", "properties": { "geometryType": "box" } },
          "material": { "type": "Material", "properties": { "color": "#ff0000" } }
        }
      }
    ]
  }
}
场景以JSON预制件形式定义,包含一个根节点和其子节点:
json
{
  "root": {
    "id": "scene",
    "children": [
      {
        "id": "my-object",
        "components": {
          "transform": { "type": "Transform", "properties": { "position": [0, 0, 0] } },
          "geometry": { "type": "Geometry", "properties": { "geometryType": "box" } },
          "material": { "type": "Material", "properties": { "color": "#ff0000" } }
        }
      }
    ]
  }
}

Built-in Components

内置组件

ComponentTypeKey Properties
Transform
Transform
position: [x,y,z]
,
rotation: [x,y,z]
(radians),
scale: [x,y,z]
Geometry
Geometry
geometryType
: box/sphere/plane/cylinder,
args
: dimension array
Material
Material
color
,
texture?
,
metalness?
,
roughness?
,
repeat?
,
repeatCount?
Physics
Physics
type
: dynamic/fixed/kinematicPosition/kinematicVelocity,
mass?
,
restitution?
,
friction?
,
linearDamping?
,
angularDamping?
,
gravityScale?
,
sensor?
,
activeCollisionTypes?: 'all'
(enable kinematic/fixed collision detection), plus any Rapier RigidBody props - See advanced physics guide
Model
Model
filename
(GLB/FBX path),
instanced?
for GPU batching
SpotLight
SpotLight
color
,
intensity
,
angle
,
penumbra
,
distance?
,
castShadow?
DirectionalLight
DirectionalLight
color
,
intensity
,
castShadow?
,
targetOffset?: [x,y,z]
AmbientLight
AmbientLight
color
,
intensity
Text
Text
text
,
font
,
size
,
depth
,
width
,
align
,
color
组件类型关键属性
Transform
Transform
position: [x,y,z]
,
rotation: [x,y,z]
(弧度),
scale: [x,y,z]
Geometry
Geometry
geometryType
: box/sphere/plane/cylinder,
args
: 维度数组
Material
Material
color
,
texture?
,
metalness?
,
roughness?
,
repeat?
,
repeatCount?
Physics
Physics
type
: dynamic/fixed/kinematicPosition/kinematicVelocity,
mass?
,
restitution?
,
friction?
,
linearDamping?
,
angularDamping?
,
gravityScale?
,
sensor?
,
activeCollisionTypes?: 'all'
(启用运动学/固定物体的碰撞检测),以及所有Rapier RigidBody属性 - 查看高级物理指南
Model
Model
filename
(GLB/FBX路径),
instanced?
(用于GPU批处理)
SpotLight
SpotLight
color
,
intensity
,
angle
,
penumbra
,
distance?
,
castShadow?
DirectionalLight
DirectionalLight
color
,
intensity
,
castShadow?
,
targetOffset?: [x,y,z]
AmbientLight
AmbientLight
color
,
intensity
Text
Text
text
,
font
,
size
,
depth
,
width
,
align
,
color

Text Component

Text组件

Requires
hb.wasm
and a font file (TTF/WOFF) in
/public/fonts/
:
Font property:
"font": "/fonts/NotoSans-Regular.ttf"
需要
hb.wasm
/public/fonts/
目录下的字体文件(TTF/WOFF格式):
字体属性:
"font": "/fonts/NotoSans-Regular.ttf"

Geometry Args by Type

不同几何类型的Args参数

geometryTypeargs array
box
[width, height, depth]
sphere
[radius, widthSegments, heightSegments]
plane
[width, height]
cylinder
[radiusTop, radiusBottom, height, radialSegments]
geometryTypeargs数组
box
[width, height, depth]
sphere
[radius, widthSegments, heightSegments]
plane
[width, height]
cylinder
[radiusTop, radiusBottom, height, radialSegments]

Material Textures

材质纹理

json
{
  "material": {
    "type": "Material",
    "properties": {
      "color": "white",
      "texture": "/textures/floor.png",
      "repeat": true,
      "repeatCount": [4, 4]
    }
  }
}
json
{
  "material": {
    "type": "Material",
    "properties": {
      "color": "white",
      "texture": "/textures/floor.png",
      "repeat": true,
      "repeatCount": [4, 4]
    }
  }
}

Rotations

旋转设置

Use radians:
1.57
= 90°,
3.14
= 180°,
-1.57
= -90°
使用弧度值:
1.57
= 90°,
3.14
= 180°,
-1.57
= -90°

Common Patterns

通用模式

Usage Modes

使用模式

GameCanvas + PrefabRoot: Pure renderer for embedding prefab data in standard R3F applications. Minimal wrapper - just renders the prefab as Three.js objects. Requires manual
<Physics>
setup. Physics always active. Use this to integrate prefabs into larger R3F scenes.
jsx
import { Physics } from '@react-three/rapier';
import { GameCanvas, PrefabRoot } from 'react-three-game';

<GameCanvas>
  <Physics>
    <PrefabRoot data={prefabData} />
    <CustomComponent />
  </Physics>
</GameCanvas>
PrefabEditor: Managed scene with editor UI and play/pause controls for physics. Full authoring tool for level design and prototyping. Includes canvas, physics, transform gizmos, and inspector. Physics only runs in play mode. Can pass R3F components as children.
jsx
import { PrefabEditor } from 'react-three-game';

<PrefabEditor initialPrefab={prefabData}>
  <CustomComponent />
</PrefabEditor>
GameCanvas + PrefabRoot:用于在标准R3F应用中嵌入预制件数据的纯渲染器。轻量封装 - 仅将预制件渲染为Three.js对象。需要手动设置
<Physics>
。物理效果始终处于激活状态。适用于将预制件集成到更大的R3F场景中。
jsx
import { Physics } from '@react-three/rapier';
import { GameCanvas, PrefabRoot } from 'react-three-game';

<GameCanvas>
  <Physics>
    <PrefabRoot data={prefabData} />
    <CustomComponent />
  </Physics>
</GameCanvas>
PrefabEditor:带有编辑器UI和物理效果播放/暂停控制的托管场景。用于关卡设计和原型开发的完整创作工具。包含画布、物理系统、变换 gizmo 和检查器。物理效果仅在播放模式下运行。可将R3F组件作为子元素传入。
jsx
import { PrefabEditor } from 'react-three-game';

<PrefabEditor initialPrefab={prefabData}>
  <CustomComponent />
</PrefabEditor>

Tree Utilities

树结构工具

typescript
import { findNode, updateNode, updateNodeById, deleteNode, cloneNode, exportGLBData } from 'react-three-game';

const node = findNode(root, nodeId);
const updated = updateNode(root, nodeId, n => ({ ...n, disabled: true }));  // or updateNodeById (identical)
const afterDelete = deleteNode(root, nodeId);
const cloned = cloneNode(node);
const glbData = await exportGLBData(sceneRoot);
typescript
import { findNode, updateNode, updateNodeById, deleteNode, cloneNode, exportGLBData } from 'react-three-game';

const node = findNode(root, nodeId);
const updated = updateNode(root, nodeId, n => ({ ...n, disabled: true }));  // 或使用updateNodeById(功能相同)
const afterDelete = deleteNode(root, nodeId);
const cloned = cloneNode(node);
const glbData = await exportGLBData(sceneRoot);

Hybrid JSON + R3F Children Pattern

JSON + R3F子元素混合模式

Prefabs define static scene structure, R3F children add dynamic behavior:
tsx
import { useRef } from 'react';
import { useFrame } from '@react-three/fiber';
import { PrefabEditor, findNode } from 'react-three-game';
import type { PrefabEditorRef } from 'react-three-game';

function DynamicLight() {
  const lightRef = useRef<THREE.SpotLight>(null!);
  
  useFrame(({ clock }) => {
    lightRef.current.intensity = 100 + Math.sin(clock.elapsedTime) * 50;
  });
  
  return <spotLight ref={lightRef} position={[10, 15, 10]} angle={0.5} />;
}

<PrefabEditor initialPrefab={staticScenePrefab}>
  <DynamicLight />
  <CustomController />
</PrefabEditor>
Use cases: Player controllers, AI behaviors, procedural animation, real-time effects.
预制件定义静态场景结构,R3F子元素添加动态行为
tsx
import { useRef } from 'react';
import { useFrame } from '@react-three/fiber';
import { PrefabEditor, findNode } from 'react-three-game';
import type { PrefabEditorRef } from 'react-three-game';

function DynamicLight() {
  const lightRef = useRef<THREE.SpotLight>(null!);
  
  useFrame(({ clock }) => {
    lightRef.current.intensity = 100 + Math.sin(clock.elapsedTime) * 50;
  });
  
  return <spotLight ref={lightRef} position={[10, 15, 10]} angle={0.5} />;
}

<PrefabEditor initialPrefab={staticScenePrefab}>
  <DynamicLight />
  <CustomController />
</PrefabEditor>
适用场景:玩家控制器、AI行为、程序化动画、实时特效。

Quick Reference Examples

快速参考示例

json
// Static geometry with physics (floor, wall, platform, ramp)
{ "id": "floor", "components": {
  "transform": { "type": "Transform", "properties": { "position": [0, -0.5, 0] } },
  "geometry": { "type": "Geometry", "properties": { "geometryType": "box", "args": [40, 1, 40] } },
  "material": { "type": "Material", "properties": { "texture": "/textures/floor.png", "repeat": true, "repeatCount": [20, 20] } },
  "physics": { "type": "Physics", "properties": { "type": "fixed" } }
}}

// Lighting
{ "id": "spot", "components": {
  "transform": { "type": "Transform", "properties": { "position": [10, 15, 10] } },
  "spotlight": { "type": "SpotLight", "properties": { "intensity": 200, "angle": 0.8, "castShadow": true } }
}}

// 3D Text
{ "id": "title", "components": {
  "transform": { "type": "Transform", "properties": { "position": [0, 3, 0] } },
  "text": { "type": "Text", "properties": { "text": "Welcome", "font": "/fonts/font.ttf", "size": 1, "depth": 0.1 } }
}}

// GLB Model
{ "id": "tree", "components": {
  "transform": { "type": "Transform", "properties": { "position": [0, 0, 0], "scale": [1.5, 1.5, 1.5] } },
  "model": { "type": "Model", "properties": { "filename": "/models/tree.glb" } }
}}
json
// 带物理效果的静态几何体(地面、墙壁、平台、坡道)
{ "id": "floor", "components": {
  "transform": { "type": "Transform", "properties": { "position": [0, -0.5, 0] } },
  "geometry": { "type": "Geometry", "properties": { "geometryType": "box", "args": [40, 1, 40] } },
  "material": { "type": "Material", "properties": { "texture": "/textures/floor.png", "repeat": true, "repeatCount": [20, 20] } },
  "physics": { "type": "Physics", "properties": { "type": "fixed" } }
}}

// 光照
{ "id": "spot", "components": {
  "transform": { "type": "Transform", "properties": { "position": [10, 15, 10] } },
  "spotlight": { "type": "SpotLight", "properties": { "intensity": 200, "angle": 0.8, "castShadow": true } }
}}

// 3D文本
{ "id": "title", "components": {
  "transform": { "type": "Transform", "properties": { "position": [0, 3, 0] } },
  "text": { "type": "Text", "properties": { "text": "Welcome", "font": "/fonts/font.ttf", "size": 1, "depth": 0.1 } }
}}

// GLB模型
{ "id": "tree", "components": {
  "transform": { "type": "Transform", "properties": { "position": [0, 0, 0], "scale": [1.5, 1.5, 1.5] } },
  "model": { "type": "Model", "properties": { "filename": "/models/tree.glb" } }
}}

Editor

编辑器

Basic Usage

基础用法

jsx
import { PrefabEditor } from 'react-three-game';

<PrefabEditor initialPrefab={sceneData} onPrefabChange={setSceneData} />
Keyboard shortcuts: T (Translate), R (Rotate), S (Scale)
jsx
import { PrefabEditor } from 'react-three-game';

<PrefabEditor initialPrefab={sceneData} onPrefabChange={setSceneData} />
键盘快捷键:T(平移)、R(旋转)、S(缩放)

Camera Control

相机控制

By default,
PrefabEditor
uses an orbit camera. Override it by adding a custom camera with
makeDefault
:
tsx
import { PerspectiveCamera } from '@react-three/drei';
import { PrefabEditor } from 'react-three-game';

<PrefabEditor initialPrefab={prefab}>
  <PerspectiveCamera makeDefault position={[0, 5, 10]} fov={75} />
</PrefabEditor>
Any R3F camera component works:
PerspectiveCamera
,
OrthographicCamera
, or custom camera controllers.
默认情况下,
PrefabEditor
使用轨道相机。可通过添加带有
makeDefault
属性的自定义相机来覆盖默认设置
tsx
import { PerspectiveCamera } from '@react-three/drei';
import { PrefabEditor } from 'react-three-game';

<PrefabEditor initialPrefab={prefab}>
  <PerspectiveCamera makeDefault position={[0, 5, 10]} fov={75} />
</PrefabEditor>
任何R3F相机组件均可使用:
PerspectiveCamera
OrthographicCamera
或自定义相机控制器。

Programmatic Updates

程序化更新

jsx
import { useRef } from 'react';
import { PrefabEditor, updateNodeById } from 'react-three-game';
import type { PrefabEditorRef } from 'react-three-game';

function Scene() {
  const editorRef = useRef<PrefabEditorRef>(null);

  const moveBall = () => {
    const prefab = editorRef.current!.prefab;
    const newRoot = updateNodeById(prefab.root, "ball", node => ({
      ...node,
      components: {
        ...node.components,
        transform: {
          ...node.components!.transform!,
          properties: { ...node.components!.transform!.properties, position: [5, 0, 0] }
        }
      }
    }));
    editorRef.current!.setPrefab({ ...prefab, root: newRoot });
  };

  return <PrefabEditor ref={editorRef} initialPrefab={sceneData} />;
}
PrefabEditorRef:
prefab
,
setPrefab()
,
screenshot()
,
exportGLB()
,
rootRef
jsx
import { useRef } from 'react';
import { PrefabEditor, updateNodeById } from 'react-three-game';
import type { PrefabEditorRef } from 'react-three-game';

function Scene() {
  const editorRef = useRef<PrefabEditorRef>(null);

  const moveBall = () => {
    const prefab = editorRef.current!.prefab;
    const newRoot = updateNodeById(prefab.root, "ball", node => ({
      ...node,
      components: {
        ...node.components,
        transform: {
          ...node.components!.transform!,
          properties: { ...node.components!.transform!.properties, position: [5, 0, 0] }
        }
      }
    }));
    editorRef.current!.setPrefab({ ...prefab, root: newRoot });
  };

  return <PrefabEditor ref={editorRef} initialPrefab={sceneData} />;
}
PrefabEditorRef
prefab
setPrefab()
screenshot()
exportGLB()
rootRef

GLB Export

GLB导出

tsx
import { exportGLBData } from 'react-three-game';

const glbData = await exportGLBData(editorRef.current!.rootRef.current!.root);
tsx
import { exportGLBData } from 'react-three-game';

const glbData = await exportGLBData(editorRef.current!.rootRef.current!.root);

Runtime Animation

运行时动画

tsx
import { useRef } from "react";
import { useFrame } from "@react-three/fiber";
import { PrefabEditor, updateNodeById } from "react-three-game";

function Animator({ editorRef }) {
  useFrame(() => {
    const prefab = editorRef.current!.prefab;
    const newRoot = updateNodeById(prefab.root, "ball", node => ({
      ...node,
      components: {
        ...node.components,
        transform: {
          ...node.components!.transform!,
          properties: { ...node.components!.transform!.properties, position: [x, y, z] }
        }
      }
    }));
    editorRef.current!.setPrefab({ ...prefab, root: newRoot });
  });
  return null;
}

function Scene() {
  const editorRef = useRef(null);
  return (
    <PrefabEditor ref={editorRef} initialPrefab={data}>
      <Animator editorRef={editorRef} />
    </PrefabEditor>
  );
}
tsx
import { useRef } from "react";
import { useFrame } from "@react-three/fiber";
import { PrefabEditor, updateNodeById } from "react-three-game";

function Animator({ editorRef }) {
  useFrame(() => {
    const prefab = editorRef.current!.prefab;
    const newRoot = updateNodeById(prefab.root, "ball", node => ({
      ...node,
      components: {
        ...node.components,
        transform: {
          ...node.components!.transform!,
          properties: { ...node.components!.transform!.properties, position: [x, y, z] }
        }
      }
    }));
    editorRef.current!.setPrefab({ ...prefab, root: newRoot });
  });
  return null;
}

function Scene() {
  const editorRef = useRef(null);
  return (
    <PrefabEditor ref={editorRef} initialPrefab={data}>
      <Animator editorRef={editorRef} />
    </PrefabEditor>
  );
}

Custom Component

自定义组件

tsx
import { Component, registerComponent, FieldRenderer } from 'react-three-game';

const MyComponent: Component = {
  name: 'MyComponent',
  Editor: ({ component, onUpdate }) => (
    <FieldRenderer fields={[{ name: 'speed', type: 'number', step: 0.1 }]} values={component.properties} onChange={onUpdate} />
  ),
  View: ({ properties, children }) => <group>{children}</group>,
  defaultProperties: { speed: 1 }
};

registerComponent(MyComponent);
Field types:
vector3
,
number
,
string
,
color
,
boolean
,
select
,
custom
tsx
import { Component, registerComponent, FieldRenderer } from 'react-three-game';

const MyComponent: Component = {
  name: 'MyComponent',
  Editor: ({ component, onUpdate }) => (
    <FieldRenderer fields={[{ name: 'speed', type: 'number', step: 0.1 }]} values={component.properties} onChange={onUpdate} />
  ),
  View: ({ properties, children }) => <group>{children}</group>,
  defaultProperties: { speed: 1 }
};

registerComponent(MyComponent);
字段类型
vector3
number
string
color
boolean
select
custom

Game Events

游戏事件

A general-purpose event system for game-wide communication. Handles physics events, gameplay events, and any custom events.
用于游戏全局通信的通用事件系统。处理物理事件、游戏玩法事件及任何自定义事件。

Core API

核心API

tsx
import { gameEvents, useGameEvent } from 'react-three-game';

// Emit events
gameEvents.emit('player:death', { playerId: 'p1', cause: 'lava' });
gameEvents.emit('score:change', { delta: 100, total: 500 });

// Subscribe (React hook - auto cleanup on unmount)
useGameEvent('player:death', (payload) => {
  showGameOver(payload.cause);
}, []);

// Subscribe (manual - returns unsubscribe function)
const unsub = gameEvents.on('score:change', (payload) => {
  updateUI(payload.total);
});
unsub(); // cleanup
tsx
import { gameEvents, useGameEvent } from 'react-three-game';

// 触发事件
gameEvents.emit('player:death', { playerId: 'p1', cause: 'lava' });
gameEvents.emit('score:change', { delta: 100, total: 500 });

// 订阅(React钩子 - 组件卸载时自动清理)
useGameEvent('player:death', (payload) => {
  showGameOver(payload.cause);
}, []);

// 订阅(手动方式 - 返回取消订阅函数)
const unsub = gameEvents.on('score:change', (payload) => {
  updateUI(payload.total);
});
unsub(); // 清理

Built-in Physics Events

内置物理事件

Physics components automatically emit these events:
EventWhenPayload
sensor:enter
Something enters a sensor collider
{ sourceEntityId, targetEntityId, targetRigidBody }
sensor:exit
Something exits a sensor collider
{ sourceEntityId, targetEntityId, targetRigidBody }
collision:enter
A collision starts
{ sourceEntityId, targetEntityId, targetRigidBody }
collision:exit
A collision ends
{ sourceEntityId, targetEntityId, targetRigidBody }
Collision filtering: By default, kinematic/fixed bodies don't detect each other. For kinematic sensors or projectiles to detect walls/floors, add
"activeCollisionTypes": "all"
to the Physics properties.
See Advanced Physics for sensor setup and collision handling patterns.
Physics组件会自动触发以下事件:
事件触发时机负载
sensor:enter
物体进入传感器碰撞体时
{ sourceEntityId, targetEntityId, targetRigidBody }
sensor:exit
物体离开传感器碰撞体时
{ sourceEntityId, targetEntityId, targetRigidBody }
collision:enter
碰撞开始时
{ sourceEntityId, targetEntityId, targetRigidBody }
collision:exit
碰撞结束时
{ sourceEntityId, targetEntityId, targetRigidBody }
碰撞过滤:默认情况下,运动学/固定物体不会互相检测。若要让运动学传感器或抛射物检测墙壁/地面,需在Physics属性中添加
"activeCollisionTypes": "all"
查看高级物理指南了解传感器设置和碰撞处理模式。

TypeScript: Typed Custom Events

TypeScript:类型化自定义事件

Extend
GameEventMap
for type-safe custom events:
typescript
declare module 'react-three-game' {
  interface GameEventMap {
    'player:death': { playerId: string; cause: string };
    'score:change': { delta: number; total: number };
    'level:complete': { levelId: number; time: number };
  }
}
扩展
GameEventMap
以实现类型安全的自定义事件:
typescript
declare module 'react-three-game' {
  interface GameEventMap {
    'player:death': { playerId: string; cause: string };
    'score:change': { delta: number; total: number };
    'level:complete': { levelId: number; time: number };
  }
}

Common Patterns

通用模式

tsx
// Gameplay controller
function GameController() {
  const [score, setScore] = useState(0);

  useGameEvent('score:change', ({ total }) => setScore(total), []);
  useGameEvent('player:death', () => setGameOver(true), []);

  return <ScoreUI score={score} />;
}

// Pickup system
useGameEvent('sensor:enter', (payload) => {
  if (payload.sourceEntityId.startsWith('coin-')) {
    gameEvents.emit('score:change', { delta: 10, total: score + 10 });
    removeEntity(payload.sourceEntityId);
  }
}, [score]);
tsx
// 游戏玩法控制器
function GameController() {
  const [score, setScore] = useState(0);

  useGameEvent('score:change', ({ total }) => setScore(total), []);
  useGameEvent('player:death', () => setGameOver(true), []);

  return <ScoreUI score={score} />;
}

// 拾取系统
useGameEvent('sensor:enter', (payload) => {
  if (payload.sourceEntityId.startsWith('coin-')) {
    gameEvents.emit('score:change', { delta: 10, total: score + 10 });
    removeEntity(payload.sourceEntityId);
  }
}, [score]);