react-three-game 0.0.86 → 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/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/tools/prefabeditor/GameEvents.d.ts +47 -0
- package/dist/tools/prefabeditor/GameEvents.js +66 -0
- package/dist/tools/prefabeditor/PrefabRoot.js +7 -8
- package/dist/tools/prefabeditor/components/PhysicsComponent.js +9 -12
- package/dist/tools/prefabeditor/components/SoundComponent.js +21 -14
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -11,13 +11,15 @@ 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
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';
|
|
14
16
|
export { registerComponent } from './tools/prefabeditor/components/ComponentRegistry';
|
|
15
17
|
export { FieldRenderer, FieldGroup, ListEditor, Label, Vector3Input, Vector3Field, NumberField, ColorInput, ColorField, StringInput, StringField, BooleanInput, BooleanField, SelectInput, SelectField, } from './tools/prefabeditor/components/Input';
|
|
16
18
|
export { loadJson, saveJson, exportGLB, exportGLBData, regenerateIds, computeParentWorldMatrix, } from './tools/prefabeditor/utils';
|
|
17
19
|
export type { ExportGLBOptions } from './tools/prefabeditor/utils';
|
|
18
20
|
export { createModelNode, createImageNode, } from './tools/prefabeditor/prefab';
|
|
19
21
|
export type { PrefabEditorProps, PrefabEditorRef, } from './tools/prefabeditor/PrefabEditor';
|
|
20
|
-
export type { SpawnOptions, } from './tools/prefabeditor/scene';
|
|
22
|
+
export type { Entity, EntityComponent, EntityData, EntityUpdate, PropertyPath, Scene, SceneUpdates, SpawnOptions, } from './tools/prefabeditor/scene';
|
|
21
23
|
export type { PrefabRootProps } from './tools/prefabeditor/PrefabRoot';
|
|
22
24
|
export type { AssetRuntime, EntityRuntime, LiveObjectRef, LiveRigidBodyRef } from './tools/prefabeditor/assetRuntime';
|
|
23
25
|
export { useAssetRuntime, useEntityRuntime, useEntityObjectRef, useEntityRigidBodyRef } from './tools/prefabeditor/assetRuntime';
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ export { useEditorContext } from './tools/prefabeditor/PrefabEditor';
|
|
|
11
11
|
// Prefab Editor - Store & Scene API
|
|
12
12
|
export { createPrefabStore, prefabStoreToPrefab, usePrefabStoreApi } from './tools/prefabeditor/prefabStore';
|
|
13
13
|
export { denormalizePrefab } from './tools/prefabeditor/prefab';
|
|
14
|
+
export { gameEvents, getEntityIdFromRigidBody, useClickEvent, useGameEvent, usePhysicsEvent } from './tools/prefabeditor/GameEvents';
|
|
14
15
|
// Prefab Editor - Component Registry
|
|
15
16
|
export { registerComponent } from './tools/prefabeditor/components/ComponentRegistry';
|
|
16
17
|
// Prefab Editor - Input Components
|
|
@@ -0,0 +1,47 @@
|
|
|
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;
|
|
16
|
+
instanceEntityId?: string;
|
|
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
|
+
};
|
|
28
|
+
export interface GameEventMap {
|
|
29
|
+
'sensor:enter': PhysicsEventPayload;
|
|
30
|
+
'sensor:exit': PhysicsEventPayload;
|
|
31
|
+
'collision:enter': PhysicsEventPayload;
|
|
32
|
+
'collision:exit': PhysicsEventPayload;
|
|
33
|
+
click: ClickEventPayload;
|
|
34
|
+
[eventType: string]: unknown;
|
|
35
|
+
}
|
|
36
|
+
export declare const gameEvents: {
|
|
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;
|
|
39
|
+
clear(): void;
|
|
40
|
+
hasListeners(type: string): boolean;
|
|
41
|
+
};
|
|
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;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { useCallback, useEffect } from 'react';
|
|
2
|
+
const subscribers = new Map();
|
|
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;
|
|
11
|
+
handlers.forEach(handler => {
|
|
12
|
+
try {
|
|
13
|
+
handler(payload);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
console.error(`Error in gameEvents handler for ${trimmedType}:`, error);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
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
|
+
},
|
|
41
|
+
clear() {
|
|
42
|
+
subscribers.clear();
|
|
43
|
+
},
|
|
44
|
+
hasListeners(type) {
|
|
45
|
+
var _a, _b;
|
|
46
|
+
return ((_b = (_a = subscribers.get(type.trim())) === null || _a === void 0 ? void 0 : _a.size) !== null && _b !== void 0 ? _b : 0) > 0;
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
export function useGameEvent(type, handler, deps = []) {
|
|
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]);
|
|
55
|
+
}
|
|
56
|
+
export function usePhysicsEvent(type, handler, deps = []) {
|
|
57
|
+
useGameEvent(type, handler, deps);
|
|
58
|
+
}
|
|
59
|
+
export function useClickEvent(type, handler, deps = []) {
|
|
60
|
+
useGameEvent(type, handler, deps);
|
|
61
|
+
}
|
|
62
|
+
export function getEntityIdFromRigidBody(rigidBody) {
|
|
63
|
+
var _a;
|
|
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;
|
|
66
|
+
}
|
|
@@ -24,6 +24,7 @@ import { composeTransform, decompose } from "./utils";
|
|
|
24
24
|
import { isPhysicsProps } from "./components/PhysicsComponent";
|
|
25
25
|
import { createPrefabStore, PrefabStoreProvider, useOptionalPrefabStoreApi, usePrefabChildIds, usePrefabNode, usePrefabRootId } from "./prefabStore";
|
|
26
26
|
import { AssetRuntimeContext, EntityRuntimeScope } from "./assetRuntime";
|
|
27
|
+
import { gameEvents } from "./GameEvents";
|
|
27
28
|
import { sound as soundManager } from "../../helpers/SoundManager";
|
|
28
29
|
builtinComponents.forEach(registerComponent);
|
|
29
30
|
const IDENTITY = new Matrix4();
|
|
@@ -44,12 +45,6 @@ function isNodeReady(node, loadedModels) {
|
|
|
44
45
|
return true;
|
|
45
46
|
return Boolean(loadedModels[model.properties.filename]);
|
|
46
47
|
}
|
|
47
|
-
function emitNativeEvent(type, detail) {
|
|
48
|
-
const trimmedType = type === null || type === void 0 ? void 0 : type.trim();
|
|
49
|
-
if (!trimmedType || typeof window === 'undefined')
|
|
50
|
-
return;
|
|
51
|
-
window.dispatchEvent(new CustomEvent(trimmedType, { detail }));
|
|
52
|
-
}
|
|
53
48
|
function getNodeClickEventName(node) {
|
|
54
49
|
var _a;
|
|
55
50
|
const clickComponents = [
|
|
@@ -214,12 +209,16 @@ function StoreRootNode(props) {
|
|
|
214
209
|
}
|
|
215
210
|
function emitNodePointerEvent(eventName, event, nodeId, node, fallbackObject) {
|
|
216
211
|
var _a;
|
|
217
|
-
|
|
212
|
+
const trimmedEventName = eventName === null || eventName === void 0 ? void 0 : eventName.trim();
|
|
213
|
+
if (!trimmedEventName)
|
|
218
214
|
return;
|
|
219
|
-
|
|
215
|
+
gameEvents.emit(trimmedEventName, {
|
|
216
|
+
sourceEntityId: nodeId,
|
|
217
|
+
sourceNodeId: nodeId,
|
|
220
218
|
nodeId,
|
|
221
219
|
node,
|
|
222
220
|
object: (_a = event.object) !== null && _a !== void 0 ? _a : fallbackObject,
|
|
221
|
+
point: [event.point.x, event.point.y, event.point.z],
|
|
223
222
|
button: event.button,
|
|
224
223
|
altKey: event.nativeEvent.altKey,
|
|
225
224
|
ctrlKey: event.nativeEvent.ctrlKey,
|
|
@@ -13,6 +13,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
13
13
|
import { CapsuleCollider, RigidBody, useRapier } from "@react-three/rapier";
|
|
14
14
|
import { useCallback, useEffect, useRef } from 'react';
|
|
15
15
|
import { useAssetRuntime, useEntityRuntime } from "../assetRuntime";
|
|
16
|
+
import { gameEvents, getEntityIdFromRigidBody } from "../GameEvents";
|
|
16
17
|
import { usePrefabNode } from "../prefabStore";
|
|
17
18
|
import { BooleanField, FieldGroup, NumberField, SelectField, StringField, Vector3Field } from "./Input";
|
|
18
19
|
import { getNodeUserData } from "../types";
|
|
@@ -80,16 +81,6 @@ function PhysicsComponentEditor({ component, onUpdate }) {
|
|
|
80
81
|
{ value: 'all', label: 'All (includes kinematic & fixed)' },
|
|
81
82
|
] })] }));
|
|
82
83
|
}
|
|
83
|
-
function emitNativeEvent(type, detail) {
|
|
84
|
-
const trimmedType = type === null || type === void 0 ? void 0 : type.trim();
|
|
85
|
-
if (!trimmedType || typeof window === 'undefined')
|
|
86
|
-
return;
|
|
87
|
-
window.dispatchEvent(new CustomEvent(trimmedType, { detail }));
|
|
88
|
-
}
|
|
89
|
-
function getEntityIdFromRigidBody(rigidBody) {
|
|
90
|
-
const userData = rigidBody === null || rigidBody === void 0 ? void 0 : rigidBody.userData;
|
|
91
|
-
return typeof (userData === null || userData === void 0 ? void 0 : userData.entityId) === 'string' ? userData.entityId : null;
|
|
92
|
-
}
|
|
93
84
|
function PhysicsComponentView({ properties, children, position, rotation, scale }) {
|
|
94
85
|
var _a, _b, _c;
|
|
95
86
|
const { registerRigidBodyRef } = useAssetRuntime();
|
|
@@ -157,11 +148,17 @@ function PhysicsComponentView({ properties, children, position, rotation, scale
|
|
|
157
148
|
var _a, _b, _c;
|
|
158
149
|
if (!nodeId)
|
|
159
150
|
return;
|
|
160
|
-
|
|
151
|
+
const trimmedEventType = eventType === null || eventType === void 0 ? void 0 : eventType.trim();
|
|
152
|
+
if (!trimmedEventType)
|
|
153
|
+
return;
|
|
154
|
+
const targetEntityId = getEntityIdFromRigidBody(payload.other.rigidBody);
|
|
155
|
+
gameEvents.emit(trimmedEventType, {
|
|
156
|
+
sourceEntityId: nodeId,
|
|
161
157
|
sourceNodeId: nodeId,
|
|
162
158
|
sourceObject: getObject(),
|
|
163
159
|
sourceRigidBody: rigidBodyRef.current,
|
|
164
|
-
|
|
160
|
+
targetEntityId,
|
|
161
|
+
targetNodeId: targetEntityId,
|
|
165
162
|
targetObject: (_b = (_a = payload.other.rigidBodyObject) !== null && _a !== void 0 ? _a : payload.other.colliderObject) !== null && _b !== void 0 ? _b : null,
|
|
166
163
|
targetRigidBody: (_c = payload.other.rigidBody) !== null && _c !== void 0 ? _c : null,
|
|
167
164
|
rapierEvent: payload,
|
|
@@ -4,6 +4,7 @@ import { useThree } from '@react-three/fiber';
|
|
|
4
4
|
import { SoundPicker } from '../../assetviewer/page';
|
|
5
5
|
import { sound as soundManager } from '../../../helpers/SoundManager';
|
|
6
6
|
import { useAssetRuntime, useEntityRuntime } from '../assetRuntime';
|
|
7
|
+
import { gameEvents } from '../GameEvents';
|
|
7
8
|
import { BooleanField, FieldGroup, FieldRenderer, ListEditor, NumberField, SelectField, StringField } from './Input';
|
|
8
9
|
import { colors } from '../styles';
|
|
9
10
|
import { AudioListener } from 'three';
|
|
@@ -62,6 +63,21 @@ function pickClip(paths, mode, sequenceIndexRef) {
|
|
|
62
63
|
}
|
|
63
64
|
return paths[Math.floor(Math.random() * paths.length)];
|
|
64
65
|
}
|
|
66
|
+
function payloadMatchesNode(nodeId, payload) {
|
|
67
|
+
if (!nodeId || !payload || typeof payload !== 'object') {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
const eventPayload = payload;
|
|
71
|
+
const relatedNodeIds = [
|
|
72
|
+
eventPayload.nodeId,
|
|
73
|
+
eventPayload.sourceEntityId,
|
|
74
|
+
eventPayload.sourceNodeId,
|
|
75
|
+
eventPayload.targetEntityId,
|
|
76
|
+
eventPayload.targetNodeId,
|
|
77
|
+
eventPayload.instanceEntityId,
|
|
78
|
+
].filter((value) => typeof value === 'string');
|
|
79
|
+
return relatedNodeIds.length > 0 ? relatedNodeIds.includes(nodeId) : true;
|
|
80
|
+
}
|
|
65
81
|
function SoundComponentEditor({ component, onUpdate, basePath = '' }) {
|
|
66
82
|
const clips = Array.isArray(component.properties.clips)
|
|
67
83
|
? component.properties.clips.map((clip) => typeof clip === 'string' ? clip : '')
|
|
@@ -156,17 +172,12 @@ function SoundComponentView({ properties, children }) {
|
|
|
156
172
|
audio.setDistanceModel(distanceModel);
|
|
157
173
|
}, [distanceModel, maxDistance, refDistance, rolloffFactor]);
|
|
158
174
|
useEffect(() => {
|
|
159
|
-
if (editMode || paths.length === 0 || !eventName
|
|
175
|
+
if (editMode || paths.length === 0 || !eventName) {
|
|
160
176
|
return;
|
|
161
177
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
if (nodeId && detail && typeof detail === 'object') {
|
|
166
|
-
const relatedNodeIds = [detail.nodeId, detail.sourceNodeId, detail.targetNodeId].filter((value) => typeof value === 'string');
|
|
167
|
-
if (relatedNodeIds.length > 0 && !relatedNodeIds.includes(nodeId)) {
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
178
|
+
return gameEvents.on(eventName, (payload) => {
|
|
179
|
+
if (!payloadMatchesNode(nodeId, payload)) {
|
|
180
|
+
return;
|
|
170
181
|
}
|
|
171
182
|
const clip = pickClip(paths, mode, sequenceIndexRef);
|
|
172
183
|
if (!clip)
|
|
@@ -200,11 +211,7 @@ function SoundComponentView({ properties, children }) {
|
|
|
200
211
|
audio.setPlaybackRate(pitch);
|
|
201
212
|
audio.setVolume(volume);
|
|
202
213
|
audio.play();
|
|
203
|
-
};
|
|
204
|
-
window.addEventListener(eventName, handleEvent);
|
|
205
|
-
return () => {
|
|
206
|
-
window.removeEventListener(eventName, handleEvent);
|
|
207
|
-
};
|
|
214
|
+
});
|
|
208
215
|
}, [editMode, eventName, getSound, mode, nodeId, paths, positional, properties]);
|
|
209
216
|
return (_jsxs(_Fragment, { children: [positional && listenerRef.current ? _jsx("positionalAudio", { ref: positionalAudioRef, args: [listenerRef.current] }) : null, children] }));
|
|
210
217
|
}
|