@needle-tools/engine 4.3.0-alpha → 4.3.0-alpha.1
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/CHANGELOG.md +3 -0
- package/dist/needle-engine.bundle.js +1467 -222
- package/dist/needle-engine.bundle.light.js +1467 -222
- package/dist/needle-engine.bundle.light.min.js +32 -32
- package/dist/needle-engine.bundle.light.umd.cjs +3 -3
- package/dist/needle-engine.bundle.min.js +3 -3
- package/dist/needle-engine.bundle.umd.cjs +3 -3
- package/dist/needle-engine.light.d.ts +9 -9
- package/lib/engine/engine_types.d.ts +162 -17
- package/lib/engine-components/Animator.d.ts +129 -21
- package/lib/engine-components/Animator.js +115 -21
- package/lib/engine-components/Animator.js.map +1 -1
- package/lib/engine-components/AnimatorController.d.ts +161 -32
- package/lib/engine-components/AnimatorController.js +176 -29
- package/lib/engine-components/AnimatorController.js.map +1 -1
- package/lib/engine-components/AudioListener.d.ts +16 -5
- package/lib/engine-components/AudioListener.js +16 -5
- package/lib/engine-components/AudioListener.js.map +1 -1
- package/lib/engine-components/AudioSource.d.ts +120 -28
- package/lib/engine-components/AudioSource.js +120 -37
- package/lib/engine-components/AudioSource.js.map +1 -1
- package/lib/engine-components/AvatarLoader.d.ts +61 -0
- package/lib/engine-components/AvatarLoader.js +61 -1
- package/lib/engine-components/AvatarLoader.js.map +1 -1
- package/lib/engine-components/AxesHelper.d.ts +19 -1
- package/lib/engine-components/AxesHelper.js +19 -1
- package/lib/engine-components/AxesHelper.js.map +1 -1
- package/lib/engine-components/BoxHelperComponent.d.ts +26 -0
- package/lib/engine-components/BoxHelperComponent.js +26 -0
- package/lib/engine-components/BoxHelperComponent.js.map +1 -1
- package/lib/engine-components/Camera.d.ts +126 -37
- package/lib/engine-components/Camera.js +139 -37
- package/lib/engine-components/Camera.js.map +1 -1
- package/lib/engine-components/CameraUtils.js +20 -0
- package/lib/engine-components/CameraUtils.js.map +1 -1
- package/lib/engine-components/Collider.d.ts +95 -21
- package/lib/engine-components/Collider.js +100 -23
- package/lib/engine-components/Collider.js.map +1 -1
- package/lib/engine-components/Component.d.ts +554 -106
- package/lib/engine-components/Component.js +352 -81
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/DragControls.d.ts +95 -21
- package/lib/engine-components/DragControls.js +126 -32
- package/lib/engine-components/DragControls.js.map +1 -1
- package/lib/engine-components/DropListener.d.ts +99 -16
- package/lib/engine-components/DropListener.js +119 -14
- package/lib/engine-components/DropListener.js.map +1 -1
- package/lib/engine-components/Light.d.ts +102 -5
- package/lib/engine-components/Light.js +102 -44
- package/lib/engine-components/Light.js.map +1 -1
- package/lib/engine-components/NeedleMenu.d.ts +28 -11
- package/lib/engine-components/NeedleMenu.js +28 -11
- package/lib/engine-components/NeedleMenu.js.map +1 -1
- package/lib/engine-components/Networking.d.ts +37 -5
- package/lib/engine-components/Networking.js +37 -5
- package/lib/engine-components/Networking.js.map +1 -1
- package/lib/engine-components/SceneSwitcher.js +44 -0
- package/lib/engine-components/SceneSwitcher.js.map +1 -1
- package/lib/engine-components/SpatialTrigger.d.ts +66 -1
- package/lib/engine-components/SpatialTrigger.js +74 -2
- package/lib/engine-components/SpatialTrigger.js.map +1 -1
- package/lib/engine-components/SpectatorCamera.d.ts +66 -4
- package/lib/engine-components/SpectatorCamera.js +132 -6
- package/lib/engine-components/SpectatorCamera.js.map +1 -1
- package/lib/engine-components/SyncedTransform.d.ts +45 -6
- package/lib/engine-components/SyncedTransform.js +45 -6
- package/lib/engine-components/SyncedTransform.js.map +1 -1
- package/lib/engine-components/TransformGizmo.d.ts +49 -3
- package/lib/engine-components/TransformGizmo.js +49 -3
- package/lib/engine-components/TransformGizmo.js.map +1 -1
- package/lib/engine-components/webxr/WebXR.d.ts +131 -22
- package/lib/engine-components/webxr/WebXR.js +132 -23
- package/lib/engine-components/webxr/WebXR.js.map +1 -1
- package/lib/engine-components-experimental/networking/PlayerSync.d.ts +82 -9
- package/lib/engine-components-experimental/networking/PlayerSync.js +76 -11
- package/lib/engine-components-experimental/networking/PlayerSync.js.map +1 -1
- package/package.json +1 -1
- package/src/engine/engine_types.ts +179 -18
- package/src/engine-components/Animator.ts +142 -22
- package/src/engine-components/AnimatorController.ts +184 -34
- package/src/engine-components/AudioListener.ts +16 -5
- package/src/engine-components/AudioSource.ts +126 -37
- package/src/engine-components/AvatarLoader.ts +61 -2
- package/src/engine-components/AxesHelper.ts +21 -1
- package/src/engine-components/BoxHelperComponent.ts +26 -0
- package/src/engine-components/Camera.ts +147 -41
- package/src/engine-components/CameraUtils.ts +20 -0
- package/src/engine-components/Collider.ts +102 -27
- package/src/engine-components/Component.ts +605 -129
- package/src/engine-components/DragControls.ts +134 -38
- package/src/engine-components/DropListener.ts +143 -23
- package/src/engine-components/Light.ts +105 -44
- package/src/engine-components/NeedleMenu.ts +29 -11
- package/src/engine-components/Networking.ts +37 -6
- package/src/engine-components/SceneSwitcher.ts +48 -1
- package/src/engine-components/SpatialTrigger.ts +80 -3
- package/src/engine-components/SpectatorCamera.ts +136 -18
- package/src/engine-components/SyncedTransform.ts +50 -7
- package/src/engine-components/TransformGizmo.ts +49 -4
- package/src/engine-components/webxr/WebXR.ts +144 -27
- package/src/engine-components-experimental/networking/PlayerSync.ts +85 -13
|
@@ -31,74 +31,127 @@ const debugQuicklook = getParam("debugusdz");
|
|
|
31
31
|
export class WebXR extends Behaviour {
|
|
32
32
|
|
|
33
33
|
// UI
|
|
34
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* When enabled, a button will be automatically added to {@link NeedleMenu} that allows users to enter VR mode.
|
|
36
|
+
*/
|
|
35
37
|
@serializable()
|
|
36
38
|
createVRButton: boolean = true;
|
|
37
|
-
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* When enabled, a button will be automatically added to {@link NeedleMenu} that allows users to enter AR mode.
|
|
42
|
+
*/
|
|
38
43
|
@serializable()
|
|
39
44
|
createARButton: boolean = true;
|
|
40
|
-
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* When enabled, a button to send the experience to an Oculus Quest will be shown if the current device does not support VR.
|
|
48
|
+
* This helps direct users to compatible devices for optimal VR experiences.
|
|
49
|
+
*/
|
|
41
50
|
@serializable()
|
|
42
51
|
createSendToQuestButton: boolean = true;
|
|
43
|
-
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* When enabled, a QR code will be generated and displayed on desktop devices to allow easy opening of the experience on mobile devices.
|
|
55
|
+
*/
|
|
44
56
|
@serializable()
|
|
45
57
|
createQRCode: boolean = true;
|
|
46
58
|
|
|
47
59
|
// VR Settings
|
|
48
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* When enabled, default movement controls will be automatically added to the scene when entering VR.
|
|
62
|
+
* This includes teleportation and smooth locomotion options for VR controllers.
|
|
63
|
+
*/
|
|
49
64
|
@serializable()
|
|
50
65
|
useDefaultControls: boolean = true;
|
|
51
|
-
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* When enabled, 3D models representing the user's VR controllers will be automatically created and rendered in the scene.
|
|
69
|
+
*/
|
|
52
70
|
@serializable()
|
|
53
71
|
showControllerModels: boolean = true;
|
|
54
|
-
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* When enabled, 3D models representing the user's hands will be automatically created and rendered when hand tracking is available.
|
|
75
|
+
*/
|
|
55
76
|
@serializable()
|
|
56
77
|
showHandModels: boolean = true;
|
|
57
78
|
|
|
58
79
|
// AR Settings
|
|
59
|
-
/**
|
|
80
|
+
/**
|
|
81
|
+
* When enabled, a reticle will be displayed to help place the scene in AR. The user must tap on a detected surface to position the scene.
|
|
82
|
+
*/
|
|
60
83
|
@serializable()
|
|
61
84
|
usePlacementReticle: boolean = true;
|
|
62
|
-
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Optional custom 3D object to use as the AR placement reticle instead of the default one.
|
|
88
|
+
*/
|
|
63
89
|
@serializable(AssetReference)
|
|
64
90
|
customARPlacementReticle?: AssetReference;
|
|
65
|
-
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* When enabled, users can adjust the position, rotation, and scale of the AR scene with one or two fingers after initial placement.
|
|
94
|
+
*/
|
|
66
95
|
@serializable()
|
|
67
96
|
usePlacementAdjustment: boolean = true;
|
|
68
|
-
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Determines the scale of the user relative to the scene in AR. Larger values make the 3D content appear smaller.
|
|
100
|
+
* Only applies when `usePlacementReticle` is enabled.
|
|
101
|
+
*/
|
|
69
102
|
@serializable()
|
|
70
103
|
arScale: number = 1;
|
|
71
|
-
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* When enabled, an XRAnchor will be created for the AR scene and its position will be regularly updated to match the anchor.
|
|
107
|
+
* This can help with spatial persistence in AR experiences.
|
|
108
|
+
* @experimental
|
|
109
|
+
*/
|
|
72
110
|
@serializable()
|
|
73
111
|
useXRAnchor: boolean = false;
|
|
112
|
+
|
|
74
113
|
/**
|
|
75
|
-
* When enabled the scene will be placed
|
|
114
|
+
* When enabled, the scene will be automatically placed as soon as a suitable surface is detected in AR,
|
|
115
|
+
* without requiring the user to tap to confirm placement.
|
|
76
116
|
*/
|
|
77
117
|
@serializable()
|
|
78
|
-
autoPlace: boolean =
|
|
79
|
-
|
|
118
|
+
autoPlace: boolean = false;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* When enabled, the AR session root center will be automatically adjusted to place the center of the scene.
|
|
122
|
+
* This helps ensure the scene is properly aligned with detected surfaces.
|
|
123
|
+
*/
|
|
80
124
|
@serializable()
|
|
81
125
|
autoCenter: boolean = false;
|
|
82
126
|
|
|
83
|
-
/**
|
|
127
|
+
/**
|
|
128
|
+
* When enabled, a USDZExporter component will be automatically added to the scene if none is found.
|
|
129
|
+
* This allows iOS and visionOS devices to view 3D content using Apple's AR QuickLook.
|
|
130
|
+
*/
|
|
84
131
|
@serializable()
|
|
85
132
|
useQuicklookExport: boolean = false;
|
|
86
133
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
*
|
|
134
|
+
/**
|
|
135
|
+
* When enabled, the 'depth-sensing' WebXR feature will be requested to provide real-time depth occlusion.
|
|
136
|
+
* Currently only supported on Oculus Quest devices.
|
|
137
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/XRDepthInformation
|
|
138
|
+
* @experimental
|
|
90
139
|
*/
|
|
91
140
|
@serializable()
|
|
92
141
|
useDepthSensing: boolean = false;
|
|
93
142
|
|
|
94
143
|
/**
|
|
95
|
-
* When enabled
|
|
144
|
+
* When enabled, a {@link SpatialGrabRaycaster} will be added or enabled in the scene,
|
|
145
|
+
* allowing users to interact with objects at a distance in VR/AR.
|
|
96
146
|
* @default true
|
|
97
147
|
*/
|
|
98
148
|
@serializable()
|
|
99
149
|
useSpatialGrab: boolean = true;
|
|
100
150
|
|
|
101
|
-
/**
|
|
151
|
+
/**
|
|
152
|
+
* Specifies the avatar representation that will be created when entering a WebXR session.
|
|
153
|
+
* Can be a reference to a 3D model or a boolean to use the default avatar.
|
|
154
|
+
*/
|
|
102
155
|
@serializable(AssetReference)
|
|
103
156
|
defaultAvatar?: AssetReference | boolean;
|
|
104
157
|
|
|
@@ -111,10 +164,17 @@ export class WebXR extends Behaviour {
|
|
|
111
164
|
|
|
112
165
|
static activeWebXRComponent: WebXR | null = null;
|
|
113
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Initializes the WebXR component by obtaining the XR sync object for this context.
|
|
169
|
+
*/
|
|
114
170
|
awake() {
|
|
115
171
|
NeedleXRSession.getXRSync(this.context);
|
|
116
172
|
}
|
|
117
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Sets up the WebXR component when it's enabled. Checks for HTTPS connection,
|
|
176
|
+
* sets up USDZ export if enabled, creates UI buttons, and configures avatar settings.
|
|
177
|
+
*/
|
|
118
178
|
onEnable(): void {
|
|
119
179
|
// check if we're on a secure connection:
|
|
120
180
|
if (window.location.protocol !== "https:") {
|
|
@@ -153,11 +213,20 @@ export class WebXR extends Behaviour {
|
|
|
153
213
|
}
|
|
154
214
|
}
|
|
155
215
|
|
|
216
|
+
/**
|
|
217
|
+
* Cleans up resources when the component is disabled.
|
|
218
|
+
* Destroys the USDZ exporter if one was created and removes UI buttons.
|
|
219
|
+
*/
|
|
156
220
|
onDisable(): void {
|
|
157
221
|
this._usdzExporter?.destroy();
|
|
158
222
|
this.removeButtons();
|
|
159
223
|
}
|
|
160
224
|
|
|
225
|
+
/**
|
|
226
|
+
* Checks if WebXR is supported and offers an appropriate session.
|
|
227
|
+
* This is used to show the WebXR session joining prompt in browsers that support it.
|
|
228
|
+
* @returns A Promise that resolves to true if a session was offered, false otherwise
|
|
229
|
+
*/
|
|
161
230
|
private async handleOfferSession() {
|
|
162
231
|
if (this.createVRButton) {
|
|
163
232
|
const hasVRSupport = await NeedleXRSession.isVRSupported();
|
|
@@ -196,7 +265,7 @@ export class WebXR extends Behaviour {
|
|
|
196
265
|
NeedleXRSession.stop();
|
|
197
266
|
}
|
|
198
267
|
|
|
199
|
-
|
|
268
|
+
private _exitXRMenuButton?: HTMLElement;
|
|
200
269
|
private _previousXRState: number = 0;
|
|
201
270
|
private _spatialGrabRaycaster?: SpatialGrabRaycaster;
|
|
202
271
|
|
|
@@ -204,6 +273,11 @@ export class WebXR extends Behaviour {
|
|
|
204
273
|
return !WebXR.activeWebXRComponent || WebXR.activeWebXRComponent === this;
|
|
205
274
|
}
|
|
206
275
|
|
|
276
|
+
/**
|
|
277
|
+
* Called before entering a WebXR session. Sets up optional features like depth sensing, if needed.
|
|
278
|
+
* @param _mode The XR session mode being requested (immersive-ar or immersive-vr)
|
|
279
|
+
* @param args The XRSessionInit object that will be passed to the WebXR API
|
|
280
|
+
*/
|
|
207
281
|
onBeforeXR(_mode: XRSessionMode, args: XRSessionInit): void {
|
|
208
282
|
if (!this.isActiveWebXR) {
|
|
209
283
|
console.warn(`WebXR: another WebXR component is already active (${WebXR.activeWebXRComponent?.name}). This is ignored: ${this.name}`);
|
|
@@ -217,6 +291,11 @@ export class WebXR extends Behaviour {
|
|
|
217
291
|
}
|
|
218
292
|
}
|
|
219
293
|
|
|
294
|
+
/**
|
|
295
|
+
* Called when a WebXR session begins. Sets up the scene for XR by configuring controllers,
|
|
296
|
+
* AR placement, and other features based on component settings.
|
|
297
|
+
* @param args Event arguments containing information about the started XR session
|
|
298
|
+
*/
|
|
220
299
|
async onEnterXR(args: NeedleXREventArgs) {
|
|
221
300
|
if (!this.isActiveWebXR) return;
|
|
222
301
|
|
|
@@ -288,6 +367,11 @@ export class WebXR extends Behaviour {
|
|
|
288
367
|
}
|
|
289
368
|
}
|
|
290
369
|
|
|
370
|
+
/**
|
|
371
|
+
* Called every frame during an active WebXR session.
|
|
372
|
+
* Updates components that depend on the current XR state.
|
|
373
|
+
* @param _args Event arguments containing information about the current XR session frame
|
|
374
|
+
*/
|
|
291
375
|
onUpdateXR(_args: NeedleXREventArgs): void {
|
|
292
376
|
if (!this.isActiveWebXR) return;
|
|
293
377
|
if (this._spatialGrabRaycaster) {
|
|
@@ -295,6 +379,11 @@ export class WebXR extends Behaviour {
|
|
|
295
379
|
}
|
|
296
380
|
}
|
|
297
381
|
|
|
382
|
+
/**
|
|
383
|
+
* Called when a WebXR session ends. Restores pre-session state,
|
|
384
|
+
* removes temporary components, and cleans up resources.
|
|
385
|
+
* @param _ Event arguments containing information about the ended XR session
|
|
386
|
+
*/
|
|
298
387
|
onLeaveXR(_: NeedleXREventArgs): void {
|
|
299
388
|
this._exitXRMenuButton?.remove();
|
|
300
389
|
|
|
@@ -339,8 +428,10 @@ export class WebXR extends Behaviour {
|
|
|
339
428
|
return models;
|
|
340
429
|
}
|
|
341
430
|
|
|
342
|
-
|
|
343
|
-
|
|
431
|
+
/**
|
|
432
|
+
* Creates and instantiates the user's avatar representation in the WebXR session.
|
|
433
|
+
* @param xr The active session
|
|
434
|
+
*/
|
|
344
435
|
protected async createLocalAvatar(xr: NeedleXRSession) {
|
|
345
436
|
if (this._playerSync && xr.running && typeof this.defaultAvatar != "boolean") {
|
|
346
437
|
this._playerSync.asset = this.defaultAvatar;
|
|
@@ -348,6 +439,11 @@ export class WebXR extends Behaviour {
|
|
|
348
439
|
}
|
|
349
440
|
}
|
|
350
441
|
|
|
442
|
+
/**
|
|
443
|
+
* Event handler called when a player avatar is spawned.
|
|
444
|
+
* Ensures the avatar has the necessary Avatar component.
|
|
445
|
+
* @param instance The spawned avatar 3D object
|
|
446
|
+
*/
|
|
351
447
|
private onAvatarSpawned = (instance: Object3D) => {
|
|
352
448
|
// spawned webxr avatars must have a avatar component
|
|
353
449
|
if (debug) console.log("WebXR.onAvatarSpawned", instance);
|
|
@@ -360,13 +456,16 @@ export class WebXR extends Behaviour {
|
|
|
360
456
|
|
|
361
457
|
// HTML UI
|
|
362
458
|
|
|
363
|
-
/** @deprecated use
|
|
459
|
+
/** @deprecated use {@link getButtonsFactory} or directly access {@link WebXRButtonFactory.getOrCreate} */
|
|
364
460
|
getButtonsContainer(): WebXRButtonFactory {
|
|
365
461
|
return this.getButtonsFactory();
|
|
366
462
|
}
|
|
367
463
|
|
|
368
|
-
/**
|
|
369
|
-
*
|
|
464
|
+
/**
|
|
465
|
+
* Returns the WebXR button factory, creating one if it doesn't exist.
|
|
466
|
+
* Use this to access and modify WebXR UI buttons.
|
|
467
|
+
* @returns The WebXRButtonFactory instance
|
|
468
|
+
*/
|
|
370
469
|
getButtonsFactory(): WebXRButtonFactory {
|
|
371
470
|
if (!this._buttonFactory) {
|
|
372
471
|
this._buttonFactory = WebXRButtonFactory.getOrCreate();
|
|
@@ -374,8 +473,15 @@ export class WebXR extends Behaviour {
|
|
|
374
473
|
return this._buttonFactory;
|
|
375
474
|
}
|
|
376
475
|
|
|
476
|
+
/**
|
|
477
|
+
* Reference to the WebXR button factory used by this component.
|
|
478
|
+
*/
|
|
377
479
|
private _buttonFactory?: WebXRButtonFactory;
|
|
378
480
|
|
|
481
|
+
/**
|
|
482
|
+
* Creates and sets up UI elements for WebXR interaction based on component settings
|
|
483
|
+
* and device capabilities. Handles creating AR, VR, QuickLook buttons and utility buttons like QR codes.
|
|
484
|
+
*/
|
|
379
485
|
private handleCreatingHTML() {
|
|
380
486
|
const xrButtonsPriority = 50;
|
|
381
487
|
|
|
@@ -423,14 +529,25 @@ export class WebXR extends Behaviour {
|
|
|
423
529
|
}
|
|
424
530
|
}
|
|
425
531
|
|
|
532
|
+
/**
|
|
533
|
+
* Storage for UI buttons created by this component.
|
|
534
|
+
*/
|
|
426
535
|
private readonly _buttons: HTMLElement[] = [];
|
|
427
536
|
|
|
537
|
+
/**
|
|
538
|
+
* Adds a button to the UI with the specified priority.
|
|
539
|
+
* @param button The HTML element to add
|
|
540
|
+
* @param priority The button's priority value (lower numbers appear first)
|
|
541
|
+
*/
|
|
428
542
|
private addButton(button: HTMLElement, priority: number) {
|
|
429
543
|
this._buttons.push(button);
|
|
430
544
|
button.setAttribute("priority", priority.toString());
|
|
431
545
|
this.context.menu.appendChild(button);
|
|
432
546
|
}
|
|
433
547
|
|
|
548
|
+
/**
|
|
549
|
+
* Removes all buttons created by this component from the UI.
|
|
550
|
+
*/
|
|
434
551
|
private removeButtons() {
|
|
435
552
|
for (const button of this._buttons) {
|
|
436
553
|
button.remove();
|
|
@@ -12,6 +12,7 @@ import { EventList } from "../../engine-components/EventList.js";
|
|
|
12
12
|
|
|
13
13
|
const debug = getParam("debugplayersync");
|
|
14
14
|
|
|
15
|
+
/** Type definition for a PlayerSync instance with a guaranteed asset property */
|
|
15
16
|
declare type PlayerSyncWithAsset = PlayerSync & Required<Pick<PlayerSync, "asset">>;
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -23,7 +24,10 @@ export class PlayerSync extends Behaviour {
|
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* This API is experimental and may change or be removed in the future.
|
|
26
|
-
*
|
|
27
|
+
* Creates a PlayerSync instance at runtime from a given URL and sets it up for networking
|
|
28
|
+
* @param url Path to the asset that should be instantiated for each player
|
|
29
|
+
* @param init Optional initialization parameters for the PlayerSync component
|
|
30
|
+
* @returns Promise resolving to a PlayerSync instance with a guaranteed asset property
|
|
27
31
|
* @example
|
|
28
32
|
* ```typescript
|
|
29
33
|
* const res = await PlayerSync.setupFrom("/assets/demo.glb");
|
|
@@ -47,15 +51,22 @@ export class PlayerSync extends Behaviour {
|
|
|
47
51
|
return ps as PlayerSyncWithAsset;
|
|
48
52
|
}
|
|
49
53
|
|
|
50
|
-
/**
|
|
54
|
+
/**
|
|
55
|
+
* When enabled, PlayerSync will automatically load and instantiate the assigned asset when joining a networked room
|
|
56
|
+
*/
|
|
51
57
|
@serializable()
|
|
52
58
|
autoSync: boolean = true;
|
|
53
59
|
|
|
54
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* The asset that will be loaded and instantiated when PlayerSync becomes active and joins a networked room
|
|
62
|
+
*/
|
|
55
63
|
@serializable(AssetReference)
|
|
56
64
|
asset?: AssetReference;
|
|
57
65
|
|
|
58
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* Event invoked when a player instance is spawned with the spawned {@link Object3D} as parameter
|
|
68
|
+
* @serializable
|
|
69
|
+
*/
|
|
59
70
|
@serializable(EventList)
|
|
60
71
|
onPlayerSpawned?: EventList<Object3D>;
|
|
61
72
|
|
|
@@ -86,6 +97,10 @@ export class PlayerSync extends Behaviour {
|
|
|
86
97
|
if (this.autoSync) this.getInstance();
|
|
87
98
|
}
|
|
88
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Gets or creates an instance of the assigned asset for the local player
|
|
102
|
+
* @returns Promise resolving to the instantiated player object or null if creation failed
|
|
103
|
+
*/
|
|
89
104
|
async getInstance() {
|
|
90
105
|
if (this._localInstance) return this._localInstance;
|
|
91
106
|
|
|
@@ -123,6 +138,9 @@ export class PlayerSync extends Behaviour {
|
|
|
123
138
|
return this._localInstance;
|
|
124
139
|
}
|
|
125
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Destroys the current player instance and cleans up networking state
|
|
143
|
+
*/
|
|
126
144
|
destroyInstance = () => {
|
|
127
145
|
this._localInstance?.then(go => {
|
|
128
146
|
if (debug) console.log("PlayerSync.destroyInstance", go);
|
|
@@ -131,8 +149,9 @@ export class PlayerSync extends Behaviour {
|
|
|
131
149
|
this._localInstance = undefined;
|
|
132
150
|
}
|
|
133
151
|
|
|
134
|
-
|
|
135
|
-
|
|
152
|
+
/**
|
|
153
|
+
* Sets up visibility change listeners to handle player cleanup when browser tab visibility changes
|
|
154
|
+
*/
|
|
136
155
|
private watchTabVisible() {
|
|
137
156
|
window.addEventListener("visibilitychange", _ => {
|
|
138
157
|
if (document.visibilityState === "visible") {
|
|
@@ -147,32 +166,50 @@ export class PlayerSync extends Behaviour {
|
|
|
147
166
|
}
|
|
148
167
|
}
|
|
149
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Enum defining events that can be triggered by PlayerState
|
|
171
|
+
*/
|
|
150
172
|
export enum PlayerStateEvent {
|
|
173
|
+
/** Triggered when a PlayerState's owner property changes */
|
|
151
174
|
OwnerChanged = "ownerChanged",
|
|
152
175
|
}
|
|
153
176
|
|
|
177
|
+
/** Arguments passed when a PlayerState's owner changes */
|
|
154
178
|
export declare interface PlayerStateOwnerChangedArgs {
|
|
179
|
+
/** The PlayerState instance that changed */
|
|
155
180
|
playerState: PlayerState;
|
|
181
|
+
/** Previous owner's connection ID */
|
|
156
182
|
oldValue: string;
|
|
183
|
+
/** New owner's connection ID */
|
|
157
184
|
newValue: string;
|
|
158
185
|
}
|
|
159
186
|
|
|
187
|
+
/** Callback type for PlayerState events */
|
|
160
188
|
export declare type PlayerStateEventCallback = (args: CustomEvent<PlayerStateOwnerChangedArgs>) => void;
|
|
161
189
|
|
|
190
|
+
/**
|
|
191
|
+
* Represents a player instance in the networked environment.
|
|
192
|
+
* Handles ownership, synchronization, and lifecycle management of player objects.
|
|
193
|
+
*/
|
|
162
194
|
export class PlayerState extends Behaviour {
|
|
163
195
|
|
|
164
196
|
private static _all: PlayerState[] = [];
|
|
165
|
-
/**
|
|
197
|
+
/** All PlayerState instances for all players in the scene */
|
|
166
198
|
static get all(): PlayerState[] {
|
|
167
199
|
return PlayerState._all;
|
|
168
200
|
};
|
|
169
201
|
|
|
170
202
|
private static _local: PlayerState[] = [];
|
|
171
|
-
/**
|
|
203
|
+
/** All PlayerState instances that belong to the local player */
|
|
172
204
|
static get local(): PlayerState[] {
|
|
173
205
|
return PlayerState._local;
|
|
174
206
|
}
|
|
175
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Gets the PlayerState component for a given object or component
|
|
210
|
+
* @param obj Object3D or Component to find the PlayerState for
|
|
211
|
+
* @returns The PlayerState component if found, undefined otherwise
|
|
212
|
+
*/
|
|
176
213
|
static getFor(obj: Object3D | Component) {
|
|
177
214
|
if (obj instanceof Object3D) {
|
|
178
215
|
return GameObject.getComponentInParent(obj, PlayerState);
|
|
@@ -183,22 +220,34 @@ export class PlayerState extends Behaviour {
|
|
|
183
220
|
return undefined;
|
|
184
221
|
}
|
|
185
222
|
|
|
186
|
-
|
|
223
|
+
/**
|
|
224
|
+
* Checks if a given object or component belongs to the local player
|
|
225
|
+
* @param obj Object3D or Component to check
|
|
226
|
+
* @returns True if the object belongs to the local player, false otherwise
|
|
227
|
+
*/
|
|
187
228
|
static isLocalPlayer(obj: Object3D | Component): boolean {
|
|
188
229
|
const state = PlayerState.getFor(obj);
|
|
189
230
|
return state?.isLocalPlayer ?? false;
|
|
190
231
|
}
|
|
191
232
|
|
|
192
|
-
// static Callback
|
|
193
233
|
private static _callbacks: { [key: string]: PlayerStateEventCallback[] } = {};
|
|
194
234
|
/**
|
|
195
|
-
*
|
|
235
|
+
* Registers a callback for a specific PlayerState event
|
|
236
|
+
* @param event The event to listen for
|
|
237
|
+
* @param cb Callback function that will be invoked when the event occurs
|
|
238
|
+
* @returns The provided callback function for chaining
|
|
196
239
|
*/
|
|
197
240
|
static addEventListener(event: PlayerStateEvent, cb: PlayerStateEventCallback) {
|
|
198
241
|
if (!this._callbacks[event]) this._callbacks[event] = [];
|
|
199
242
|
this._callbacks[event].push(cb);
|
|
200
243
|
return cb;
|
|
201
244
|
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Removes a previously registered event callback
|
|
248
|
+
* @param event The event type to remove the callback from
|
|
249
|
+
* @param cb The callback function to remove
|
|
250
|
+
*/
|
|
202
251
|
static removeEventListener(event: PlayerStateEvent, cb: PlayerStateEventCallback) {
|
|
203
252
|
if (!this._callbacks[event]) return;
|
|
204
253
|
const index = this._callbacks[event].indexOf(cb);
|
|
@@ -212,20 +261,39 @@ export class PlayerState extends Behaviour {
|
|
|
212
261
|
}
|
|
213
262
|
|
|
214
263
|
|
|
264
|
+
/** Event triggered when the owner of this PlayerState changes */
|
|
215
265
|
public onOwnerChangeEvent = new EventList();
|
|
266
|
+
|
|
267
|
+
/** Event triggered the first time an owner is assigned to this PlayerState */
|
|
216
268
|
public onFirstOwnerChangeEvent = new EventList();
|
|
269
|
+
|
|
270
|
+
/** Indicates if this PlayerState has an owner assigned */
|
|
217
271
|
public hasOwner = false;
|
|
218
272
|
|
|
273
|
+
/**
|
|
274
|
+
* The connection ID of the player who owns this PlayerState instance
|
|
275
|
+
* @syncField Synchronized across the network
|
|
276
|
+
*/
|
|
219
277
|
@syncField(PlayerState.prototype.onOwnerChange)
|
|
220
278
|
owner?: string;
|
|
221
279
|
|
|
222
|
-
/**
|
|
280
|
+
/**
|
|
281
|
+
* When enabled, PlayerState will not destroy itself when the owner is not connected anymore
|
|
282
|
+
*/
|
|
223
283
|
dontDestroy: boolean = false;
|
|
224
284
|
|
|
285
|
+
/**
|
|
286
|
+
* Indicates if this PlayerState belongs to the local player
|
|
287
|
+
*/
|
|
225
288
|
get isLocalPlayer(): boolean {
|
|
226
289
|
return this.owner === this.context.connection.connectionId;
|
|
227
290
|
}
|
|
228
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Handles owner change events and updates relevant state
|
|
294
|
+
* @param newOwner The new owner's connection ID
|
|
295
|
+
* @param oldOwner The previous owner's connection ID
|
|
296
|
+
*/
|
|
229
297
|
private onOwnerChange(newOwner: string, oldOwner: string) {
|
|
230
298
|
if (debug) console.log(`PlayerSync.onOwnerChange: ${oldOwner} → ${newOwner} (me: ${this.context.connection.connectionId})`);
|
|
231
299
|
|
|
@@ -300,7 +368,7 @@ export class PlayerState extends Behaviour {
|
|
|
300
368
|
}
|
|
301
369
|
}
|
|
302
370
|
|
|
303
|
-
/**
|
|
371
|
+
/** Tells the server that this client has been destroyed, and the networking message for the instantiate will be removed */
|
|
304
372
|
doDestroy() {
|
|
305
373
|
if (debug) console.log("PlayerSync.doDestroy → syncDestroy", this.name);
|
|
306
374
|
syncDestroy(this.gameObject, this.context.connection, true, { saveInRoom: false });
|
|
@@ -319,6 +387,10 @@ export class PlayerState extends Behaviour {
|
|
|
319
387
|
}
|
|
320
388
|
}
|
|
321
389
|
|
|
390
|
+
/**
|
|
391
|
+
* Handler for when a user leaves the networked room
|
|
392
|
+
* @param model Object containing the ID of the user who left
|
|
393
|
+
*/
|
|
322
394
|
private onUserLeftRoom = (model: { userId: string }) => {
|
|
323
395
|
if (model.userId === this.owner) {
|
|
324
396
|
if (debug)
|