react-three-game 0.0.86 → 0.0.88
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 +62 -67
- 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,
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef } from 'react';
|
|
3
|
-
import { useThree } from '@react-three/fiber';
|
|
4
3
|
import { SoundPicker } from '../../assetviewer/page';
|
|
5
|
-
import { sound as soundManager } from '../../../helpers/SoundManager';
|
|
6
4
|
import { useAssetRuntime, useEntityRuntime } from '../assetRuntime';
|
|
5
|
+
import { gameEvents } from '../GameEvents';
|
|
7
6
|
import { BooleanField, FieldGroup, FieldRenderer, ListEditor, NumberField, SelectField, StringField } from './Input';
|
|
8
7
|
import { colors } from '../styles';
|
|
9
8
|
import { AudioListener } from 'three';
|
|
@@ -62,6 +61,32 @@ function pickClip(paths, mode, sequenceIndexRef) {
|
|
|
62
61
|
}
|
|
63
62
|
return paths[Math.floor(Math.random() * paths.length)];
|
|
64
63
|
}
|
|
64
|
+
function payloadMatchesNode(nodeId, payload) {
|
|
65
|
+
if (!nodeId || !payload || typeof payload !== 'object') {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
const eventPayload = payload;
|
|
69
|
+
const relatedNodeIds = [
|
|
70
|
+
eventPayload.nodeId,
|
|
71
|
+
eventPayload.sourceEntityId,
|
|
72
|
+
eventPayload.sourceNodeId,
|
|
73
|
+
eventPayload.targetEntityId,
|
|
74
|
+
eventPayload.targetNodeId,
|
|
75
|
+
eventPayload.instanceEntityId,
|
|
76
|
+
].filter((value) => typeof value === 'string');
|
|
77
|
+
return relatedNodeIds.length > 0 ? relatedNodeIds.includes(nodeId) : true;
|
|
78
|
+
}
|
|
79
|
+
function playBufferedAudio(audio, buffer, properties) {
|
|
80
|
+
void audio.listener.context.resume();
|
|
81
|
+
if (audio.isPlaying) {
|
|
82
|
+
audio.stop();
|
|
83
|
+
}
|
|
84
|
+
audio.setBuffer(buffer);
|
|
85
|
+
audio.setLoop(Boolean(properties.loop));
|
|
86
|
+
audio.setPlaybackRate(getPitchValue(properties));
|
|
87
|
+
audio.setVolume(getVolumeValue(properties));
|
|
88
|
+
audio.play();
|
|
89
|
+
}
|
|
65
90
|
function SoundComponentEditor({ component, onUpdate, basePath = '' }) {
|
|
66
91
|
const clips = Array.isArray(component.properties.clips)
|
|
67
92
|
? component.properties.clips.map((clip) => typeof clip === 'string' ? clip : '')
|
|
@@ -83,7 +108,7 @@ function SoundComponentEditor({ component, onUpdate, basePath = '' }) {
|
|
|
83
108
|
const removeClip = (index) => {
|
|
84
109
|
setClips(clips.filter((_, clipIndex) => clipIndex !== index));
|
|
85
110
|
};
|
|
86
|
-
return (_jsxs(FieldGroup, { children: [_jsx(StringField, { name: "eventName", label: "Listen Event", values: component.properties, onChange: onUpdate, placeholder: "player:footstep" }), _jsx(FieldRenderer, { fields: [
|
|
111
|
+
return (_jsxs(FieldGroup, { children: [_jsx(StringField, { name: "eventName", label: "Listen Event", values: component.properties, onChange: onUpdate, placeholder: "player:footstep" }), _jsx(BooleanField, { name: "autoplay", label: "Autoplay", values: component.properties, onChange: onUpdate, fallback: false }), _jsx(BooleanField, { name: "loop", label: "Loop", values: component.properties, onChange: onUpdate, fallback: false }), _jsx(FieldRenderer, { fields: [
|
|
87
112
|
{
|
|
88
113
|
name: 'clipMode',
|
|
89
114
|
label: 'Clip Mode',
|
|
@@ -117,8 +142,7 @@ function SoundComponentEditor({ component, onUpdate, basePath = '' }) {
|
|
|
117
142
|
function SoundComponentView({ properties, children }) {
|
|
118
143
|
const { getSound } = useAssetRuntime();
|
|
119
144
|
const { editMode, nodeId } = useEntityRuntime();
|
|
120
|
-
const {
|
|
121
|
-
const { eventName, positional = false, refDistance = 1, maxDistance = 24, rolloffFactor = 1, distanceModel = 'inverse' } = properties;
|
|
145
|
+
const { eventName, autoplay = false, positional = false, refDistance = 1, maxDistance = 24, rolloffFactor = 1, distanceModel = 'inverse' } = properties;
|
|
122
146
|
const sequenceIndexRef = useRef(0);
|
|
123
147
|
const listenerRef = useRef(null);
|
|
124
148
|
const positionalAudioRef = useRef(null);
|
|
@@ -126,87 +150,56 @@ function SoundComponentView({ properties, children }) {
|
|
|
126
150
|
if (!listenerRef.current) {
|
|
127
151
|
listenerRef.current = getSharedAudioListener();
|
|
128
152
|
}
|
|
129
|
-
useEffect(() => {
|
|
130
|
-
var _a;
|
|
131
|
-
if (!positional) {
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
const listener = listenerRef.current;
|
|
135
|
-
if (!listener) {
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
if (listener.parent !== camera) {
|
|
139
|
-
(_a = listener.parent) === null || _a === void 0 ? void 0 : _a.remove(listener);
|
|
140
|
-
camera.add(listener);
|
|
141
|
-
}
|
|
142
|
-
return () => {
|
|
143
|
-
if (listener.parent === camera) {
|
|
144
|
-
camera.remove(listener);
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
}, [camera, positional]);
|
|
148
153
|
useEffect(() => {
|
|
149
154
|
const audio = positionalAudioRef.current;
|
|
150
155
|
if (!audio) {
|
|
151
156
|
return;
|
|
152
157
|
}
|
|
153
|
-
audio.setRefDistance(refDistance);
|
|
154
|
-
audio.setMaxDistance(maxDistance);
|
|
155
|
-
audio.setRolloffFactor(rolloffFactor);
|
|
156
|
-
audio.setDistanceModel(distanceModel);
|
|
157
|
-
}, [distanceModel, maxDistance, refDistance, rolloffFactor]);
|
|
158
|
+
audio.setRefDistance(positional ? refDistance : Math.max(refDistance, 1));
|
|
159
|
+
audio.setMaxDistance(positional ? maxDistance : 1000000);
|
|
160
|
+
audio.setRolloffFactor(positional ? rolloffFactor : 0);
|
|
161
|
+
audio.setDistanceModel(positional ? distanceModel : 'inverse');
|
|
162
|
+
}, [distanceModel, maxDistance, positional, refDistance, rolloffFactor]);
|
|
158
163
|
useEffect(() => {
|
|
159
|
-
if (editMode || paths.length === 0 || !eventName
|
|
164
|
+
if (editMode || paths.length === 0 || !eventName) {
|
|
160
165
|
return;
|
|
161
166
|
}
|
|
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
|
-
}
|
|
167
|
+
return gameEvents.on(eventName, (payload) => {
|
|
168
|
+
if (!payloadMatchesNode(nodeId, payload)) {
|
|
169
|
+
return;
|
|
170
170
|
}
|
|
171
171
|
const clip = pickClip(paths, mode, sequenceIndexRef);
|
|
172
172
|
if (!clip)
|
|
173
173
|
return;
|
|
174
|
-
const pitch = getPitchValue(properties);
|
|
175
|
-
const volume = getVolumeValue(properties);
|
|
176
|
-
if (!positional) {
|
|
177
|
-
const loadedBuffer = getSound(clip);
|
|
178
|
-
if (loadedBuffer && !soundManager.hasBuffer(clip)) {
|
|
179
|
-
soundManager.setBuffer(clip, loadedBuffer);
|
|
180
|
-
}
|
|
181
|
-
if (soundManager.hasBuffer(clip)) {
|
|
182
|
-
soundManager.playSync(clip, { pitch, volume });
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
void soundManager.play(clip, { pitch, volume });
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
174
|
const audio = positionalAudioRef.current;
|
|
189
|
-
const listener = listenerRef.current;
|
|
190
175
|
const buffer = getSound(clip);
|
|
191
|
-
if (!audio || !
|
|
176
|
+
if (!audio || !buffer) {
|
|
192
177
|
return;
|
|
193
178
|
}
|
|
194
|
-
|
|
195
|
-
|
|
179
|
+
playBufferedAudio(audio, buffer, properties);
|
|
180
|
+
});
|
|
181
|
+
}, [editMode, eventName, getSound, mode, nodeId, paths, properties]);
|
|
182
|
+
useEffect(() => {
|
|
183
|
+
if (editMode || !autoplay || paths.length === 0) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const clip = pickClip(paths, mode, sequenceIndexRef);
|
|
187
|
+
if (!clip) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const audio = positionalAudioRef.current;
|
|
191
|
+
const buffer = getSound(clip);
|
|
192
|
+
if (!audio || !buffer) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
playBufferedAudio(audio, buffer, properties);
|
|
196
|
+
return () => {
|
|
197
|
+
if (audio === null || audio === void 0 ? void 0 : audio.isPlaying) {
|
|
196
198
|
audio.stop();
|
|
197
199
|
}
|
|
198
|
-
audio.setBuffer(buffer);
|
|
199
|
-
audio.setLoop(false);
|
|
200
|
-
audio.setPlaybackRate(pitch);
|
|
201
|
-
audio.setVolume(volume);
|
|
202
|
-
audio.play();
|
|
203
|
-
};
|
|
204
|
-
window.addEventListener(eventName, handleEvent);
|
|
205
|
-
return () => {
|
|
206
|
-
window.removeEventListener(eventName, handleEvent);
|
|
207
200
|
};
|
|
208
|
-
}, [
|
|
209
|
-
return (_jsxs(_Fragment, { children: [
|
|
201
|
+
}, [autoplay, editMode, getSound, mode, paths, properties]);
|
|
202
|
+
return (_jsxs(_Fragment, { children: [listenerRef.current ? _jsx("positionalAudio", { ref: positionalAudioRef, args: [listenerRef.current] }) : null, children] }));
|
|
210
203
|
}
|
|
211
204
|
const SoundComponent = {
|
|
212
205
|
name: 'Sound',
|
|
@@ -214,6 +207,8 @@ const SoundComponent = {
|
|
|
214
207
|
View: SoundComponentView,
|
|
215
208
|
defaultProperties: {
|
|
216
209
|
eventName: '',
|
|
210
|
+
autoplay: false,
|
|
211
|
+
loop: false,
|
|
217
212
|
clips: [],
|
|
218
213
|
clipMode: 'single',
|
|
219
214
|
positional: false,
|