react-three-game 0.0.59 → 0.0.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/tools/dragdrop/DragDropLoader.d.ts +8 -8
  2. package/dist/tools/dragdrop/DragDropLoader.js +33 -15
  3. package/dist/tools/dragdrop/index.d.ts +3 -3
  4. package/dist/tools/dragdrop/index.js +1 -1
  5. package/dist/tools/dragdrop/modelLoader.d.ts +10 -1
  6. package/dist/tools/dragdrop/modelLoader.js +39 -0
  7. package/dist/tools/prefabeditor/PrefabEditor.js +17 -26
  8. package/dist/tools/prefabeditor/PrefabRoot.d.ts +1 -1
  9. package/dist/tools/prefabeditor/PrefabRoot.js +2 -8
  10. package/package.json +9 -3
  11. package/.gitattributes +0 -2
  12. package/.github/copilot-instructions.md +0 -83
  13. package/.github/workflows/nextjs.yml +0 -99
  14. package/.gitmodules +0 -3
  15. package/assets/architecture.png +0 -0
  16. package/assets/editor.gif +0 -0
  17. package/assets/favicon.ico +0 -0
  18. package/assets/react-three-game-logo.png +0 -0
  19. package/dist/tools/dragdrop/page.d.ts +0 -1
  20. package/dist/tools/dragdrop/page.js +0 -11
  21. package/dist/tools/prefabeditor/EntityEvents.d.ts +0 -54
  22. package/dist/tools/prefabeditor/EntityEvents.js +0 -85
  23. package/dist/tools/prefabeditor/page.d.ts +0 -1
  24. package/dist/tools/prefabeditor/page.js +0 -5
  25. package/react-three-game-skill/.gitattributes +0 -2
  26. package/react-three-game-skill/README.md +0 -7
  27. package/react-three-game-skill/react-three-game/SKILL.md +0 -514
  28. package/react-three-game-skill/react-three-game/rules/ADVANCED_PHYSICS.md +0 -472
  29. package/react-three-game-skill/react-three-game/rules/LIGHTING.md +0 -6
  30. package/src/helpers/SoundManager.ts +0 -130
  31. package/src/helpers/index.ts +0 -91
  32. package/src/index.ts +0 -59
  33. package/src/shared/ContactShadow.tsx +0 -74
  34. package/src/shared/GameCanvas.tsx +0 -52
  35. package/src/tools/assetviewer/page.tsx +0 -425
  36. package/src/tools/dragdrop/DragDropLoader.tsx +0 -136
  37. package/src/tools/dragdrop/index.ts +0 -4
  38. package/src/tools/dragdrop/modelLoader.ts +0 -145
  39. package/src/tools/dragdrop/page.tsx +0 -45
  40. package/src/tools/prefabeditor/Dropdown.tsx +0 -112
  41. package/src/tools/prefabeditor/EditorContext.tsx +0 -25
  42. package/src/tools/prefabeditor/EditorTree.tsx +0 -452
  43. package/src/tools/prefabeditor/EditorTreeMenus.tsx +0 -307
  44. package/src/tools/prefabeditor/EditorUI.tsx +0 -204
  45. package/src/tools/prefabeditor/EventSystem.tsx +0 -36
  46. package/src/tools/prefabeditor/GameEvents.ts +0 -191
  47. package/src/tools/prefabeditor/InstanceProvider.tsx +0 -466
  48. package/src/tools/prefabeditor/PrefabEditor.tsx +0 -262
  49. package/src/tools/prefabeditor/PrefabRoot.tsx +0 -773
  50. package/src/tools/prefabeditor/components/AmbientLightComponent.tsx +0 -34
  51. package/src/tools/prefabeditor/components/CameraComponent.tsx +0 -117
  52. package/src/tools/prefabeditor/components/ComponentRegistry.ts +0 -40
  53. package/src/tools/prefabeditor/components/DirectionalLightComponent.tsx +0 -210
  54. package/src/tools/prefabeditor/components/EnvironmentComponent.tsx +0 -47
  55. package/src/tools/prefabeditor/components/GeometryComponent.tsx +0 -133
  56. package/src/tools/prefabeditor/components/Input.tsx +0 -820
  57. package/src/tools/prefabeditor/components/MaterialComponent.tsx +0 -431
  58. package/src/tools/prefabeditor/components/ModelComponent.tsx +0 -176
  59. package/src/tools/prefabeditor/components/PhysicsComponent.tsx +0 -188
  60. package/src/tools/prefabeditor/components/SpotLightComponent.tsx +0 -109
  61. package/src/tools/prefabeditor/components/TextComponent.tsx +0 -137
  62. package/src/tools/prefabeditor/components/TransformComponent.tsx +0 -173
  63. package/src/tools/prefabeditor/components/index.ts +0 -26
  64. package/src/tools/prefabeditor/page.tsx +0 -10
  65. package/src/tools/prefabeditor/styles.ts +0 -235
  66. package/src/tools/prefabeditor/types.ts +0 -20
  67. package/src/tools/prefabeditor/utils.ts +0 -312
@@ -1,85 +0,0 @@
1
- import { useEffect, useCallback } from 'react';
2
- // Internal subscriber storage
3
- const subscribers = new Map();
4
- /**
5
- * Entity event system for physics interactions.
6
- *
7
- * Events:
8
- * - sensor:enter - Fired when something enters a sensor collider
9
- * - sensor:exit - Fired when something exits a sensor collider
10
- * - collision:enter - Fired when a collision starts
11
- * - collision:exit - Fired when a collision ends
12
- */
13
- export const entityEvents = {
14
- /**
15
- * Emit an event to all subscribers
16
- */
17
- emit(type, payload) {
18
- const handlers = subscribers.get(type);
19
- if (handlers) {
20
- handlers.forEach(handler => {
21
- try {
22
- handler(payload);
23
- }
24
- catch (e) {
25
- console.error(`Error in entityEvents handler for ${type}:`, e);
26
- }
27
- });
28
- }
29
- },
30
- /**
31
- * Subscribe to an event type
32
- * @returns Unsubscribe function
33
- */
34
- on(type, handler) {
35
- if (!subscribers.has(type)) {
36
- subscribers.set(type, new Set());
37
- }
38
- subscribers.get(type).add(handler);
39
- return () => {
40
- var _a;
41
- (_a = subscribers.get(type)) === null || _a === void 0 ? void 0 : _a.delete(handler);
42
- };
43
- },
44
- /**
45
- * Unsubscribe from an event type
46
- */
47
- off(type, handler) {
48
- var _a;
49
- (_a = subscribers.get(type)) === null || _a === void 0 ? void 0 : _a.delete(handler);
50
- },
51
- /**
52
- * Remove all subscribers (useful for cleanup)
53
- */
54
- clear() {
55
- subscribers.clear();
56
- }
57
- };
58
- /**
59
- * React hook to subscribe to entity events.
60
- * Automatically cleans up on unmount.
61
- *
62
- * @example
63
- * useEntityEvent('sensor:enter', (payload) => {
64
- * if (payload.sourceEntityId === 'trigger-zone') {
65
- * console.log('Player entered trigger zone!');
66
- * }
67
- * });
68
- */
69
- export function useEntityEvent(type, handler, deps = []) {
70
- const stableHandler = useCallback(handler, deps);
71
- useEffect(() => {
72
- return entityEvents.on(type, stableHandler);
73
- }, [type, stableHandler]);
74
- }
75
- /**
76
- * Helper to extract entity ID from Rapier collision data.
77
- * Entity IDs are stored in RigidBody userData.
78
- */
79
- export function getEntityIdFromRigidBody(rigidBody) {
80
- var _a;
81
- if (!rigidBody)
82
- return null;
83
- const userData = rigidBody.userData;
84
- return (_a = userData === null || userData === void 0 ? void 0 : userData.entityId) !== null && _a !== void 0 ? _a : null;
85
- }
@@ -1 +0,0 @@
1
- export default function PrefabEditorPage(): import("react/jsx-runtime").JSX.Element;
@@ -1,5 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import PrefabEditor from "./PrefabEditor";
3
- export default function PrefabEditorPage() {
4
- return _jsx("div", { style: { width: '100%', height: '100%' }, children: _jsx(PrefabEditor, { children: _jsx("directionalLight", { position: [5, 10, 7.5], intensity: 1, castShadow: true }) }) });
5
- }
@@ -1,2 +0,0 @@
1
- # Auto detect text files and perform LF normalization
2
- * text=auto
@@ -1,7 +0,0 @@
1
- Agent skill for [react-three-game](https://github.com/prnthh/react-three-game)
2
-
3
- Gives your agent the ability to make 3D scenes, physics simulations and games.
4
-
5
- ```
6
- npx skills add https://github.com/prnthh/react-three-game-skill
7
- ```
@@ -1,514 +0,0 @@
1
- ---
2
- name: react-three-game
3
- description: react-three-game, a JSON-first 3D game engine built on React Three Fiber, WebGPU, and Rapier Physics.
4
- ---
5
-
6
- # react-three-game
7
-
8
- Instructions for the agent to follow when this skill is activated.
9
-
10
- ## When to use
11
-
12
- generate 3D scenes, games and physics simulations in React.
13
-
14
- ## Agent Workflow: JSON → GLB
15
-
16
- Agents can programmatically generate 3D assets:
17
-
18
- 1. Create a JSON prefab following the GameObject schema
19
- 2. Load it in `PrefabEditor` to render the Three.js scene
20
- 3. Export the scene to GLB format using `exportGLB` or `exportGLBData`
21
-
22
- ```tsx
23
- import { useRef, useEffect } from 'react';
24
- import { PrefabEditor, exportGLBData } from 'react-three-game';
25
- import type { PrefabEditorRef } from 'react-three-game'
26
-
27
- const jsonPrefab = {
28
- root: {
29
- id: "scene",
30
- children: [
31
- {
32
- id: "cube",
33
- components: {
34
- transform: { type: "Transform", properties: { position: [0, 0, 0] } },
35
- geometry: { type: "Geometry", properties: { geometryType: "box", args: [1, 1, 1] } },
36
- material: { type: "Material", properties: { color: "#ff0000" } }
37
- }
38
- }
39
- ]
40
- }
41
- };
42
-
43
- function AgentExporter() {
44
- const editorRef = useRef<PrefabEditorRef>(null);
45
-
46
- useEffect(() => {
47
- const timer = setTimeout(async () => {
48
- const sceneRoot = editorRef.current?.rootRef.current?.root;
49
- if (!sceneRoot) return;
50
-
51
- const glbData = await exportGLBData(sceneRoot);
52
- // glbData is an ArrayBuffer ready for upload/storage
53
- }, 1000); // Wait for scene to render
54
-
55
- return () => clearTimeout(timer);
56
- }, []);
57
-
58
- return <PrefabEditor ref={editorRef} initialPrefab={jsonPrefab} />;
59
- }
60
- ```
61
-
62
- ## Core Concepts
63
-
64
- ### Asset Paths and Public Directory
65
-
66
- **All asset paths are relative to `/public`** and omit the `/public` prefix:
67
-
68
- ```json
69
- {
70
- "texture": "/textures/floor.png",
71
- "model": "/models/car.glb",
72
- "font": "/fonts/font.ttf"
73
- }
74
- ```
75
-
76
- Path `"/any/path/file.ext"` refers to `/public/any/path/file.ext`.
77
- ### GameObject Structure
78
-
79
- Every game object follows this schema:
80
-
81
- ```typescript
82
- interface GameObject {
83
- id: string;
84
- name?: string;
85
- disabled?: boolean;
86
- components?: Record<string, { type: string; properties: any }>;
87
- children?: GameObject[];
88
- }
89
- ```
90
-
91
- `disabled` is the canonical visibility toggle. Transforms are local to the parent node.
92
-
93
- ### Prefab JSON Format
94
-
95
- Scenes are defined as JSON prefabs with a root node containing children:
96
-
97
- ```json
98
- {
99
- "root": {
100
- "id": "scene",
101
- "children": [
102
- {
103
- "id": "my-object",
104
- "components": {
105
- "transform": { "type": "Transform", "properties": { "position": [0, 0, 0] } },
106
- "geometry": { "type": "Geometry", "properties": { "geometryType": "box" } },
107
- "material": { "type": "Material", "properties": { "color": "#ff0000" } }
108
- }
109
- }
110
- ]
111
- }
112
- }
113
- ```
114
-
115
- ## Built-in Components
116
-
117
- | Component | Type | Key Properties |
118
- |-----------|------|----------------|
119
- | Transform | `Transform` | `position: [x,y,z]`, `rotation: [x,y,z]` (radians), `scale: [x,y,z]` |
120
- | Geometry | `Geometry` | `geometryType`: box/sphere/plane/cylinder, `args`: dimension array |
121
- | Material | `Material` | `color`, `texture?`, `metalness?`, `roughness?`, `repeat?`, `repeatCount?` |
122
- | 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](./rules/ADVANCED_PHYSICS.md) |
123
- | Model | `Model` | `filename` (GLB/FBX path), `instanced?` for GPU batching |
124
- | SpotLight | `SpotLight` | `color`, `intensity`, `angle`, `penumbra`, `distance?`, `castShadow?` |
125
- | DirectionalLight | `DirectionalLight` | `color`, `intensity`, `castShadow?`, `targetOffset?: [x,y,z]` |
126
- | AmbientLight | `AmbientLight` | `color`, `intensity` |
127
- | Environment | `Environment` | `intensity`, `resolution` |
128
- | Camera | `Camera` | `fov`, `near`, `far`, `zoom` |
129
- | Text | `Text` | `text`, `font`, `size`, `depth`, `width`, `align`, `color` |
130
-
131
- ### Text Component
132
-
133
- Requires `hb.wasm` and a font file (TTF/WOFF) in `/public/fonts/`:
134
- - hb.wasm: https://github.com/prnthh/react-three-game/raw/refs/heads/main/docs/public/fonts/hb.wasm
135
- - Sample font: https://github.com/prnthh/react-three-game/raw/refs/heads/main/docs/public/fonts/NotoSans-Regular.ttf
136
-
137
- Font property: `"font": "/fonts/NotoSans-Regular.ttf"`
138
-
139
- ### Geometry Args by Type
140
-
141
- | geometryType | args array |
142
- |--------------|------------|
143
- | `box` | `[width, height, depth]` |
144
- | `sphere` | `[radius, widthSegments, heightSegments]` |
145
- | `plane` | `[width, height]` |
146
- | `cylinder` | `[radiusTop, radiusBottom, height, radialSegments]` |
147
-
148
- ### Material Textures
149
-
150
- ```json
151
- {
152
- "material": {
153
- "type": "Material",
154
- "properties": {
155
- "color": "white",
156
- "texture": "/textures/floor.png",
157
- "repeat": true,
158
- "repeatCount": [4, 4]
159
- }
160
- }
161
- }
162
- ```
163
-
164
- ### Rotations
165
-
166
- Use radians: `1.57` = 90°, `3.14` = 180°, `-1.57` = -90°
167
-
168
- ## Common Patterns
169
-
170
- ### Usage Modes
171
-
172
- **PrefabRoot**: Pure renderer for embedding prefab data in standard R3F applications. Render it inside a regular `@react-three/fiber` `Canvas`. `GameCanvas` provides the WebGPU canvas setup. Add a `Physics` wrapper to enable physics. Use this to integrate prefabs into larger R3F scenes.
173
-
174
- ```jsx
175
- import { Canvas } from '@react-three/fiber';
176
- import { Physics } from '@react-three/rapier';
177
- import { PrefabRoot } from 'react-three-game';
178
-
179
- <Canvas>
180
- <Physics>
181
- <PrefabRoot data={prefabData} />
182
- <CustomComponent />
183
- </Physics>
184
- </Canvas>
185
- ```
186
-
187
- `GameCanvas` provides the library's WebGPU canvas setup.
188
-
189
- **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. Editor actions live under `Menu > File`, and exports under `Menu > Export`.
190
-
191
- ```jsx
192
- import { PrefabEditor } from 'react-three-game';
193
-
194
- <PrefabEditor initialPrefab={prefabData}>
195
- <CustomComponent />
196
- </PrefabEditor>
197
- ```
198
-
199
- ### Tree Utilities
200
-
201
- ```typescript
202
- import { findNode, updateNode, updateNodeById, deleteNode, cloneNode, exportGLBData } from 'react-three-game';
203
-
204
- const node = findNode(root, nodeId);
205
- const updated = updateNode(root, nodeId, n => ({ ...n, disabled: true })); // or updateNodeById (identical)
206
- const afterDelete = deleteNode(root, nodeId);
207
- const cloned = cloneNode(node);
208
- const glbData = await exportGLBData(sceneRoot);
209
- ```
210
-
211
- ## Hybrid JSON + R3F Children Pattern
212
-
213
- **Prefabs define static scene structure, R3F children add dynamic behavior**:
214
-
215
- ```tsx
216
- import { useRef } from 'react';
217
- import { useFrame } from '@react-three/fiber';
218
- import { PrefabEditor, findNode } from 'react-three-game';
219
- import type { PrefabEditorRef } from 'react-three-game';
220
-
221
- function DynamicLight() {
222
- const lightRef = useRef<THREE.SpotLight>(null!);
223
-
224
- useFrame(({ clock }) => {
225
- lightRef.current.intensity = 100 + Math.sin(clock.elapsedTime) * 50;
226
- });
227
-
228
- return <spotLight ref={lightRef} position={[10, 15, 10]} angle={0.5} />;
229
- }
230
-
231
- <PrefabEditor initialPrefab={staticScenePrefab}>
232
- <DynamicLight />
233
- <CustomController />
234
- </PrefabEditor>
235
- ```
236
-
237
- **Use cases**: Player controllers, AI behaviors, procedural animation, real-time effects.
238
-
239
- ## World Scene Pattern
240
-
241
- The current world demo combines prefab-authored level geometry with runtime React behavior:
242
-
243
- - Static level layout, props, and collision live in prefab JSON.
244
- - `Environment` can wrap sky geometry or lighting content for a full scene backdrop.
245
- - `Camera` can live in the prefab so view-only scenes and editor scenes share the same authored viewpoint.
246
- - Runtime logic can use `useFrame` plus `updateNodeById` to animate prefab entities without abandoning the JSON scene model.
247
-
248
- ```json
249
- {
250
- "id": "environment",
251
- "components": {
252
- "environment": {
253
- "type": "Environment",
254
- "properties": { "intensity": 1, "resolution": 256 }
255
- }
256
- },
257
- "children": [
258
- {
259
- "id": "sky",
260
- "components": {
261
- "geometry": { "type": "Geometry", "properties": { "geometryType": "sphere", "args": [100, 32, 16] } },
262
- "material": { "type": "Material", "properties": { "texture": "/textures/skybox/skybox1.jpg", "side": "BackSide", "materialType": "basic" } }
263
- }
264
- }
265
- ]
266
- }
267
- ```
268
-
269
- ## Quick Reference Examples
270
-
271
- ```json
272
- // Static geometry with physics (floor, wall, platform, ramp)
273
- { "id": "floor", "components": {
274
- "transform": { "type": "Transform", "properties": { "position": [0, -0.5, 0] } },
275
- "geometry": { "type": "Geometry", "properties": { "geometryType": "box", "args": [40, 1, 40] } },
276
- "material": { "type": "Material", "properties": { "texture": "/textures/floor.png", "repeat": true, "repeatCount": [20, 20] } },
277
- "physics": { "type": "Physics", "properties": { "type": "fixed" } }
278
- }}
279
-
280
- // Lighting
281
- { "id": "spot", "components": {
282
- "transform": { "type": "Transform", "properties": { "position": [10, 15, 10] } },
283
- "spotlight": { "type": "SpotLight", "properties": { "intensity": 200, "angle": 0.8, "castShadow": true } }
284
- }}
285
-
286
- // 3D Text
287
- { "id": "title", "components": {
288
- "transform": { "type": "Transform", "properties": { "position": [0, 3, 0] } },
289
- "text": { "type": "Text", "properties": { "text": "Welcome", "font": "/fonts/font.ttf", "size": 1, "depth": 0.1 } }
290
- }}
291
-
292
- // GLB Model
293
- { "id": "tree", "components": {
294
- "transform": { "type": "Transform", "properties": { "position": [0, 0, 0], "scale": [1.5, 1.5, 1.5] } },
295
- "model": { "type": "Model", "properties": { "filename": "/models/tree.glb" } }
296
- }}
297
- ```
298
-
299
- ## Editor
300
-
301
- ### Basic Usage
302
-
303
- ```jsx
304
- import { PrefabEditor } from 'react-three-game';
305
-
306
- <PrefabEditor initialPrefab={sceneData} onPrefabChange={setSceneData} />
307
- ```
308
-
309
- Keyboard shortcuts: **T** (Translate), **R** (Rotate), **S** (Scale)
310
-
311
- ### Camera Control
312
-
313
- By default, `PrefabEditor` uses an orbit camera. **Override it by adding a custom camera with `makeDefault`**:
314
-
315
- ```tsx
316
- import { PerspectiveCamera } from '@react-three/drei';
317
- import { PrefabEditor } from 'react-three-game';
318
-
319
- <PrefabEditor initialPrefab={prefab}>
320
- <PerspectiveCamera makeDefault position={[0, 5, 10]} fov={75} />
321
- </PrefabEditor>
322
- ```
323
-
324
- Any R3F camera component works: `PerspectiveCamera`, `OrthographicCamera`, or custom camera controllers.
325
-
326
- ### Programmatic Updates
327
-
328
- ```jsx
329
- import { useRef } from 'react';
330
- import { PrefabEditor, updateNodeById } from 'react-three-game';
331
- import type { PrefabEditorRef } from 'react-three-game';
332
-
333
- function Scene() {
334
- const editorRef = useRef<PrefabEditorRef>(null);
335
-
336
- const moveBall = () => {
337
- const prefab = editorRef.current!.prefab;
338
- const newRoot = updateNodeById(prefab.root, "ball", node => ({
339
- ...node,
340
- components: {
341
- ...node.components,
342
- transform: {
343
- ...node.components!.transform!,
344
- properties: { ...node.components!.transform!.properties, position: [5, 0, 0] }
345
- }
346
- }
347
- }));
348
- editorRef.current!.setPrefab({ ...prefab, root: newRoot });
349
- };
350
-
351
- return <PrefabEditor ref={editorRef} initialPrefab={sceneData} />;
352
- }
353
- ```
354
-
355
- **PrefabEditorRef**: `prefab`, `setPrefab()`, `screenshot()`, `exportGLB()`, `rootRef`
356
-
357
- ### GLB Export
358
-
359
- ```tsx
360
- import { exportGLBData } from 'react-three-game';
361
-
362
- const glbData = await exportGLBData(editorRef.current!.rootRef.current!.root);
363
- ```
364
-
365
- ### Runtime Animation
366
-
367
- ```tsx
368
- import { useRef } from "react";
369
- import { useFrame } from "@react-three/fiber";
370
- import { PrefabEditor, updateNodeById } from "react-three-game";
371
-
372
- function Animator({ editorRef }) {
373
- useFrame(() => {
374
- const prefab = editorRef.current!.prefab;
375
- const newRoot = updateNodeById(prefab.root, "ball", node => ({
376
- ...node,
377
- components: {
378
- ...node.components,
379
- transform: {
380
- ...node.components!.transform!,
381
- properties: { ...node.components!.transform!.properties, position: [x, y, z] }
382
- }
383
- }
384
- }));
385
- editorRef.current!.setPrefab({ ...prefab, root: newRoot });
386
- });
387
- return null;
388
- }
389
-
390
- function Scene() {
391
- const editorRef = useRef(null);
392
- return (
393
- <PrefabEditor ref={editorRef} initialPrefab={data}>
394
- <Animator editorRef={editorRef} />
395
- </PrefabEditor>
396
- );
397
- }
398
- ```
399
-
400
- ### Custom Component
401
-
402
- ```tsx
403
- import { Component, registerComponent, FieldRenderer } from 'react-three-game';
404
-
405
- const MyComponent: Component = {
406
- name: 'MyComponent',
407
- Editor: ({ component, onUpdate }) => (
408
- <FieldRenderer fields={[{ name: 'speed', type: 'number', step: 0.1 }]} values={component.properties} onChange={onUpdate} />
409
- ),
410
- View: ({ properties, children }) => <group>{children}</group>,
411
- defaultProperties: { speed: 1 }
412
- };
413
-
414
- registerComponent(MyComponent);
415
- ```
416
-
417
- Use the component in prefab JSON by adding a component entry whose `type` matches the registered component name:
418
-
419
- ```json
420
- {
421
- "components": {
422
- "mycomponent": {
423
- "type": "MyComponent",
424
- "properties": {
425
- "speed": 1
426
- }
427
- }
428
- }
429
- }
430
- ```
431
-
432
- Rules:
433
- - Call `registerComponent(MyComponent)` before rendering `<PrefabEditor>` or `<PrefabRoot>` with prefab data that uses it.
434
- - `type` must match the registered component name exactly (`name: 'MyComponent'` -> `"type": "MyComponent"`).
435
- - Use `View` to render visible content, wrap `children`, or add runtime behavior with hooks like `useFrame`.
436
-
437
- **Field types**: `vector3`, `number`, `string`, `color`, `boolean`, `select`, `custom`
438
-
439
- ## Game Events
440
-
441
- A general-purpose event system for game-wide communication. Handles physics events, gameplay events, and any custom events.
442
-
443
- ### Core API
444
-
445
- ```tsx
446
- import { gameEvents, useGameEvent } from 'react-three-game';
447
-
448
- // Emit events
449
- gameEvents.emit('player:death', { playerId: 'p1', cause: 'lava' });
450
- gameEvents.emit('score:change', { delta: 100, total: 500 });
451
-
452
- // Subscribe (React hook - auto cleanup on unmount)
453
- useGameEvent('player:death', (payload) => {
454
- showGameOver(payload.cause);
455
- }, []);
456
-
457
- // Subscribe (manual - returns unsubscribe function)
458
- const unsub = gameEvents.on('score:change', (payload) => {
459
- updateUI(payload.total);
460
- });
461
- unsub(); // cleanup
462
- ```
463
-
464
- ### Built-in Physics Events
465
-
466
- Physics components automatically emit these events:
467
-
468
- | Event | When | Payload |
469
- |-------|------|---------|
470
- | `sensor:enter` | Something enters a sensor collider | `{ sourceEntityId, targetEntityId, targetRigidBody }` |
471
- | `sensor:exit` | Something exits a sensor collider | `{ sourceEntityId, targetEntityId, targetRigidBody }` |
472
- | `collision:enter` | A collision starts | `{ sourceEntityId, targetEntityId, targetRigidBody }` |
473
- | `collision:exit` | A collision ends | `{ sourceEntityId, targetEntityId, targetRigidBody }` |
474
-
475
- **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.
476
-
477
- See [Advanced Physics](./rules/ADVANCED_PHYSICS.md) for sensor setup and collision handling patterns.
478
-
479
- ### TypeScript: Typed Custom Events
480
-
481
- Extend `GameEventMap` for type-safe custom events:
482
-
483
- ```typescript
484
- declare module 'react-three-game' {
485
- interface GameEventMap {
486
- 'player:death': { playerId: string; cause: string };
487
- 'score:change': { delta: number; total: number };
488
- 'level:complete': { levelId: number; time: number };
489
- }
490
- }
491
- ```
492
-
493
- ### Common Patterns
494
-
495
- ```tsx
496
- // Gameplay controller
497
- function GameController() {
498
- const [score, setScore] = useState(0);
499
-
500
- useGameEvent('score:change', ({ total }) => setScore(total), []);
501
- useGameEvent('player:death', () => setGameOver(true), []);
502
-
503
- return <ScoreUI score={score} />;
504
- }
505
-
506
- // Pickup system
507
- useGameEvent('sensor:enter', (payload) => {
508
- if (payload.sourceEntityId.startsWith('coin-')) {
509
- gameEvents.emit('score:change', { delta: 10, total: score + 10 });
510
- removeEntity(payload.sourceEntityId);
511
- }
512
- }, [score]);
513
- ```
514
-