react-three-game 0.0.79 → 0.0.81
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/tools/prefabeditor/PrefabRoot.js +7 -3
- package/dist/tools/prefabeditor/components/CameraComponent.js +42 -12
- 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
|
@@ -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,42 +1,72 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
2
|
-
import { PerspectiveCamera, useHelper } from '@react-three/drei';
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
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',
|
|
37
67
|
Editor: CameraComponentEditor,
|
|
38
68
|
View: CameraComponentView,
|
|
39
|
-
composition: '
|
|
69
|
+
composition: 'wrap',
|
|
40
70
|
defaultProperties: cameraDefaults,
|
|
41
71
|
};
|
|
42
72
|
export default CameraComponent;
|
|
@@ -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;
|