@needle-tools/engine 4.2.5 → 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.
Files changed (121) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/components.needle.json +1 -1
  3. package/dist/needle-engine.bundle.js +4245 -2977
  4. package/dist/needle-engine.bundle.light.js +4236 -2968
  5. package/dist/needle-engine.bundle.light.min.js +73 -73
  6. package/dist/needle-engine.bundle.light.umd.cjs +61 -61
  7. package/dist/needle-engine.bundle.min.js +62 -62
  8. package/dist/needle-engine.bundle.umd.cjs +61 -61
  9. package/dist/needle-engine.light.d.ts +9 -9
  10. package/dist/three-examples.js +825 -794
  11. package/dist/three-examples.light.js +825 -794
  12. package/dist/three-examples.light.min.js +12 -12
  13. package/dist/three-examples.light.umd.cjs +10 -10
  14. package/dist/three-examples.min.js +12 -12
  15. package/dist/three-examples.umd.cjs +10 -10
  16. package/lib/engine/engine_addressables.d.ts +3 -0
  17. package/lib/engine/engine_addressables.js +18 -0
  18. package/lib/engine/engine_addressables.js.map +1 -1
  19. package/lib/engine/engine_input.d.ts +20 -1
  20. package/lib/engine/engine_input.js.map +1 -1
  21. package/lib/engine/engine_types.d.ts +162 -17
  22. package/lib/engine-components/Animator.d.ts +129 -21
  23. package/lib/engine-components/Animator.js +115 -21
  24. package/lib/engine-components/Animator.js.map +1 -1
  25. package/lib/engine-components/AnimatorController.d.ts +161 -32
  26. package/lib/engine-components/AnimatorController.js +176 -29
  27. package/lib/engine-components/AnimatorController.js.map +1 -1
  28. package/lib/engine-components/AudioListener.d.ts +16 -5
  29. package/lib/engine-components/AudioListener.js +16 -5
  30. package/lib/engine-components/AudioListener.js.map +1 -1
  31. package/lib/engine-components/AudioSource.d.ts +120 -28
  32. package/lib/engine-components/AudioSource.js +120 -37
  33. package/lib/engine-components/AudioSource.js.map +1 -1
  34. package/lib/engine-components/AvatarLoader.d.ts +61 -0
  35. package/lib/engine-components/AvatarLoader.js +61 -1
  36. package/lib/engine-components/AvatarLoader.js.map +1 -1
  37. package/lib/engine-components/AxesHelper.d.ts +19 -1
  38. package/lib/engine-components/AxesHelper.js +19 -1
  39. package/lib/engine-components/AxesHelper.js.map +1 -1
  40. package/lib/engine-components/BoxHelperComponent.d.ts +26 -0
  41. package/lib/engine-components/BoxHelperComponent.js +26 -0
  42. package/lib/engine-components/BoxHelperComponent.js.map +1 -1
  43. package/lib/engine-components/Camera.d.ts +126 -37
  44. package/lib/engine-components/Camera.js +139 -37
  45. package/lib/engine-components/Camera.js.map +1 -1
  46. package/lib/engine-components/CameraUtils.js +20 -0
  47. package/lib/engine-components/CameraUtils.js.map +1 -1
  48. package/lib/engine-components/Collider.d.ts +95 -21
  49. package/lib/engine-components/Collider.js +100 -23
  50. package/lib/engine-components/Collider.js.map +1 -1
  51. package/lib/engine-components/Component.d.ts +554 -106
  52. package/lib/engine-components/Component.js +352 -81
  53. package/lib/engine-components/Component.js.map +1 -1
  54. package/lib/engine-components/DragControls.d.ts +95 -21
  55. package/lib/engine-components/DragControls.js +126 -32
  56. package/lib/engine-components/DragControls.js.map +1 -1
  57. package/lib/engine-components/DropListener.d.ts +99 -16
  58. package/lib/engine-components/DropListener.js +119 -14
  59. package/lib/engine-components/DropListener.js.map +1 -1
  60. package/lib/engine-components/Light.d.ts +102 -5
  61. package/lib/engine-components/Light.js +102 -44
  62. package/lib/engine-components/Light.js.map +1 -1
  63. package/lib/engine-components/NeedleMenu.d.ts +28 -11
  64. package/lib/engine-components/NeedleMenu.js +28 -11
  65. package/lib/engine-components/NeedleMenu.js.map +1 -1
  66. package/lib/engine-components/Networking.d.ts +37 -5
  67. package/lib/engine-components/Networking.js +37 -5
  68. package/lib/engine-components/Networking.js.map +1 -1
  69. package/lib/engine-components/SceneSwitcher.d.ts +8 -0
  70. package/lib/engine-components/SceneSwitcher.js +72 -8
  71. package/lib/engine-components/SceneSwitcher.js.map +1 -1
  72. package/lib/engine-components/SpatialTrigger.d.ts +66 -1
  73. package/lib/engine-components/SpatialTrigger.js +74 -2
  74. package/lib/engine-components/SpatialTrigger.js.map +1 -1
  75. package/lib/engine-components/SpectatorCamera.d.ts +66 -4
  76. package/lib/engine-components/SpectatorCamera.js +132 -6
  77. package/lib/engine-components/SpectatorCamera.js.map +1 -1
  78. package/lib/engine-components/SyncedTransform.d.ts +45 -6
  79. package/lib/engine-components/SyncedTransform.js +45 -6
  80. package/lib/engine-components/SyncedTransform.js.map +1 -1
  81. package/lib/engine-components/TransformGizmo.d.ts +49 -3
  82. package/lib/engine-components/TransformGizmo.js +49 -3
  83. package/lib/engine-components/TransformGizmo.js.map +1 -1
  84. package/lib/engine-components/ui/EventSystem.d.ts +1 -0
  85. package/lib/engine-components/ui/EventSystem.js +8 -5
  86. package/lib/engine-components/ui/EventSystem.js.map +1 -1
  87. package/lib/engine-components/webxr/WebXR.d.ts +131 -22
  88. package/lib/engine-components/webxr/WebXR.js +132 -23
  89. package/lib/engine-components/webxr/WebXR.js.map +1 -1
  90. package/lib/engine-components-experimental/networking/PlayerSync.d.ts +82 -9
  91. package/lib/engine-components-experimental/networking/PlayerSync.js +76 -11
  92. package/lib/engine-components-experimental/networking/PlayerSync.js.map +1 -1
  93. package/package.json +1 -1
  94. package/plugins/vite/alias.js +6 -3
  95. package/src/engine/engine_addressables.ts +21 -0
  96. package/src/engine/engine_input.ts +20 -1
  97. package/src/engine/engine_types.ts +179 -18
  98. package/src/engine-components/Animator.ts +142 -22
  99. package/src/engine-components/AnimatorController.ts +184 -34
  100. package/src/engine-components/AudioListener.ts +16 -5
  101. package/src/engine-components/AudioSource.ts +126 -37
  102. package/src/engine-components/AvatarLoader.ts +61 -2
  103. package/src/engine-components/AxesHelper.ts +21 -1
  104. package/src/engine-components/BoxHelperComponent.ts +26 -0
  105. package/src/engine-components/Camera.ts +147 -41
  106. package/src/engine-components/CameraUtils.ts +20 -0
  107. package/src/engine-components/Collider.ts +102 -27
  108. package/src/engine-components/Component.ts +605 -129
  109. package/src/engine-components/DragControls.ts +134 -38
  110. package/src/engine-components/DropListener.ts +143 -23
  111. package/src/engine-components/Light.ts +105 -44
  112. package/src/engine-components/NeedleMenu.ts +29 -11
  113. package/src/engine-components/Networking.ts +37 -6
  114. package/src/engine-components/SceneSwitcher.ts +78 -9
  115. package/src/engine-components/SpatialTrigger.ts +80 -3
  116. package/src/engine-components/SpectatorCamera.ts +136 -18
  117. package/src/engine-components/SyncedTransform.ts +50 -7
  118. package/src/engine-components/TransformGizmo.ts +49 -4
  119. package/src/engine-components/ui/EventSystem.ts +9 -7
  120. package/src/engine-components/webxr/WebXR.ts +144 -27
  121. package/src/engine-components-experimental/networking/PlayerSync.ts +85 -13
@@ -59,6 +59,7 @@ export class EventSystem extends Behaviour {
59
59
  return ctx.scene.getComponent(EventSystem);
60
60
  }
61
61
 
62
+ /** Get the currently active event system */
62
63
  static get instance(): EventSystem | null {
63
64
  return this.get(Context.Current);
64
65
  }
@@ -75,7 +76,10 @@ export class EventSystem extends Behaviour {
75
76
  }
76
77
  }
77
78
 
78
- get hasActiveUI() { return this.currentActiveMeshUIComponents.length > 0; }
79
+ get hasActiveUI() {
80
+ return this.currentActiveMeshUIComponents.length > 0;
81
+ }
82
+
79
83
  get isHoveringObjects() { return this.hoveredByID.size > 0; }
80
84
 
81
85
  awake(): void {
@@ -424,7 +428,6 @@ export class EventSystem extends Behaviour {
424
428
  const res = UIRaycastUtils.isInteractable(actualGo, this.out);
425
429
  if (!res) return false;
426
430
  canvasGroup = this.out.canvasGroup ?? null;
427
-
428
431
  const handled = this.handleMeshUIIntersection(object, pressedOrClicked);
429
432
  if (!clicked && handled) {
430
433
  // return true;
@@ -753,7 +756,6 @@ export class EventSystem extends Behaviour {
753
756
  }
754
757
 
755
758
  private resetMeshUIStates() {
756
-
757
759
  if (this.context.input.getPointerPressedCount() > 0) {
758
760
  MeshUIHelper.resetLastSelected();
759
761
  }
@@ -816,7 +818,7 @@ class MeshUIHelper {
816
818
  let foundBlock: Object3D | null = null;
817
819
 
818
820
  if (intersect) {
819
- foundBlock = this.findBlockInParent(intersect);
821
+ foundBlock = this.findBlockOrTextInParent(intersect);
820
822
  // console.log(intersect, "-- found block:", foundBlock)
821
823
  if (foundBlock && foundBlock !== this.lastSelected) {
822
824
  const interactable = foundBlock["interactable"];
@@ -840,13 +842,13 @@ class MeshUIHelper {
840
842
  this.needsUpdate = true;
841
843
  }
842
844
 
843
- static findBlockInParent(elem: any): Object3D | null {
845
+ static findBlockOrTextInParent(elem: any): Object3D | null {
844
846
  if (!elem) return null;
845
- if (elem.isBlock) {
847
+ if (elem.isBlock || (elem.isText)) {
846
848
  // @TODO : Replace states managements
847
849
  // if (Object.keys(elem.states).length > 0)
848
850
  return elem;
849
851
  }
850
- return this.findBlockInParent(elem.parent);
852
+ return this.findBlockOrTextInParent(elem.parent);
851
853
  }
852
854
  }
@@ -31,74 +31,127 @@ const debugQuicklook = getParam("debugusdz");
31
31
  export class WebXR extends Behaviour {
32
32
 
33
33
  // UI
34
- /** When enabled a button will be added to the UI to enter VR */
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
- /** When enabled a button will be added to the UI to enter AR */
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
- /** When enabled a send to quest button will be shown if the device does not support VR */
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
- /** When enabled a QRCode will be created to open the website on a mobile device */
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
- /** When enabled default movement behaviour will be added */
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
- /** When enabled controller models will automatically be created and updated when you are using controllers in WebXR */
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
- /** When enabled hand models will automatically be created and updated when you are using hands in WebXR */
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
- /** When enabled the scene must be placed in AR */
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
- /** When assigned this object will be used as the AR placement reticle */
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
- /** When enabled you can position, rotate or scale your AR scene with one or two fingers */
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
- /** Used when `usePlacementReticle` is enabled. This is the scale of the user in the scene in AR. Larger values make the 3D content appear smaller */
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
- /** Experimental: When enabled an XRAnchor will be created for the AR scene and the position will be updated to the anchor position every few frames */
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 automatically when a point in the real world is found
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 = true;
79
- /** When enabled the AR session root center will be automatically adjusted to place the center of the scene */
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
- /** When enabled a USDZExporter component will be added to the scene (if none is found) */
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
- /** Preview feature enabling occlusion (when available: https://github.com/cabanier/three.js/commit/b6ee92bcd8f20718c186120b7f19a3b68a1d4e47)
89
- * Enables the 'depth-sensing' WebXR feature to provide realtime depth occlusion. Only supported on Oculus Quest right now.
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 the spatial grab raycaster will be added or enabled in the scene
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
- /** This avatar representation will be spawned when you enter a webxr session */
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
- private _exitXRMenuButton?: HTMLElement;
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 `getButtonsFactory()` or access `WebXRButtonFactory.getOrCreate()` directory */
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
- /** Calling this function will get the Needle WebXR button factory (it will be created if it doesnt exist yet)
369
- * @returns the Needle WebXR button factory */
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
- * Create a PlayerSync instance at runtime from a given URL
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
- /** when enabled PlayerSync will automatically load and instantiate the assigned asset when joining a networked room */
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
- /** This asset will be loaded and instantiated when PlayerSync becomes active and joins a networked room */
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
- /** Event called when an instance is spawned */
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
- /** all instances for all players */
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
- /** all instances for the local player */
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
- //** use to check if a component or gameobject is part of a instance owned by the local player */
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
- * Add a callback for a PlayerStateEvent
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
- /** when enabled PlayerSync will not destroy itself when not connected anymore */
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
- /** this tells the server that this client has been destroyed and the networking message for the instantiate will be removed */
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)