react-three-game 0.0.85 → 0.0.87
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.
- package/README.md +87 -35
- package/dist/index.d.ts +5 -7
- package/dist/index.js +2 -4
- package/dist/tools/prefabeditor/GameEvents.d.ts +36 -117
- package/dist/tools/prefabeditor/GameEvents.js +44 -96
- package/dist/tools/prefabeditor/InstanceProvider.d.ts +0 -4
- package/dist/tools/prefabeditor/InstanceProvider.js +13 -44
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +7 -2
- package/dist/tools/prefabeditor/PrefabEditor.js +13 -24
- package/dist/tools/prefabeditor/PrefabRoot.js +93 -44
- package/dist/tools/prefabeditor/{runtime.d.ts → assetRuntime.d.ts} +0 -25
- package/dist/tools/prefabeditor/assetRuntime.js +37 -0
- package/dist/tools/prefabeditor/components/BufferGeometryComponent.js +4 -2
- package/dist/tools/prefabeditor/components/CameraComponent.js +1 -1
- package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +0 -3
- package/dist/tools/prefabeditor/components/DataComponent.d.ts +3 -0
- package/dist/tools/prefabeditor/components/DataComponent.js +87 -0
- package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +1 -1
- package/dist/tools/prefabeditor/components/EnvironmentComponent.js +1 -1
- package/dist/tools/prefabeditor/components/GeometryComponent.js +4 -2
- package/dist/tools/prefabeditor/components/Input.d.ts +2 -13
- package/dist/tools/prefabeditor/components/Input.js +0 -55
- package/dist/tools/prefabeditor/components/MaterialComponent.js +1 -1
- package/dist/tools/prefabeditor/components/ModelComponent.js +3 -3
- package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +4 -0
- package/dist/tools/prefabeditor/components/PhysicsComponent.js +64 -130
- package/dist/tools/prefabeditor/components/PointLightComponent.js +1 -1
- package/dist/tools/prefabeditor/components/SoundComponent.js +18 -11
- package/dist/tools/prefabeditor/components/SpotLightComponent.js +1 -1
- package/dist/tools/prefabeditor/components/index.js +2 -2
- package/dist/tools/prefabeditor/types.d.ts +1 -0
- package/dist/tools/prefabeditor/types.js +18 -0
- package/dist/tools/prefabeditor/usePointerEvents.d.ts +27 -0
- package/dist/tools/prefabeditor/usePointerEvents.js +52 -0
- package/package.json +1 -1
- package/dist/tools/prefabeditor/components/ClickComponent.d.ts +0 -3
- package/dist/tools/prefabeditor/components/ClickComponent.js +0 -52
- package/dist/tools/prefabeditor/runtime.js +0 -184
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
JSON-first
|
|
5
|
+
JSON-first scene mounting and authoring for React Three Fiber.
|
|
6
6
|
|
|
7
7
|
Built on top of [three.js](https://github.com/mrdoob/three.js), [@react-three/fiber](https://github.com/pmndrs/react-three-fiber), and [@react-three/rapier](https://github.com/pmndrs/react-three-rapier).
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@ Built on top of [three.js](https://github.com/mrdoob/three.js), [@react-three/fi
|
|
|
10
10
|
* **🎬 Scene Editor** - Edit prefabs visually with hierarchy, inspector, transform gizmos, and play mode.
|
|
11
11
|
* **⚛️ Physics** - Author rigid bodies directly in prefab data and run them through Rapier.
|
|
12
12
|
* **🧩 Components** - Build scenes from reusable `GameObject` + component composition.
|
|
13
|
-
* **🔧 Runtime
|
|
13
|
+
* **🔧 Direct Runtime Access** - Get native `Object3D`, Rapier rigid body, and prefab store access without a parallel engine API.
|
|
14
14
|
* **⚡ R3F Native** - Use normal React Three Fiber components whenever runtime behavior is clearer in code.
|
|
15
15
|
|
|
16
16
|
## Documentation
|
|
@@ -98,12 +98,10 @@ Open the hosted editor here:
|
|
|
98
98
|
|
|
99
99
|
* https://prnth.com/react-three-game/editor
|
|
100
100
|
|
|
101
|
-
## Prefabs And Scenes
|
|
101
|
+
## Prefabs And Mounted Scenes
|
|
102
102
|
|
|
103
103
|
`Prefab` is the serializable pure data format.
|
|
104
104
|
|
|
105
|
-
`Scene` is the live runtime/editor world handle.
|
|
106
|
-
|
|
107
105
|
That means a saved scene is just a prefab, and the same prefab can be:
|
|
108
106
|
|
|
109
107
|
* edited directly in `PrefabEditor`
|
|
@@ -163,7 +161,7 @@ interface GameObject {
|
|
|
163
161
|
|
|
164
162
|
## Runtime Mutation
|
|
165
163
|
|
|
166
|
-
Use
|
|
164
|
+
Use native object access for Three.js behavior and the prefab store for authored data changes.
|
|
167
165
|
|
|
168
166
|
```tsx
|
|
169
167
|
import { useEffect, useRef } from "react";
|
|
@@ -173,48 +171,105 @@ function RaiseBall() {
|
|
|
173
171
|
const editorRef = useRef<PrefabEditorRef>(null);
|
|
174
172
|
|
|
175
173
|
useEffect(() => {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
174
|
+
editorRef.current?.store.getState().updateNode("ball", (node) => ({
|
|
175
|
+
...node,
|
|
176
|
+
components: {
|
|
177
|
+
...node.components,
|
|
178
|
+
transform: {
|
|
179
|
+
type: "Transform",
|
|
180
|
+
properties: {
|
|
181
|
+
...node.components?.transform?.properties,
|
|
182
|
+
position: [0, 8, 0],
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
}));
|
|
182
187
|
}, []);
|
|
183
188
|
|
|
184
189
|
return <PrefabEditor ref={editorRef} initialPrefab={prefab} />;
|
|
185
190
|
}
|
|
186
191
|
```
|
|
187
192
|
|
|
188
|
-
|
|
193
|
+
For live Three.js access, use mounted objects directly:
|
|
189
194
|
|
|
190
195
|
```tsx
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
scene.find("orb2")?.getComponent("Transform")?.set("position", [-1, 0, 0]);
|
|
194
|
-
});
|
|
196
|
+
const ball = editorRef.current?.getObject("ball");
|
|
197
|
+
ball?.rotateY(0.5);
|
|
195
198
|
```
|
|
196
199
|
|
|
197
|
-
|
|
200
|
+
Mounted node metadata is mirrored onto the canonical Three.js wrapper object:
|
|
201
|
+
|
|
202
|
+
* `GameObject.id` -> `object.userData.prefabNodeId`
|
|
203
|
+
* `GameObject.name` -> `object.name` and `object.userData.prefabNodeName`
|
|
204
|
+
* `Data.properties.data` -> merged into `object.userData`
|
|
205
|
+
|
|
206
|
+
That gives you a stable authored id for traversal-based integrations, while still making Three.js name lookup convenient:
|
|
198
207
|
|
|
199
208
|
```tsx
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
209
|
+
const playerByName = editorRef.current?.root?.getObjectByName("Player");
|
|
210
|
+
const playerById = editorRef.current?.root?.getObjectByProperty("userData.prefabNodeId", "player");
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Treat names as a convenience surface, not the primary lookup key:
|
|
214
|
+
|
|
215
|
+
* names are not guaranteed unique
|
|
216
|
+
* `getObject(id)` is still the stable authored-node lookup
|
|
217
|
+
* traversal metadata is applied to the prefab node transform object, not necessarily the inner mesh or model child
|
|
218
|
+
|
|
219
|
+
You can author extra `userData` from the editor with a `Data` component:
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
{
|
|
223
|
+
"data": {
|
|
224
|
+
"faction": "enemy",
|
|
225
|
+
"health": 100,
|
|
226
|
+
"loot": { "table": "crate" }
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
For batched authored updates, write through the store once:
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
editorRef.current?.store.getState().updateNodes([
|
|
235
|
+
{
|
|
236
|
+
id: "orb1",
|
|
237
|
+
update: (node) => ({
|
|
238
|
+
...node,
|
|
239
|
+
components: {
|
|
240
|
+
...node.components,
|
|
241
|
+
transform: {
|
|
242
|
+
...node.components?.transform,
|
|
243
|
+
type: "Transform",
|
|
244
|
+
properties: {
|
|
245
|
+
...node.components?.transform?.properties,
|
|
246
|
+
position: [1, 0, 0],
|
|
247
|
+
},
|
|
248
|
+
},
|
|
211
249
|
},
|
|
212
|
-
}
|
|
250
|
+
}),
|
|
213
251
|
},
|
|
214
|
-
|
|
252
|
+
{
|
|
253
|
+
id: "orb2",
|
|
254
|
+
update: (node) => ({
|
|
255
|
+
...node,
|
|
256
|
+
components: {
|
|
257
|
+
...node.components,
|
|
258
|
+
transform: {
|
|
259
|
+
...node.components?.transform,
|
|
260
|
+
type: "Transform",
|
|
261
|
+
properties: {
|
|
262
|
+
...node.components?.transform?.properties,
|
|
263
|
+
position: [-1, 0, 0],
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
}),
|
|
268
|
+
},
|
|
269
|
+
]);
|
|
215
270
|
```
|
|
216
271
|
|
|
217
|
-
`View`
|
|
272
|
+
Custom component `View`s should use normal React and R3F behavior, such as `useFrame`, refs, and native Three.js APIs.
|
|
218
273
|
|
|
219
274
|
## Useful Exports
|
|
220
275
|
|
|
@@ -224,9 +279,6 @@ const Rotator: Component = {
|
|
|
224
279
|
* `PrefabEditorMode`
|
|
225
280
|
* `Prefab`
|
|
226
281
|
* `GameObject`
|
|
227
|
-
* `Scene`
|
|
228
|
-
* `Entity`
|
|
229
|
-
* `EntityComponent`
|
|
230
282
|
* `registerComponent`
|
|
231
283
|
* `createPrefabStore`
|
|
232
284
|
* `usePrefabStoreApi`
|
package/dist/index.d.ts
CHANGED
|
@@ -10,19 +10,19 @@ export { useEditorContext } from './tools/prefabeditor/PrefabEditor';
|
|
|
10
10
|
export type { EditorContextType } from './tools/prefabeditor/PrefabEditor';
|
|
11
11
|
export { createPrefabStore, prefabStoreToPrefab, usePrefabStoreApi } from './tools/prefabeditor/prefabStore';
|
|
12
12
|
export type { PrefabStoreApi, PrefabStoreState } from './tools/prefabeditor/prefabStore';
|
|
13
|
-
export { createScene } from './tools/prefabeditor/scene';
|
|
14
13
|
export { denormalizePrefab } from './tools/prefabeditor/prefab';
|
|
14
|
+
export { gameEvents, getEntityIdFromRigidBody, useClickEvent, useGameEvent, usePhysicsEvent } from './tools/prefabeditor/GameEvents';
|
|
15
|
+
export type { ClickEventPayload, GameEventHandler, GameEventMap, PhysicsEventPayload } from './tools/prefabeditor/GameEvents';
|
|
15
16
|
export { registerComponent } from './tools/prefabeditor/components/ComponentRegistry';
|
|
16
17
|
export { FieldRenderer, FieldGroup, ListEditor, Label, Vector3Input, Vector3Field, NumberField, ColorInput, ColorField, StringInput, StringField, BooleanInput, BooleanField, SelectInput, SelectField, } from './tools/prefabeditor/components/Input';
|
|
17
18
|
export { loadJson, saveJson, exportGLB, exportGLBData, regenerateIds, computeParentWorldMatrix, } from './tools/prefabeditor/utils';
|
|
18
19
|
export type { ExportGLBOptions } from './tools/prefabeditor/utils';
|
|
19
20
|
export { createModelNode, createImageNode, } from './tools/prefabeditor/prefab';
|
|
20
21
|
export type { PrefabEditorProps, PrefabEditorRef, } from './tools/prefabeditor/PrefabEditor';
|
|
21
|
-
export type {
|
|
22
|
+
export type { Entity, EntityComponent, EntityData, EntityUpdate, PropertyPath, Scene, SceneUpdates, SpawnOptions, } from './tools/prefabeditor/scene';
|
|
22
23
|
export type { PrefabRootProps } from './tools/prefabeditor/PrefabRoot';
|
|
23
|
-
export type { AssetRuntime, EntityRuntime, LiveObjectRef, LiveRigidBodyRef } from './tools/prefabeditor/
|
|
24
|
-
export { useAssetRuntime, useEntityRuntime, useEntityObjectRef, useEntityRigidBodyRef } from './tools/prefabeditor/
|
|
25
|
-
export type { ComponentInstance, ComponentRuntimeContext } from './tools/prefabeditor/runtime';
|
|
24
|
+
export type { AssetRuntime, EntityRuntime, LiveObjectRef, LiveRigidBodyRef } from './tools/prefabeditor/assetRuntime';
|
|
25
|
+
export { useAssetRuntime, useEntityRuntime, useEntityObjectRef, useEntityRigidBodyRef } from './tools/prefabeditor/assetRuntime';
|
|
26
26
|
export type { Component, ComponentViewProps } from './tools/prefabeditor/components/ComponentRegistry';
|
|
27
27
|
export type { FieldDefinition, FieldType } from './tools/prefabeditor/components/Input';
|
|
28
28
|
export { MaterialOverridesProvider, useMaterialOverrides } from './tools/prefabeditor/components/MaterialComponent';
|
|
@@ -30,8 +30,6 @@ export type { MaterialOverrides } from './tools/prefabeditor/components/Material
|
|
|
30
30
|
export type { Prefab, GameObject, ComponentData } from './tools/prefabeditor/types';
|
|
31
31
|
export { findComponent, findComponentEntry, hasComponent } from './tools/prefabeditor/types';
|
|
32
32
|
export { float, positionLocal, sin, time, uniform, vec3, } from 'three/tsl';
|
|
33
|
-
export { gameEvents, useGameEvent, usePhysicsEvent, useClickEvent, getEntityIdFromRigidBody } from './tools/prefabeditor/GameEvents';
|
|
34
|
-
export type { GameEventType, GameEventMap, GameEventPayload, PhysicsEventType, InteractionEventType, PhysicsEventPayload, ClickEventPayload } from './tools/prefabeditor/GameEvents';
|
|
35
33
|
export { loadFiles } from './tools/dragdrop/DragDropLoader';
|
|
36
34
|
export type { AssetLoadOptions } from './tools/dragdrop/DragDropLoader';
|
|
37
35
|
export { loadModel, loadSound, loadTexture } from './tools/dragdrop/modelLoader';
|
package/dist/index.js
CHANGED
|
@@ -10,8 +10,8 @@ export { default as PrefabRoot } from './tools/prefabeditor/PrefabRoot';
|
|
|
10
10
|
export { useEditorContext } from './tools/prefabeditor/PrefabEditor';
|
|
11
11
|
// Prefab Editor - Store & Scene API
|
|
12
12
|
export { createPrefabStore, prefabStoreToPrefab, usePrefabStoreApi } from './tools/prefabeditor/prefabStore';
|
|
13
|
-
export { createScene } from './tools/prefabeditor/scene';
|
|
14
13
|
export { denormalizePrefab } from './tools/prefabeditor/prefab';
|
|
14
|
+
export { gameEvents, getEntityIdFromRigidBody, useClickEvent, useGameEvent, usePhysicsEvent } from './tools/prefabeditor/GameEvents';
|
|
15
15
|
// Prefab Editor - Component Registry
|
|
16
16
|
export { registerComponent } from './tools/prefabeditor/components/ComponentRegistry';
|
|
17
17
|
// Prefab Editor - Input Components
|
|
@@ -19,12 +19,10 @@ export { FieldRenderer, FieldGroup, ListEditor, Label, Vector3Input, Vector3Fiel
|
|
|
19
19
|
// Prefab Editor - Utils
|
|
20
20
|
export { loadJson, saveJson, exportGLB, exportGLBData, regenerateIds, computeParentWorldMatrix, } from './tools/prefabeditor/utils';
|
|
21
21
|
export { createModelNode, createImageNode, } from './tools/prefabeditor/prefab';
|
|
22
|
-
export { useAssetRuntime, useEntityRuntime, useEntityObjectRef, useEntityRigidBodyRef } from './tools/prefabeditor/
|
|
22
|
+
export { useAssetRuntime, useEntityRuntime, useEntityObjectRef, useEntityRigidBodyRef } from './tools/prefabeditor/assetRuntime';
|
|
23
23
|
export { MaterialOverridesProvider, useMaterialOverrides } from './tools/prefabeditor/components/MaterialComponent';
|
|
24
24
|
export { findComponent, findComponentEntry, hasComponent } from './tools/prefabeditor/types';
|
|
25
25
|
export { float, positionLocal, sin, time, uniform, vec3, } from 'three/tsl';
|
|
26
|
-
// Game Events (physics + custom events)
|
|
27
|
-
export { gameEvents, useGameEvent, usePhysicsEvent, useClickEvent, getEntityIdFromRigidBody } from './tools/prefabeditor/GameEvents';
|
|
28
26
|
// Asset Loading
|
|
29
27
|
export { loadFiles } from './tools/dragdrop/DragDropLoader';
|
|
30
28
|
export { loadModel, loadSound, loadTexture } from './tools/dragdrop/modelLoader';
|
|
@@ -1,128 +1,47 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
export type GameEventHandler<TPayload = unknown> = (payload: TPayload) => void;
|
|
2
|
+
export type PhysicsEventPayload = {
|
|
3
|
+
sourceEntityId?: string;
|
|
4
|
+
sourceNodeId?: string;
|
|
5
|
+
sourceObject?: unknown;
|
|
6
|
+
sourceRigidBody?: unknown;
|
|
7
|
+
targetEntityId?: string | null;
|
|
8
|
+
targetNodeId?: string | null;
|
|
9
|
+
targetObject?: unknown;
|
|
10
|
+
targetRigidBody?: unknown;
|
|
11
|
+
rapierEvent?: unknown;
|
|
12
|
+
};
|
|
13
|
+
export type ClickEventPayload = {
|
|
14
|
+
sourceEntityId?: string;
|
|
15
|
+
sourceNodeId?: string;
|
|
13
16
|
instanceEntityId?: string;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
* interface GameEventMap {
|
|
26
|
-
* 'player:death': { playerId: string; cause: string };
|
|
27
|
-
* 'score:change': { delta: number; total: number };
|
|
28
|
-
* }
|
|
29
|
-
* }
|
|
30
|
-
*/
|
|
17
|
+
nodeId?: string;
|
|
18
|
+
node?: unknown;
|
|
19
|
+
object?: unknown;
|
|
20
|
+
point?: [number, number, number];
|
|
21
|
+
button?: number;
|
|
22
|
+
altKey?: boolean;
|
|
23
|
+
ctrlKey?: boolean;
|
|
24
|
+
metaKey?: boolean;
|
|
25
|
+
shiftKey?: boolean;
|
|
26
|
+
r3fEvent?: unknown;
|
|
27
|
+
};
|
|
31
28
|
export interface GameEventMap {
|
|
32
29
|
'sensor:enter': PhysicsEventPayload;
|
|
33
30
|
'sensor:exit': PhysicsEventPayload;
|
|
34
31
|
'collision:enter': PhysicsEventPayload;
|
|
35
32
|
'collision:exit': PhysicsEventPayload;
|
|
36
|
-
|
|
33
|
+
click: ClickEventPayload;
|
|
34
|
+
[eventType: string]: unknown;
|
|
37
35
|
}
|
|
38
|
-
/** All registered event types */
|
|
39
|
-
export type GameEventType = keyof GameEventMap | (string & {});
|
|
40
|
-
/** Get payload type for an event, or fallback to generic */
|
|
41
|
-
export type GameEventPayload<T extends string> = T extends keyof GameEventMap ? GameEventMap[T] : Record<string, unknown>;
|
|
42
|
-
type EventHandler<T = unknown> = (payload: T) => void;
|
|
43
|
-
type UnknownEventPayload = Record<string, unknown>;
|
|
44
|
-
declare function emitGameEvent<TType extends keyof GameEventMap>(type: TType, payload: GameEventMap[TType]): void;
|
|
45
|
-
declare function emitGameEvent(type: string, payload: UnknownEventPayload): void;
|
|
46
|
-
declare function onGameEvent<TType extends keyof GameEventMap>(type: TType, handler: EventHandler<GameEventMap[TType]>): () => void;
|
|
47
|
-
declare function onGameEvent(type: string, handler: EventHandler<UnknownEventPayload>): () => void;
|
|
48
|
-
declare function offGameEvent<TType extends keyof GameEventMap>(type: TType, handler: EventHandler<GameEventMap[TType]>): void;
|
|
49
|
-
declare function offGameEvent(type: string, handler: EventHandler<UnknownEventPayload>): void;
|
|
50
|
-
/**
|
|
51
|
-
* Game event system for all game interactions.
|
|
52
|
-
*
|
|
53
|
-
* Built-in events:
|
|
54
|
-
* - sensor:enter - Something entered a sensor collider
|
|
55
|
-
* - sensor:exit - Something exited a sensor collider
|
|
56
|
-
* - collision:enter - A collision started
|
|
57
|
-
* - collision:exit - A collision ended
|
|
58
|
-
* - click - A prefab entity with a Click component was clicked in play mode
|
|
59
|
-
*
|
|
60
|
-
* Custom events:
|
|
61
|
-
* - Emit any event type with any payload
|
|
62
|
-
* - Extend GameEventMap interface for type safety
|
|
63
|
-
*
|
|
64
|
-
* @example
|
|
65
|
-
* // Physics events (typed)
|
|
66
|
-
* gameEvents.emit('sensor:enter', { sourceEntityId: 'zone', targetEntityId: 'player', targetRigidBody: rb });
|
|
67
|
-
*
|
|
68
|
-
* // Custom events
|
|
69
|
-
* gameEvents.emit('player:death', { playerId: 'p1', cause: 'lava' });
|
|
70
|
-
* gameEvents.emit('level:complete', { levelId: 3, time: 45.2 });
|
|
71
|
-
*/
|
|
72
36
|
export declare const gameEvents: {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
*/
|
|
76
|
-
emit: typeof emitGameEvent;
|
|
77
|
-
/**
|
|
78
|
-
* Subscribe to an event type
|
|
79
|
-
* @returns Unsubscribe function
|
|
80
|
-
*/
|
|
81
|
-
on: typeof onGameEvent;
|
|
82
|
-
/**
|
|
83
|
-
* Unsubscribe from an event type
|
|
84
|
-
*/
|
|
85
|
-
off: typeof offGameEvent;
|
|
86
|
-
/**
|
|
87
|
-
* Remove all subscribers (useful for cleanup/reset)
|
|
88
|
-
*/
|
|
37
|
+
emit<TType extends string>(type: TType, payload: TType extends keyof GameEventMap ? GameEventMap[TType] : unknown): void;
|
|
38
|
+
on<TType extends string>(type: TType, handler: GameEventHandler<TType extends keyof GameEventMap ? GameEventMap[TType] : unknown>): () => void;
|
|
89
39
|
clear(): void;
|
|
90
|
-
/**
|
|
91
|
-
* Check if an event type has any subscribers
|
|
92
|
-
*/
|
|
93
40
|
hasListeners(type: string): boolean;
|
|
94
41
|
};
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
* useGameEvent('sensor:enter', (payload) => {
|
|
102
|
-
* if (payload.sourceEntityId === 'coin') collectCoin();
|
|
103
|
-
* }, []);
|
|
104
|
-
*
|
|
105
|
-
* // Custom event
|
|
106
|
-
* useGameEvent('player:death', (payload) => {
|
|
107
|
-
* const cause = typeof payload.cause === 'string' ? payload.cause : 'unknown';
|
|
108
|
-
* showGameOver(cause);
|
|
109
|
-
* }, []);
|
|
110
|
-
*/
|
|
111
|
-
export declare function useGameEvent<TType extends keyof GameEventMap>(type: TType, handler: EventHandler<GameEventMap[TType]>, deps?: unknown[]): void;
|
|
112
|
-
export declare function useGameEvent(type: string, handler: EventHandler<UnknownEventPayload>, deps?: unknown[]): void;
|
|
113
|
-
/**
|
|
114
|
-
* React hook to subscribe to any physics event payload.
|
|
115
|
-
* Use this when the event name is dynamic but the payload comes from PhysicsComponent.
|
|
116
|
-
*/
|
|
117
|
-
export declare function usePhysicsEvent(type: string, handler: EventHandler<PhysicsEventPayload>, deps?: unknown[]): void;
|
|
118
|
-
/**
|
|
119
|
-
* React hook to subscribe to click event payloads.
|
|
120
|
-
* Use this when the event name is dynamic but the payload comes from ClickComponent.
|
|
121
|
-
*/
|
|
122
|
-
export declare function useClickEvent(type: string, handler: EventHandler<ClickEventPayload>, deps?: unknown[]): void;
|
|
123
|
-
/**
|
|
124
|
-
* Helper to extract entity ID from Rapier collision data.
|
|
125
|
-
* Entity IDs are stored in RigidBody userData.
|
|
126
|
-
*/
|
|
127
|
-
export declare function getEntityIdFromRigidBody(rigidBody: RapierRigidBody | null | undefined): string | null;
|
|
128
|
-
export {};
|
|
42
|
+
export declare function useGameEvent<TType extends string>(type: TType, handler: GameEventHandler<TType extends keyof GameEventMap ? GameEventMap[TType] : unknown>, deps?: React.DependencyList): void;
|
|
43
|
+
export declare function usePhysicsEvent<TType extends string>(type: TType, handler: GameEventHandler<TType extends keyof GameEventMap ? GameEventMap[TType] : unknown>, deps?: React.DependencyList): void;
|
|
44
|
+
export declare function useClickEvent<TType extends string>(type: TType, handler: GameEventHandler<TType extends keyof GameEventMap ? GameEventMap[TType] : unknown>, deps?: React.DependencyList): void;
|
|
45
|
+
export declare function getEntityIdFromRigidBody(rigidBody: {
|
|
46
|
+
userData?: unknown;
|
|
47
|
+
} | null | undefined): string | null;
|
|
@@ -1,118 +1,66 @@
|
|
|
1
|
-
import {
|
|
2
|
-
// Internal subscriber storage
|
|
1
|
+
import { useCallback, useEffect } from 'react';
|
|
3
2
|
const subscribers = new Map();
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
export const gameEvents = {
|
|
4
|
+
emit(type, payload) {
|
|
5
|
+
const trimmedType = type.trim();
|
|
6
|
+
if (!trimmedType)
|
|
7
|
+
return;
|
|
8
|
+
const handlers = subscribers.get(trimmedType);
|
|
9
|
+
if (!handlers)
|
|
10
|
+
return;
|
|
7
11
|
handlers.forEach(handler => {
|
|
8
12
|
try {
|
|
9
13
|
handler(payload);
|
|
10
14
|
}
|
|
11
|
-
catch (
|
|
12
|
-
console.error(`Error in gameEvents handler for ${
|
|
15
|
+
catch (error) {
|
|
16
|
+
console.error(`Error in gameEvents handler for ${trimmedType}:`, error);
|
|
13
17
|
}
|
|
14
18
|
});
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
},
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Game event system for all game interactions.
|
|
40
|
-
*
|
|
41
|
-
* Built-in events:
|
|
42
|
-
* - sensor:enter - Something entered a sensor collider
|
|
43
|
-
* - sensor:exit - Something exited a sensor collider
|
|
44
|
-
* - collision:enter - A collision started
|
|
45
|
-
* - collision:exit - A collision ended
|
|
46
|
-
* - click - A prefab entity with a Click component was clicked in play mode
|
|
47
|
-
*
|
|
48
|
-
* Custom events:
|
|
49
|
-
* - Emit any event type with any payload
|
|
50
|
-
* - Extend GameEventMap interface for type safety
|
|
51
|
-
*
|
|
52
|
-
* @example
|
|
53
|
-
* // Physics events (typed)
|
|
54
|
-
* gameEvents.emit('sensor:enter', { sourceEntityId: 'zone', targetEntityId: 'player', targetRigidBody: rb });
|
|
55
|
-
*
|
|
56
|
-
* // Custom events
|
|
57
|
-
* gameEvents.emit('player:death', { playerId: 'p1', cause: 'lava' });
|
|
58
|
-
* gameEvents.emit('level:complete', { levelId: 3, time: 45.2 });
|
|
59
|
-
*/
|
|
60
|
-
export const gameEvents = {
|
|
61
|
-
/**
|
|
62
|
-
* Emit an event to all subscribers
|
|
63
|
-
*/
|
|
64
|
-
emit: emitGameEvent,
|
|
65
|
-
/**
|
|
66
|
-
* Subscribe to an event type
|
|
67
|
-
* @returns Unsubscribe function
|
|
68
|
-
*/
|
|
69
|
-
on: onGameEvent,
|
|
70
|
-
/**
|
|
71
|
-
* Unsubscribe from an event type
|
|
72
|
-
*/
|
|
73
|
-
off: offGameEvent,
|
|
74
|
-
/**
|
|
75
|
-
* Remove all subscribers (useful for cleanup/reset)
|
|
76
|
-
*/
|
|
19
|
+
},
|
|
20
|
+
on(type, handler) {
|
|
21
|
+
const trimmedType = type.trim();
|
|
22
|
+
if (!trimmedType) {
|
|
23
|
+
return () => { };
|
|
24
|
+
}
|
|
25
|
+
let handlers = subscribers.get(trimmedType);
|
|
26
|
+
if (!handlers) {
|
|
27
|
+
handlers = new Set();
|
|
28
|
+
subscribers.set(trimmedType, handlers);
|
|
29
|
+
}
|
|
30
|
+
handlers.add(handler);
|
|
31
|
+
return () => {
|
|
32
|
+
const currentHandlers = subscribers.get(trimmedType);
|
|
33
|
+
if (!currentHandlers)
|
|
34
|
+
return;
|
|
35
|
+
currentHandlers.delete(handler);
|
|
36
|
+
if (currentHandlers.size === 0) {
|
|
37
|
+
subscribers.delete(trimmedType);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
},
|
|
77
41
|
clear() {
|
|
78
42
|
subscribers.clear();
|
|
79
43
|
},
|
|
80
|
-
/**
|
|
81
|
-
* Check if an event type has any subscribers
|
|
82
|
-
*/
|
|
83
44
|
hasListeners(type) {
|
|
84
45
|
var _a, _b;
|
|
85
|
-
return ((_b = (_a = subscribers.get(type)) === null || _a === void 0 ? void 0 : _a.size) !== null && _b !== void 0 ? _b : 0) > 0;
|
|
86
|
-
}
|
|
46
|
+
return ((_b = (_a = subscribers.get(type.trim())) === null || _a === void 0 ? void 0 : _a.size) !== null && _b !== void 0 ? _b : 0) > 0;
|
|
47
|
+
},
|
|
87
48
|
};
|
|
88
49
|
export function useGameEvent(type, handler, deps = []) {
|
|
89
|
-
|
|
50
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
51
|
+
const stableHandler = useCallback(handler, deps);
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
return gameEvents.on(type, stableHandler);
|
|
54
|
+
}, [type, stableHandler]);
|
|
90
55
|
}
|
|
91
|
-
/**
|
|
92
|
-
* React hook to subscribe to any physics event payload.
|
|
93
|
-
* Use this when the event name is dynamic but the payload comes from PhysicsComponent.
|
|
94
|
-
*/
|
|
95
56
|
export function usePhysicsEvent(type, handler, deps = []) {
|
|
96
|
-
|
|
57
|
+
useGameEvent(type, handler, deps);
|
|
97
58
|
}
|
|
98
|
-
/**
|
|
99
|
-
* React hook to subscribe to click event payloads.
|
|
100
|
-
* Use this when the event name is dynamic but the payload comes from ClickComponent.
|
|
101
|
-
*/
|
|
102
59
|
export function useClickEvent(type, handler, deps = []) {
|
|
103
|
-
|
|
60
|
+
useGameEvent(type, handler, deps);
|
|
104
61
|
}
|
|
105
|
-
// ============================================================================
|
|
106
|
-
// Helpers
|
|
107
|
-
// ============================================================================
|
|
108
|
-
/**
|
|
109
|
-
* Helper to extract entity ID from Rapier collision data.
|
|
110
|
-
* Entity IDs are stored in RigidBody userData.
|
|
111
|
-
*/
|
|
112
62
|
export function getEntityIdFromRigidBody(rigidBody) {
|
|
113
63
|
var _a;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const userData = rigidBody.userData;
|
|
117
|
-
return (_a = userData === null || userData === void 0 ? void 0 : userData.entityId) !== null && _a !== void 0 ? _a : null;
|
|
64
|
+
const entityId = (_a = rigidBody === null || rigidBody === void 0 ? void 0 : rigidBody.userData) === null || _a === void 0 ? void 0 : _a.entityId;
|
|
65
|
+
return typeof entityId === 'string' ? entityId : null;
|
|
118
66
|
}
|
|
@@ -12,8 +12,6 @@ export declare function getRepeatAxesFromModelProperties(properties: Record<stri
|
|
|
12
12
|
export type InstanceData = {
|
|
13
13
|
id: string;
|
|
14
14
|
sourceId: string;
|
|
15
|
-
clickable?: boolean;
|
|
16
|
-
clickEventName?: string;
|
|
17
15
|
locked?: boolean;
|
|
18
16
|
position: [number, number, number];
|
|
19
17
|
rotation: [number, number, number];
|
|
@@ -35,8 +33,6 @@ export declare function useInstanceCheck(id: string): boolean;
|
|
|
35
33
|
export declare const GameInstance: React.ForwardRefExoticComponent<{
|
|
36
34
|
id: string;
|
|
37
35
|
sourceId?: string;
|
|
38
|
-
clickable?: boolean;
|
|
39
|
-
clickEventName?: string;
|
|
40
36
|
modelUrl: string;
|
|
41
37
|
locked?: boolean;
|
|
42
38
|
position: [number, number, number];
|