react-three-game 0.0.80 → 0.0.82
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 +2 -1
- package/dist/tools/prefabeditor/GameEvents.d.ts +24 -5
- package/dist/tools/prefabeditor/GameEvents.js +51 -46
- package/dist/tools/prefabeditor/PrefabRoot.js +7 -3
- package/dist/tools/prefabeditor/components/CameraComponent.js +40 -10
- package/dist/tools/prefabeditor/components/ClickComponent.js +2 -2
- package/dist/tools/prefabeditor/components/GeometryComponent.js +2 -2
- package/dist/tools/prefabeditor/components/Input.d.ts +28 -2
- package/dist/tools/prefabeditor/components/Input.js +132 -1
- package/dist/tools/prefabeditor/components/ModelComponent.js +2 -7
- package/dist/tools/prefabeditor/components/PhysicsComponent.js +2 -2
- package/dist/tools/prefabeditor/components/SoundComponent.js +2 -2
- package/dist/tools/prefabeditor/components/TransformComponent.js +1 -5
- package/dist/tools/prefabeditor/runtimeContext.d.ts +2 -2
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as GameCanvas } from './shared/GameCanvas';
|
|
2
2
|
export type { GameCanvasProps } from './shared/GameCanvas';
|
|
3
|
+
export { useBeforePhysicsStep, useAfterPhysicsStep, useRapier } from '@react-three/rapier';
|
|
3
4
|
export { ground } from './helpers';
|
|
4
5
|
export type { GroundOptions, Vec3 } from './helpers';
|
|
5
6
|
export { sound as soundManager } from './helpers/SoundManager';
|
|
@@ -26,10 +27,11 @@ export type { Component, ComponentViewProps } from './tools/prefabeditor/compone
|
|
|
26
27
|
export type { FieldDefinition, FieldType } from './tools/prefabeditor/components/Input';
|
|
27
28
|
export type { Prefab, GameObject, ComponentData } from './tools/prefabeditor/types';
|
|
28
29
|
export { findComponent, findComponentEntry, hasComponent } from './tools/prefabeditor/types';
|
|
29
|
-
export { gameEvents, useGameEvent, getEntityIdFromRigidBody } from './tools/prefabeditor/GameEvents';
|
|
30
|
+
export { gameEvents, useGameEvent, usePhysicsEvent, useClickEvent, getEntityIdFromRigidBody } from './tools/prefabeditor/GameEvents';
|
|
30
31
|
export type { GameEventType, GameEventMap, GameEventPayload, PhysicsEventType, InteractionEventType, PhysicsEventPayload, ClickEventPayload } from './tools/prefabeditor/GameEvents';
|
|
31
32
|
export { loadFiles } from './tools/dragdrop/DragDropLoader';
|
|
32
33
|
export type { AssetLoadOptions } from './tools/dragdrop/DragDropLoader';
|
|
33
34
|
export { loadModel, loadSound, loadTexture } from './tools/dragdrop/modelLoader';
|
|
34
35
|
export type { LoadedModel, LoadedModels, ModelLoadResult, LoadedSound, LoadedSounds, SoundLoadResult, LoadedTexture, LoadedTextures, TextureLoadResult, ProgressCallback, } from './tools/dragdrop/modelLoader';
|
|
35
36
|
export { ModelListViewer, SoundListViewer, ModelPicker, SoundPicker, TextureListViewer, TexturePicker, SingleModelViewer, SingleSoundViewer, SingleTextureViewer, SharedCanvas, } from './tools/assetviewer/page';
|
|
37
|
+
export type { PrefabRootRef } from './tools/prefabeditor/PrefabRoot';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Core
|
|
2
2
|
export { default as GameCanvas } from './shared/GameCanvas';
|
|
3
|
+
export { useBeforePhysicsStep, useAfterPhysicsStep, useRapier } from '@react-three/rapier';
|
|
3
4
|
// Helpers
|
|
4
5
|
export { ground } from './helpers';
|
|
5
6
|
export { sound as soundManager } from './helpers/SoundManager';
|
|
@@ -22,7 +23,7 @@ export { createModelNode, createImageNode, } from './tools/prefabeditor/prefab';
|
|
|
22
23
|
export { useAssetRuntime, useEntityRuntime, useEntityObjectRef, useEntityRigidBodyRef } from './tools/prefabeditor/runtimeContext';
|
|
23
24
|
export { findComponent, findComponentEntry, hasComponent } from './tools/prefabeditor/types';
|
|
24
25
|
// Game Events (physics + custom events)
|
|
25
|
-
export { gameEvents, useGameEvent, getEntityIdFromRigidBody } from './tools/prefabeditor/GameEvents';
|
|
26
|
+
export { gameEvents, useGameEvent, usePhysicsEvent, useClickEvent, getEntityIdFromRigidBody } from './tools/prefabeditor/GameEvents';
|
|
26
27
|
// Asset Loading
|
|
27
28
|
export { loadFiles } from './tools/dragdrop/DragDropLoader';
|
|
28
29
|
export { loadModel, loadSound, loadTexture } from './tools/dragdrop/modelLoader';
|
|
@@ -40,6 +40,13 @@ export type GameEventType = keyof GameEventMap | (string & {});
|
|
|
40
40
|
/** Get payload type for an event, or fallback to generic */
|
|
41
41
|
export type GameEventPayload<T extends string> = T extends keyof GameEventMap ? GameEventMap[T] : Record<string, unknown>;
|
|
42
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;
|
|
43
50
|
/**
|
|
44
51
|
* Game event system for all game interactions.
|
|
45
52
|
*
|
|
@@ -66,16 +73,16 @@ export declare const gameEvents: {
|
|
|
66
73
|
/**
|
|
67
74
|
* Emit an event to all subscribers
|
|
68
75
|
*/
|
|
69
|
-
emit
|
|
76
|
+
emit: typeof emitGameEvent;
|
|
70
77
|
/**
|
|
71
78
|
* Subscribe to an event type
|
|
72
79
|
* @returns Unsubscribe function
|
|
73
80
|
*/
|
|
74
|
-
on
|
|
81
|
+
on: typeof onGameEvent;
|
|
75
82
|
/**
|
|
76
83
|
* Unsubscribe from an event type
|
|
77
84
|
*/
|
|
78
|
-
off
|
|
85
|
+
off: typeof offGameEvent;
|
|
79
86
|
/**
|
|
80
87
|
* Remove all subscribers (useful for cleanup/reset)
|
|
81
88
|
*/
|
|
@@ -97,10 +104,22 @@ export declare const gameEvents: {
|
|
|
97
104
|
*
|
|
98
105
|
* // Custom event
|
|
99
106
|
* useGameEvent('player:death', (payload) => {
|
|
100
|
-
*
|
|
107
|
+
* const cause = typeof payload.cause === 'string' ? payload.cause : 'unknown';
|
|
108
|
+
* showGameOver(cause);
|
|
101
109
|
* }, []);
|
|
102
110
|
*/
|
|
103
|
-
export declare function useGameEvent<
|
|
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;
|
|
104
123
|
/**
|
|
105
124
|
* Helper to extract entity ID from Rapier collision data.
|
|
106
125
|
* Entity IDs are stored in RigidBody userData.
|
|
@@ -1,6 +1,40 @@
|
|
|
1
1
|
import { useEffect, useCallback } from 'react';
|
|
2
2
|
// Internal subscriber storage
|
|
3
3
|
const subscribers = new Map();
|
|
4
|
+
function emitGameEvent(type, payload) {
|
|
5
|
+
const handlers = subscribers.get(type);
|
|
6
|
+
if (handlers) {
|
|
7
|
+
handlers.forEach(handler => {
|
|
8
|
+
try {
|
|
9
|
+
handler(payload);
|
|
10
|
+
}
|
|
11
|
+
catch (e) {
|
|
12
|
+
console.error(`Error in gameEvents handler for ${type}:`, e);
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function onGameEvent(type, handler) {
|
|
18
|
+
if (!subscribers.has(type)) {
|
|
19
|
+
subscribers.set(type, new Set());
|
|
20
|
+
}
|
|
21
|
+
subscribers.get(type).add(handler);
|
|
22
|
+
return () => {
|
|
23
|
+
var _a;
|
|
24
|
+
(_a = subscribers.get(type)) === null || _a === void 0 ? void 0 : _a.delete(handler);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function offGameEvent(type, handler) {
|
|
28
|
+
var _a;
|
|
29
|
+
(_a = subscribers.get(type)) === null || _a === void 0 ? void 0 : _a.delete(handler);
|
|
30
|
+
}
|
|
31
|
+
function useTypedGameEvent(type, handler, deps = []) {
|
|
32
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
33
|
+
const stableHandler = useCallback(handler, deps);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
return onGameEvent(type, stableHandler);
|
|
36
|
+
}, [type, stableHandler]);
|
|
37
|
+
}
|
|
4
38
|
/**
|
|
5
39
|
* Game event system for all game interactions.
|
|
6
40
|
*
|
|
@@ -27,40 +61,16 @@ export const gameEvents = {
|
|
|
27
61
|
/**
|
|
28
62
|
* Emit an event to all subscribers
|
|
29
63
|
*/
|
|
30
|
-
emit
|
|
31
|
-
const handlers = subscribers.get(type);
|
|
32
|
-
if (handlers) {
|
|
33
|
-
handlers.forEach(handler => {
|
|
34
|
-
try {
|
|
35
|
-
handler(payload);
|
|
36
|
-
}
|
|
37
|
-
catch (e) {
|
|
38
|
-
console.error(`Error in gameEvents handler for ${type}:`, e);
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
},
|
|
64
|
+
emit: emitGameEvent,
|
|
43
65
|
/**
|
|
44
66
|
* Subscribe to an event type
|
|
45
67
|
* @returns Unsubscribe function
|
|
46
68
|
*/
|
|
47
|
-
on
|
|
48
|
-
if (!subscribers.has(type)) {
|
|
49
|
-
subscribers.set(type, new Set());
|
|
50
|
-
}
|
|
51
|
-
subscribers.get(type).add(handler);
|
|
52
|
-
return () => {
|
|
53
|
-
var _a;
|
|
54
|
-
(_a = subscribers.get(type)) === null || _a === void 0 ? void 0 : _a.delete(handler);
|
|
55
|
-
};
|
|
56
|
-
},
|
|
69
|
+
on: onGameEvent,
|
|
57
70
|
/**
|
|
58
71
|
* Unsubscribe from an event type
|
|
59
72
|
*/
|
|
60
|
-
off
|
|
61
|
-
var _a;
|
|
62
|
-
(_a = subscribers.get(type)) === null || _a === void 0 ? void 0 : _a.delete(handler);
|
|
63
|
-
},
|
|
73
|
+
off: offGameEvent,
|
|
64
74
|
/**
|
|
65
75
|
* Remove all subscribers (useful for cleanup/reset)
|
|
66
76
|
*/
|
|
@@ -75,27 +85,22 @@ export const gameEvents = {
|
|
|
75
85
|
return ((_b = (_a = subscribers.get(type)) === null || _a === void 0 ? void 0 : _a.size) !== null && _b !== void 0 ? _b : 0) > 0;
|
|
76
86
|
}
|
|
77
87
|
};
|
|
88
|
+
export function useGameEvent(type, handler, deps = []) {
|
|
89
|
+
useTypedGameEvent(type, handler, deps);
|
|
90
|
+
}
|
|
78
91
|
/**
|
|
79
|
-
* React hook to subscribe to
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* @example
|
|
83
|
-
* // Physics event
|
|
84
|
-
* useGameEvent('sensor:enter', (payload) => {
|
|
85
|
-
* if (payload.sourceEntityId === 'coin') collectCoin();
|
|
86
|
-
* }, []);
|
|
87
|
-
*
|
|
88
|
-
* // Custom event
|
|
89
|
-
* useGameEvent('player:death', (payload) => {
|
|
90
|
-
* showGameOver(payload.cause);
|
|
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.
|
|
92
94
|
*/
|
|
93
|
-
export function
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
export function usePhysicsEvent(type, handler, deps = []) {
|
|
96
|
+
useTypedGameEvent(type, handler, deps);
|
|
97
|
+
}
|
|
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
|
+
export function useClickEvent(type, handler, deps = []) {
|
|
103
|
+
useTypedGameEvent(type, handler, deps);
|
|
99
104
|
}
|
|
100
105
|
// ============================================================================
|
|
101
106
|
// Helpers
|
|
@@ -373,17 +373,21 @@ function renderCompositionNode(gameObject, ctx, childNodes) {
|
|
|
373
373
|
return applyNodeComposition(gameObject, _jsxs(_Fragment, { children: [primaryContent, childNodes] }));
|
|
374
374
|
}
|
|
375
375
|
function renderNodePrimaryContent(gameObject, ctx) {
|
|
376
|
-
var _a;
|
|
376
|
+
var _a, _b;
|
|
377
377
|
const geometry = findComponent(gameObject, "Geometry");
|
|
378
378
|
const material = findComponent(gameObject, "Material");
|
|
379
379
|
const model = findComponent(gameObject, "Model");
|
|
380
380
|
const geometryDef = geometry && getComponentDef(geometry.type);
|
|
381
381
|
const materialDef = material && getComponentDef(material.type);
|
|
382
382
|
const modelDef = model && getComponentDef(model.type);
|
|
383
|
+
const geometryProperties = (_a = geometry === null || geometry === void 0 ? void 0 : geometry.properties) !== null && _a !== void 0 ? _a : {};
|
|
384
|
+
const meshVisible = geometryProperties.visible !== false;
|
|
385
|
+
const meshCastShadow = meshVisible && geometryProperties.castShadow !== false;
|
|
386
|
+
const meshReceiveShadow = meshVisible && geometryProperties.receiveShadow !== false;
|
|
383
387
|
if ((geometry === null || geometry === void 0 ? void 0 : geometry.type) && (geometryDef === null || geometryDef === void 0 ? void 0 : geometryDef.View)) {
|
|
384
|
-
return (_jsxs("mesh", { castShadow:
|
|
388
|
+
return (_jsxs("mesh", { visible: meshVisible, castShadow: meshCastShadow, receiveShadow: meshReceiveShadow, children: [_jsx(geometryDef.View, { properties: geometry.properties }), material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, { properties: material.properties }, "material"))] }));
|
|
385
389
|
}
|
|
386
|
-
if ((model === null || model === void 0 ? void 0 : model.type) && (modelDef === null || modelDef === void 0 ? void 0 : modelDef.View) && !((
|
|
390
|
+
if ((model === null || model === void 0 ? void 0 : model.type) && (modelDef === null || modelDef === void 0 ? void 0 : modelDef.View) && !((_b = model.properties) === null || _b === void 0 ? void 0 : _b.instanced) && isNodeReady(gameObject, ctx.loadedModels)) {
|
|
387
391
|
return _jsx(modelDef.View, { properties: model.properties });
|
|
388
392
|
}
|
|
389
393
|
return null;
|
|
@@ -1,36 +1,66 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { PerspectiveCamera, useHelper } from '@react-three/drei';
|
|
2
|
+
import { OrthographicCamera, PerspectiveCamera, useHelper } from '@react-three/drei';
|
|
3
3
|
import { useRef } from 'react';
|
|
4
4
|
import { CameraHelper } from 'three';
|
|
5
|
-
import { useFrame } from '@react-three/fiber';
|
|
5
|
+
import { useFrame, useThree } from '@react-three/fiber';
|
|
6
6
|
import { useEntityRuntime } from '../runtimeContext';
|
|
7
|
-
import { FieldGroup, NumberField } from './Input';
|
|
7
|
+
import { FieldGroup, NumberField, SelectField } from './Input';
|
|
8
|
+
const CAMERA_PROJECTION_OPTIONS = [
|
|
9
|
+
{ value: 'perspective', label: 'Perspective' },
|
|
10
|
+
{ value: 'orthographic', label: 'Orthographic' },
|
|
11
|
+
];
|
|
8
12
|
const cameraDefaults = {
|
|
13
|
+
projection: 'perspective',
|
|
9
14
|
fov: 50,
|
|
10
15
|
near: 0.1,
|
|
11
16
|
zoom: 1,
|
|
12
17
|
far: 1000,
|
|
18
|
+
orthographicSize: 10,
|
|
13
19
|
};
|
|
14
20
|
function CameraComponentEditor({ component, onUpdate }) {
|
|
21
|
+
var _a;
|
|
15
22
|
const values = Object.assign(Object.assign({}, cameraDefaults), component.properties);
|
|
16
|
-
|
|
23
|
+
const projection = (_a = values.projection) !== null && _a !== void 0 ? _a : cameraDefaults.projection;
|
|
24
|
+
return (_jsxs(FieldGroup, { children: [_jsx(SelectField, { name: "projection", label: "Projection", values: values, onChange: onUpdate, fallback: cameraDefaults.projection, options: [...CAMERA_PROJECTION_OPTIONS] }), projection === 'perspective' ? (_jsx(NumberField, { name: "fov", label: "FOV", values: values, onChange: onUpdate, fallback: 50, min: 1, max: 179, step: 1 })) : null, projection === 'orthographic' ? (_jsx(NumberField, { name: "orthographicSize", label: "Ortho Size", values: values, onChange: onUpdate, fallback: cameraDefaults.orthographicSize, min: 0.01, step: 0.1 })) : null, _jsx(NumberField, { name: "near", label: "Near", values: values, onChange: onUpdate, fallback: 0.1, min: 0.001, step: 0.1 }), _jsx(NumberField, { name: "zoom", label: "Zoom", values: values, onChange: onUpdate, fallback: 1, min: 0.01, step: 0.1 }), _jsx(NumberField, { name: "far", label: "Far", values: values, onChange: onUpdate, fallback: 1000, min: 0.1, step: 1 })] }));
|
|
17
25
|
}
|
|
18
26
|
function CameraComponentView({ properties, children }) {
|
|
27
|
+
var _a;
|
|
19
28
|
const { editMode, isSelected } = useEntityRuntime();
|
|
29
|
+
const { size } = useThree();
|
|
20
30
|
const merged = Object.assign(Object.assign({}, cameraDefaults), properties);
|
|
31
|
+
const projection = (_a = merged.projection) !== null && _a !== void 0 ? _a : cameraDefaults.projection;
|
|
21
32
|
const fov = merged.fov;
|
|
22
33
|
const near = merged.near;
|
|
23
34
|
const zoom = merged.zoom;
|
|
24
35
|
const far = merged.far;
|
|
25
|
-
const
|
|
26
|
-
|
|
36
|
+
const orthographicSize = merged.orthographicSize;
|
|
37
|
+
const aspect = size.height > 0 ? size.width / size.height : 1;
|
|
38
|
+
const halfHeight = orthographicSize / 2;
|
|
39
|
+
const halfWidth = halfHeight * aspect;
|
|
40
|
+
const perspectiveCameraRef = useRef(null);
|
|
41
|
+
const orthographicCameraRef = useRef(null);
|
|
42
|
+
const activeCameraRef = projection === 'orthographic'
|
|
43
|
+
? orthographicCameraRef
|
|
44
|
+
: perspectiveCameraRef;
|
|
45
|
+
useHelper(editMode && isSelected ? activeCameraRef : null, CameraHelper);
|
|
27
46
|
useFrame(() => {
|
|
28
|
-
if (
|
|
29
|
-
|
|
30
|
-
|
|
47
|
+
if (!editMode || !isSelected)
|
|
48
|
+
return;
|
|
49
|
+
if (projection === 'orthographic' && orthographicCameraRef.current) {
|
|
50
|
+
orthographicCameraRef.current.updateProjectionMatrix();
|
|
51
|
+
orthographicCameraRef.current.updateMatrixWorld();
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (perspectiveCameraRef.current) {
|
|
55
|
+
perspectiveCameraRef.current.updateProjectionMatrix();
|
|
56
|
+
perspectiveCameraRef.current.updateMatrixWorld();
|
|
31
57
|
}
|
|
32
58
|
});
|
|
33
|
-
|
|
59
|
+
const helperContent = editMode ? (_jsxs("group", { children: [_jsxs("mesh", { children: [_jsx("boxGeometry", { args: [0.3, 0.3, 0.5] }), _jsx("meshBasicMaterial", { color: '#22d3ee', wireframe: true })] }), _jsxs("mesh", { position: [0, 0, -0.25], rotation: [Math.PI / 2, 0, 0], children: [_jsx("coneGeometry", { args: [0.08, 0.16, 16] }), _jsx("meshBasicMaterial", { color: '#22d3ee', wireframe: true })] })] })) : null;
|
|
60
|
+
if (projection === 'orthographic') {
|
|
61
|
+
return (_jsxs(OrthographicCamera, { ref: orthographicCameraRef, makeDefault: !editMode, near: near, zoom: zoom, far: far, left: -halfWidth, right: halfWidth, top: halfHeight, bottom: -halfHeight, children: [helperContent, children] }));
|
|
62
|
+
}
|
|
63
|
+
return (_jsxs(PerspectiveCamera, { ref: perspectiveCameraRef, makeDefault: !editMode, fov: fov, near: near, zoom: zoom, far: far, children: [helperContent, children] }));
|
|
34
64
|
}
|
|
35
65
|
const CameraComponent = {
|
|
36
66
|
name: 'Camera',
|
|
@@ -2,9 +2,9 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useRef } from 'react';
|
|
3
3
|
import { gameEvents } from '../GameEvents';
|
|
4
4
|
import { useEntityRuntime } from '../runtimeContext';
|
|
5
|
-
import {
|
|
5
|
+
import { EventField, FieldGroup } from './Input';
|
|
6
6
|
function ClickComponentEditor({ component, onUpdate }) {
|
|
7
|
-
return (_jsxs(FieldGroup, { children: [_jsx("div", { style: { fontSize: 12, opacity: 0.8 }, children: "Emits a game event in play mode when this entity is clicked." }), _jsx(
|
|
7
|
+
return (_jsxs(FieldGroup, { children: [_jsx("div", { style: { fontSize: 12, opacity: 0.8 }, children: "Emits a game event in play mode when this entity is clicked." }), _jsx(EventField, { name: "eventName", label: "Emit Event", values: component.properties, onChange: onUpdate, placeholder: "click" })] }));
|
|
8
8
|
}
|
|
9
9
|
function ClickComponentView({ children, properties }) {
|
|
10
10
|
const clickValid = useRef(false);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { FieldGroup, NumberField, SelectField } from "./Input";
|
|
2
|
+
import { BooleanField, FieldGroup, NumberField, SelectField } from "./Input";
|
|
3
3
|
const GEOMETRY_ARGS = {
|
|
4
4
|
box: {
|
|
5
5
|
fields: [
|
|
@@ -61,7 +61,7 @@ function GeometryComponentEditor({ component, onUpdate, }) {
|
|
|
61
61
|
] }), schema.fields.map((field, index) => {
|
|
62
62
|
var _a;
|
|
63
63
|
return (_jsx(NumberField, { name: field.name, label: field.label, values: { [field.name]: (_a = args[index]) !== null && _a !== void 0 ? _a : field.defaultValue }, onChange: (next) => updateArg(index, next[field.name]), fallback: field.defaultValue, min: field.min, step: field.step }, field.name));
|
|
64
|
-
})] }));
|
|
64
|
+
}), _jsx(BooleanField, { name: "visible", label: "Visible", values: component.properties, onChange: handleChange, fallback: true }), _jsx(BooleanField, { name: "castShadow", label: "Cast Shadow", values: component.properties, onChange: handleChange, fallback: true }), _jsx(BooleanField, { name: "receiveShadow", label: "Receive Shadow", values: component.properties, onChange: handleChange, fallback: true })] }));
|
|
65
65
|
}
|
|
66
66
|
// View for Geometry component
|
|
67
67
|
function GeometryComponentView({ properties, children }) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
export type FieldType = 'vector3' | 'number' | 'string' | 'color' | 'boolean' | 'select';
|
|
2
|
+
export type FieldType = 'vector3' | 'number' | 'string' | 'color' | 'boolean' | 'select' | 'node' | 'event';
|
|
3
3
|
interface BaseFieldDefinition {
|
|
4
4
|
name: string;
|
|
5
5
|
label: string;
|
|
@@ -31,6 +31,15 @@ interface SelectFieldDefinition extends BaseFieldDefinition {
|
|
|
31
31
|
label: string;
|
|
32
32
|
}[];
|
|
33
33
|
}
|
|
34
|
+
interface NodeFieldDefinition extends BaseFieldDefinition {
|
|
35
|
+
type: 'node';
|
|
36
|
+
placeholder?: string;
|
|
37
|
+
includeRoot?: boolean;
|
|
38
|
+
}
|
|
39
|
+
interface EventFieldDefinition extends BaseFieldDefinition {
|
|
40
|
+
type: 'event';
|
|
41
|
+
placeholder?: string;
|
|
42
|
+
}
|
|
34
43
|
interface CustomFieldDefinition extends BaseFieldDefinition {
|
|
35
44
|
type: 'custom';
|
|
36
45
|
render: (props: {
|
|
@@ -40,7 +49,7 @@ interface CustomFieldDefinition extends BaseFieldDefinition {
|
|
|
40
49
|
onChangeMultiple: (values: Record<string, any>) => void;
|
|
41
50
|
}) => React.ReactNode;
|
|
42
51
|
}
|
|
43
|
-
export type FieldDefinition = Vector3FieldDefinition | NumberFieldDefinition | StringFieldDefinition | ColorFieldDefinition | BooleanFieldDefinition | SelectFieldDefinition | CustomFieldDefinition;
|
|
52
|
+
export type FieldDefinition = Vector3FieldDefinition | NumberFieldDefinition | StringFieldDefinition | ColorFieldDefinition | BooleanFieldDefinition | SelectFieldDefinition | NodeFieldDefinition | EventFieldDefinition | CustomFieldDefinition;
|
|
44
53
|
declare const styles: {
|
|
45
54
|
input: React.CSSProperties;
|
|
46
55
|
label: React.CSSProperties;
|
|
@@ -79,6 +88,19 @@ export declare function StringInput({ label, value, onChange, placeholder }: {
|
|
|
79
88
|
onChange: (value: string) => void;
|
|
80
89
|
placeholder?: string;
|
|
81
90
|
}): import("react/jsx-runtime").JSX.Element;
|
|
91
|
+
export declare function NodeInput({ label, value, onChange, placeholder, includeRoot, }: {
|
|
92
|
+
label: string;
|
|
93
|
+
value: string;
|
|
94
|
+
onChange: (value: string) => void;
|
|
95
|
+
placeholder?: string;
|
|
96
|
+
includeRoot?: boolean;
|
|
97
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
98
|
+
export declare function EventInput({ label, value, onChange, placeholder, }: {
|
|
99
|
+
label: string;
|
|
100
|
+
value: string;
|
|
101
|
+
onChange: (value: string) => void;
|
|
102
|
+
placeholder?: string;
|
|
103
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
82
104
|
export declare function BooleanInput({ label, value, onChange }: {
|
|
83
105
|
label?: string;
|
|
84
106
|
value: boolean;
|
|
@@ -157,6 +179,10 @@ export declare function StringField({ name, label, values, onChange, fallback, p
|
|
|
157
179
|
export declare function ColorField({ name, label, values, onChange, fallback, }: BoundColorFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
158
180
|
export declare function BooleanField({ name, label, values, onChange, fallback, }: BoundBooleanFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
159
181
|
export declare function SelectField({ name, label, values, onChange, fallback, options, }: BoundSelectFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
182
|
+
export declare function NodeField({ name, label, values, onChange, fallback, }: BoundStringFieldProps & {
|
|
183
|
+
fallback?: string;
|
|
184
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
185
|
+
export declare function EventField({ name, label, values, onChange, fallback, placeholder, }: BoundStringFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
160
186
|
export declare function Vector3Field({ name, label, values, onChange, fallback, snap, labelExtra, }: BoundVector3FieldProps): import("react/jsx-runtime").JSX.Element;
|
|
161
187
|
interface FieldRendererProps {
|
|
162
188
|
fields: FieldDefinition[];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { colors } from '../styles';
|
|
4
|
+
import { useOptionalPrefabStoreApi } from '../prefabStore';
|
|
4
5
|
// ============================================================================
|
|
5
6
|
// Shared Styles (derived from shared color tokens)
|
|
6
7
|
// ============================================================================
|
|
@@ -277,6 +278,124 @@ export function ColorInput({ label, value, onChange }) {
|
|
|
277
278
|
export function StringInput({ label, value, onChange, placeholder }) {
|
|
278
279
|
return (_jsxs("div", { children: [label && _jsx(Label, { children: label }), _jsx("input", { type: "text", style: styles.input, value: value, onChange: e => onChange(e.target.value), placeholder: placeholder })] }));
|
|
279
280
|
}
|
|
281
|
+
function useOptionalPrefabSnapshot() {
|
|
282
|
+
const store = useOptionalPrefabStoreApi();
|
|
283
|
+
const [state, setState] = useState(() => { var _a; return (_a = store === null || store === void 0 ? void 0 : store.getState()) !== null && _a !== void 0 ? _a : null; });
|
|
284
|
+
useEffect(() => {
|
|
285
|
+
if (!store) {
|
|
286
|
+
setState(null);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
setState(store.getState());
|
|
290
|
+
return store.subscribe(nextState => setState(nextState));
|
|
291
|
+
}, [store]);
|
|
292
|
+
return state;
|
|
293
|
+
}
|
|
294
|
+
function SearchSuggestionList({ query, options, onSelect, emptyMessage, }) {
|
|
295
|
+
const normalizedQuery = query.trim().toLowerCase();
|
|
296
|
+
const filtered = useMemo(() => {
|
|
297
|
+
if (!normalizedQuery)
|
|
298
|
+
return options.slice(0, 8);
|
|
299
|
+
return options.filter(option => option.searchText.includes(normalizedQuery)).slice(0, 8);
|
|
300
|
+
}, [normalizedQuery, options]);
|
|
301
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [_jsx("input", { type: "text", style: Object.assign(Object.assign({}, styles.input), { width: '100%', textAlign: 'left' }), value: query, onChange: () => undefined, readOnly: true, "aria-hidden": true, tabIndex: -1, hidden: true }), _jsx("div", { style: {
|
|
302
|
+
display: 'flex',
|
|
303
|
+
flexDirection: 'column',
|
|
304
|
+
gap: 4,
|
|
305
|
+
maxHeight: 160,
|
|
306
|
+
overflowY: 'auto',
|
|
307
|
+
border: `1px solid ${colors.border}`,
|
|
308
|
+
borderRadius: 3,
|
|
309
|
+
background: colors.bgSurface,
|
|
310
|
+
padding: 4,
|
|
311
|
+
}, children: filtered.length === 0 ? (_jsx("div", { style: { fontSize: 11, color: colors.textMuted, padding: '4px 6px' }, children: emptyMessage })) : filtered.map(option => (_jsxs("button", { type: "button", onClick: () => onSelect(option.value), style: {
|
|
312
|
+
display: 'flex',
|
|
313
|
+
flexDirection: 'column',
|
|
314
|
+
alignItems: 'flex-start',
|
|
315
|
+
gap: 2,
|
|
316
|
+
border: `1px solid ${colors.border}`,
|
|
317
|
+
borderRadius: 3,
|
|
318
|
+
background: colors.bgInput,
|
|
319
|
+
color: colors.text,
|
|
320
|
+
padding: '6px 8px',
|
|
321
|
+
cursor: 'pointer',
|
|
322
|
+
textAlign: 'left',
|
|
323
|
+
}, children: [_jsx("span", { style: { fontSize: 11, fontWeight: 500 }, children: option.label }), option.description ? (_jsx("span", { style: { fontSize: 10, color: colors.textMuted, fontFamily: 'monospace' }, children: option.description })) : null] }, option.value))) })] }));
|
|
324
|
+
}
|
|
325
|
+
export function NodeInput({ label, value, onChange, placeholder, includeRoot = true, }) {
|
|
326
|
+
const prefabState = useOptionalPrefabSnapshot();
|
|
327
|
+
const [query, setQuery] = useState('');
|
|
328
|
+
const options = useMemo(() => {
|
|
329
|
+
var _a;
|
|
330
|
+
const nodesById = (_a = prefabState === null || prefabState === void 0 ? void 0 : prefabState.nodesById) !== null && _a !== void 0 ? _a : {};
|
|
331
|
+
const rootId = prefabState === null || prefabState === void 0 ? void 0 : prefabState.rootId;
|
|
332
|
+
return Object.values(nodesById)
|
|
333
|
+
.filter(node => includeRoot || node.id !== rootId)
|
|
334
|
+
.map(node => {
|
|
335
|
+
const nodeName = typeof node.name === 'string' && node.name.trim().length > 0 ? node.name.trim() : '(unnamed)';
|
|
336
|
+
return {
|
|
337
|
+
value: node.id,
|
|
338
|
+
label: nodeName,
|
|
339
|
+
description: node.id,
|
|
340
|
+
searchText: `${nodeName} ${node.id}`.toLowerCase(),
|
|
341
|
+
};
|
|
342
|
+
})
|
|
343
|
+
.sort((left, right) => left.label.localeCompare(right.label) || left.value.localeCompare(right.value));
|
|
344
|
+
}, [includeRoot, prefabState === null || prefabState === void 0 ? void 0 : prefabState.nodesById, prefabState === null || prefabState === void 0 ? void 0 : prefabState.rootId]);
|
|
345
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [_jsx(StringInput, { label: label, value: value, onChange: onChange, placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : 'Node id' }), options.length > 0 ? (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [_jsx("input", { type: "text", style: Object.assign(Object.assign({}, styles.input), { width: '100%', textAlign: 'left' }), value: query, onChange: e => setQuery(e.target.value), placeholder: "Search nodes by name or id" }), _jsx(SearchSuggestionList, { query: query, options: options, onSelect: (nextValue) => {
|
|
346
|
+
onChange(nextValue);
|
|
347
|
+
setQuery('');
|
|
348
|
+
}, emptyMessage: "No matching nodes." })] })) : null] }));
|
|
349
|
+
}
|
|
350
|
+
const BUILT_IN_EVENT_OPTIONS = [
|
|
351
|
+
'sensor:enter',
|
|
352
|
+
'sensor:exit',
|
|
353
|
+
'collision:enter',
|
|
354
|
+
'collision:exit',
|
|
355
|
+
'click',
|
|
356
|
+
].map(eventName => ({
|
|
357
|
+
value: eventName,
|
|
358
|
+
label: eventName,
|
|
359
|
+
searchText: eventName.toLowerCase(),
|
|
360
|
+
}));
|
|
361
|
+
export function EventInput({ label, value, onChange, placeholder, }) {
|
|
362
|
+
const prefabState = useOptionalPrefabSnapshot();
|
|
363
|
+
const [query, setQuery] = useState('');
|
|
364
|
+
const options = useMemo(() => {
|
|
365
|
+
var _a;
|
|
366
|
+
const authoredEvents = new Map();
|
|
367
|
+
Object.values((_a = prefabState === null || prefabState === void 0 ? void 0 : prefabState.nodesById) !== null && _a !== void 0 ? _a : {}).forEach(node => {
|
|
368
|
+
var _a;
|
|
369
|
+
Object.values((_a = node.components) !== null && _a !== void 0 ? _a : {}).forEach(component => {
|
|
370
|
+
var _a;
|
|
371
|
+
Object.entries((_a = component === null || component === void 0 ? void 0 : component.properties) !== null && _a !== void 0 ? _a : {}).forEach(([key, entry]) => {
|
|
372
|
+
var _a, _b;
|
|
373
|
+
if (typeof entry !== 'string')
|
|
374
|
+
return;
|
|
375
|
+
if (!(key === 'eventName' || key.endsWith('EventName')))
|
|
376
|
+
return;
|
|
377
|
+
const eventName = entry.trim();
|
|
378
|
+
if (!eventName)
|
|
379
|
+
return;
|
|
380
|
+
authoredEvents.set(eventName, {
|
|
381
|
+
value: eventName,
|
|
382
|
+
label: eventName,
|
|
383
|
+
description: `${(_a = component === null || component === void 0 ? void 0 : component.type) !== null && _a !== void 0 ? _a : 'Component'} -> ${key}`,
|
|
384
|
+
searchText: `${eventName} ${(_b = component === null || component === void 0 ? void 0 : component.type) !== null && _b !== void 0 ? _b : ''} ${key}`.toLowerCase(),
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
const merged = new Map();
|
|
390
|
+
BUILT_IN_EVENT_OPTIONS.forEach(option => merged.set(option.value, option));
|
|
391
|
+
authoredEvents.forEach((option, key) => merged.set(key, option));
|
|
392
|
+
return [...merged.values()].sort((left, right) => left.value.localeCompare(right.value));
|
|
393
|
+
}, [prefabState === null || prefabState === void 0 ? void 0 : prefabState.nodesById]);
|
|
394
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [_jsx(StringInput, { label: label, value: value, onChange: onChange, placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : 'Event name' }), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [_jsx("input", { type: "text", style: Object.assign(Object.assign({}, styles.input), { width: '100%', textAlign: 'left' }), value: query, onChange: e => setQuery(e.target.value), placeholder: "Search built-in and authored events" }), _jsx(SearchSuggestionList, { query: query, options: options, onSelect: (nextValue) => {
|
|
395
|
+
onChange(nextValue);
|
|
396
|
+
setQuery('');
|
|
397
|
+
}, emptyMessage: "No matching events." })] })] }));
|
|
398
|
+
}
|
|
280
399
|
export function BooleanInput({ label, value, onChange }) {
|
|
281
400
|
return (_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [label && _jsx(Label, { children: label }), _jsx("input", { type: "checkbox", style: {
|
|
282
401
|
height: 16,
|
|
@@ -347,6 +466,14 @@ export function SelectField({ name, label, values, onChange, fallback, options,
|
|
|
347
466
|
var _a, _b, _c, _d;
|
|
348
467
|
return (_jsx(SelectInput, { label: label, value: (_d = (_b = (_a = values[name]) !== null && _a !== void 0 ? _a : fallback) !== null && _b !== void 0 ? _b : (_c = options[0]) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : '', onChange: bindFieldChange(name, onChange), options: options }));
|
|
349
468
|
}
|
|
469
|
+
export function NodeField({ name, label, values, onChange, fallback = '', }) {
|
|
470
|
+
var _a;
|
|
471
|
+
return (_jsx(NodeInput, { label: label, value: (_a = values[name]) !== null && _a !== void 0 ? _a : fallback, onChange: bindFieldChange(name, onChange) }));
|
|
472
|
+
}
|
|
473
|
+
export function EventField({ name, label, values, onChange, fallback = '', placeholder, }) {
|
|
474
|
+
var _a;
|
|
475
|
+
return (_jsx(EventInput, { label: label, value: (_a = values[name]) !== null && _a !== void 0 ? _a : fallback, onChange: bindFieldChange(name, onChange), placeholder: placeholder }));
|
|
476
|
+
}
|
|
350
477
|
export function Vector3Field({ name, label, values, onChange, fallback = [0, 0, 0], snap, labelExtra, }) {
|
|
351
478
|
var _a;
|
|
352
479
|
return (_jsx(Vector3Input, { label: label, value: (_a = values[name]) !== null && _a !== void 0 ? _a : fallback, onChange: bindFieldChange(name, onChange), snap: snap, labelExtra: labelExtra }));
|
|
@@ -371,6 +498,10 @@ export function FieldRenderer({ fields, values, onChange }) {
|
|
|
371
498
|
return (_jsx(BooleanInput, { label: field.label, value: value !== null && value !== void 0 ? value : false, onChange: v => updateField(field.name, v) }, field.name));
|
|
372
499
|
case 'select':
|
|
373
500
|
return (_jsx(SelectInput, { label: field.label, value: (_b = value !== null && value !== void 0 ? value : (_a = field.options[0]) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : '', onChange: v => updateField(field.name, v), options: field.options }, field.name));
|
|
501
|
+
case 'node':
|
|
502
|
+
return (_jsx(NodeInput, { label: field.label, value: value !== null && value !== void 0 ? value : '', onChange: v => updateField(field.name, v), placeholder: field.placeholder, includeRoot: field.includeRoot }, field.name));
|
|
503
|
+
case 'event':
|
|
504
|
+
return (_jsx(EventInput, { label: field.label, value: value !== null && value !== void 0 ? value : '', onChange: v => updateField(field.name, v), placeholder: field.placeholder }, field.name));
|
|
374
505
|
case 'custom':
|
|
375
506
|
return (_jsxs("div", { children: [field.label && _jsx(Label, { children: field.label }), field.render({
|
|
376
507
|
value,
|
|
@@ -4,7 +4,7 @@ import { useContext, useMemo } from 'react';
|
|
|
4
4
|
import { BooleanField, FieldGroup, Label, ListEditor, NumberInput, SelectInput } from './Input';
|
|
5
5
|
import { useAssetRuntime } from '../runtimeContext';
|
|
6
6
|
import { EditorContext } from '../PrefabEditor';
|
|
7
|
-
import {
|
|
7
|
+
import { getRepeatAxesFromModelProperties, normalizeRepeatAxes } from '../InstanceProvider';
|
|
8
8
|
import { colors } from '../styles';
|
|
9
9
|
const AXIS_OPTIONS = [
|
|
10
10
|
{ value: 'x', label: 'X' },
|
|
@@ -91,12 +91,7 @@ const ModelComponent = {
|
|
|
91
91
|
name: 'Model',
|
|
92
92
|
Editor: ModelComponentEditor,
|
|
93
93
|
View: ModelComponentView,
|
|
94
|
-
defaultProperties: {
|
|
95
|
-
filename: '',
|
|
96
|
-
instanced: false,
|
|
97
|
-
repeat: false,
|
|
98
|
-
repeatAxes: DEFAULT_REPEAT_AXES
|
|
99
|
-
},
|
|
94
|
+
defaultProperties: {},
|
|
100
95
|
getAssetRefs: (properties) => {
|
|
101
96
|
if (properties.filename)
|
|
102
97
|
return [{ type: 'model', path: properties.filename }];
|
|
@@ -13,7 +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 { useRef, useEffect, useCallback } from 'react';
|
|
15
15
|
import { useAssetRuntime, useEntityRuntime } from "../runtimeContext";
|
|
16
|
-
import { BooleanField, FieldGroup, ListEditor, NumberField, SelectField, SelectInput,
|
|
16
|
+
import { BooleanField, EventInput, FieldGroup, ListEditor, NumberField, SelectField, SelectInput, Vector3Field } from "./Input";
|
|
17
17
|
import { gameEvents, getEntityIdFromRigidBody } from "../GameEvents";
|
|
18
18
|
import { colors } from "../styles";
|
|
19
19
|
export function isPhysicsProps(v) {
|
|
@@ -103,7 +103,7 @@ function PhysicsEventBindingsEditor({ values, onChange, }) {
|
|
|
103
103
|
cursor: 'pointer',
|
|
104
104
|
padding: 0,
|
|
105
105
|
flexShrink: 0,
|
|
106
|
-
}, title: "Remove physics event", children: "\u00D7" })] }), _jsx(
|
|
106
|
+
}, title: "Remove physics event", children: "\u00D7" })] }), _jsx(EventInput, { label: "Event Name", value: (_a = values[option.key]) !== null && _a !== void 0 ? _a : option.defaultName, onChange: (eventName) => updateEventName(option.key, eventName), placeholder: option.defaultName })] }, option.key));
|
|
107
107
|
} }));
|
|
108
108
|
}
|
|
109
109
|
function LockedAxisField({ label, name, values, onChange, }) {
|
|
@@ -5,7 +5,7 @@ import { SoundPicker } from '../../assetviewer/page';
|
|
|
5
5
|
import { sound as soundManager } from '../../../helpers/SoundManager';
|
|
6
6
|
import { gameEvents } from '../GameEvents';
|
|
7
7
|
import { useAssetRuntime, useEntityRuntime } from '../runtimeContext';
|
|
8
|
-
import { BooleanField, FieldGroup, FieldRenderer, ListEditor, NumberField, SelectField
|
|
8
|
+
import { BooleanField, EventField, FieldGroup, FieldRenderer, ListEditor, NumberField, SelectField } from './Input';
|
|
9
9
|
import { colors } from '../styles';
|
|
10
10
|
import { AudioListener } from 'three';
|
|
11
11
|
const CLIP_MODE_OPTIONS = [
|
|
@@ -84,7 +84,7 @@ function SoundComponentEditor({ component, onUpdate, basePath = '' }) {
|
|
|
84
84
|
const removeClip = (index) => {
|
|
85
85
|
setClips(clips.filter((_, clipIndex) => clipIndex !== index));
|
|
86
86
|
};
|
|
87
|
-
return (_jsxs(FieldGroup, { children: [_jsx(
|
|
87
|
+
return (_jsxs(FieldGroup, { children: [_jsx(EventField, { name: "eventName", label: "Listen Event", values: component.properties, onChange: onUpdate, placeholder: "click" }), _jsx(FieldRenderer, { fields: [
|
|
88
88
|
{
|
|
89
89
|
name: 'clipMode',
|
|
90
90
|
label: 'Clip Mode',
|
|
@@ -48,10 +48,6 @@ function TransformComponentEditor({ component, onUpdate }) {
|
|
|
48
48
|
const TransformComponent = {
|
|
49
49
|
name: 'Transform',
|
|
50
50
|
Editor: TransformComponentEditor,
|
|
51
|
-
defaultProperties: {
|
|
52
|
-
position: [0, 0, 0],
|
|
53
|
-
rotation: [0, 0, 0],
|
|
54
|
-
scale: [1, 1, 1]
|
|
55
|
-
}
|
|
51
|
+
defaultProperties: {}
|
|
56
52
|
};
|
|
57
53
|
export default TransformComponent;
|
|
@@ -12,11 +12,11 @@ export interface AssetRuntime {
|
|
|
12
12
|
getTexture: (path: string) => Texture | null;
|
|
13
13
|
getSound: (path: string) => AudioBuffer | null;
|
|
14
14
|
getAssetRevision: () => string;
|
|
15
|
-
}
|
|
16
|
-
export interface AssetRuntimeContextValue extends AssetRuntime {
|
|
17
15
|
getObject: (id: string) => Object3D | null;
|
|
18
16
|
getRigidBody: (id: string) => any;
|
|
19
17
|
}
|
|
18
|
+
export interface AssetRuntimeContextValue extends AssetRuntime {
|
|
19
|
+
}
|
|
20
20
|
export interface EntityRuntime {
|
|
21
21
|
nodeId: string;
|
|
22
22
|
editMode?: boolean;
|