@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
@@ -4,50 +4,68 @@ import { DeviceUtilities } from '../engine/engine_utils.js';
4
4
  import { Behaviour } from './Component.js';
5
5
 
6
6
  /**
7
- * Exposes options to customize the built-in Needle Menu.
7
+ * Provides configuration options for the built-in Needle Menu.
8
+ * Needle Menu uses HTML in 2D mode, and automatically switches to a 3D menu in VR/AR mode.
9
+ *
10
+ * Controls display options, button visibility, and menu positioning.
8
11
  * From code, you can access the menu via {@link Context.menu}.
9
12
  * @category User Interface
10
13
  * @group Components
11
14
  **/
12
15
  export class NeedleMenu extends Behaviour {
13
16
 
17
+ /**
18
+ * Determines the vertical positioning of the menu on the screen
19
+ */
14
20
  @serializable()
15
21
  position: "top" | "bottom" = "bottom";
16
22
 
17
- /** Show the Needle logo in the menu (requires PRO license) */
23
+ /**
24
+ * Controls the visibility of the Needle logo in the menu (requires PRO license)
25
+ */
18
26
  @serializable()
19
27
  showNeedleLogo: boolean = true;
20
28
 
21
- /** When enabled the menu will also be visible in VR/AR when you look up
29
+ /**
30
+ * When enabled, displays the menu in VR/AR mode when the user looks up
22
31
  * @default undefined
23
- */
32
+ */
24
33
  @serializable()
25
34
  showSpatialMenu?: boolean;
26
35
 
27
- /** When enabled a button to enter fullscreen will be added to the menu
36
+ /**
37
+ * When enabled, adds a fullscreen toggle button to the menu
28
38
  * @default undefined
29
- */
39
+ */
30
40
  @serializable()
31
41
  createFullscreenButton?: boolean;
32
- /** When enabled a button to mute the application will be added to the menu
42
+
43
+ /**
44
+ * When enabled, adds an audio mute/unmute button to the menu
33
45
  * @default undefined
34
- */
46
+ */
35
47
  @serializable()
36
48
  createMuteButton?: boolean;
37
49
 
38
50
  /**
39
- * When enabled a button to show a QR code will be added to the menu.
51
+ * When enabled, adds a button to display a QR code for sharing the application.
52
+ * The QR code is only displayed on desktop devices.
40
53
  * @default undefined
41
54
  */
42
55
  @serializable()
43
56
  createQRCodeButton?: boolean;
44
57
 
45
- /** @hidden */
58
+ /**
59
+ * Applies the configured menu options when the component is enabled
60
+ * @hidden
61
+ */
46
62
  onEnable() {
47
63
  this.applyOptions();
48
64
  }
49
65
 
50
- /** applies the options to `this.context.menu` */
66
+ /**
67
+ * Applies all configured options to the active {@link Context.menu}.
68
+ */
51
69
  applyOptions() {
52
70
  this.context.menu.setPosition(this.position);
53
71
  this.context.menu.showNeedleLogo(this.showNeedleLogo);
@@ -7,21 +7,33 @@ import { Behaviour } from "./Component.js";
7
7
  const debug = getParam("debugnet");
8
8
 
9
9
  /**
10
- * The networking component is used to provide a websocket url to the networking system. It implements the {@link INetworkingWebsocketUrlProvider} interface.
10
+ * Provides configuration to the built-in networking system.
11
+ * This component supplies websocket URLs for establishing connections.
12
+ * It implements the {@link INetworkingWebsocketUrlProvider} interface.
13
+ *
11
14
  * @category Networking
12
15
  * @group Components
13
16
  */
14
17
  export class Networking extends Behaviour implements INetworkingWebsocketUrlProvider {
15
18
 
16
- /** The url that should be used for the websocket connection */
19
+ /**
20
+ * The websocket URL to connect to for networking functionality.
21
+ * Can be a complete URL or a relative path that will be resolved against the current origin.
22
+ */
17
23
  @serializable()
18
24
  url: string | null = null;
19
25
 
20
- /** The name of the url parameter that should be used to override the url. When set the url will be overridden by the url parameter e.g. when `urlParameterName=ws` `?ws=ws://localhost:8080` */
26
+ /**
27
+ * Name of the URL parameter that can override the websocket connection URL.
28
+ * When set, the URL will be overridden by the parameter value from the browser URL.
29
+ * For example, with `urlParameterName="ws"`, adding `?ws=ws://localhost:8080` to the browser URL will override the connection URL.
30
+ */
21
31
  @serializable()
22
32
  urlParameterName: string | null = null;
23
33
 
24
- /** Thie localhost url that should be used when the networking is running on a local network. This is useful when the server is running on the same machine as the client.
34
+ /**
35
+ * Alternative URL to use when running on a local network.
36
+ * This is particularly useful for development, when the server is running on the same machine as the client.
25
37
  */
26
38
  @serializable()
27
39
  localhost: string | null = null;
@@ -33,7 +45,13 @@ export class Networking extends Behaviour implements INetworkingWebsocketUrlProv
33
45
  this.context.connection.registerProvider(this);
34
46
  }
35
47
 
36
- /** @internal */
48
+ /**
49
+ * Determines the websocket URL to use for networking connections.
50
+ * Processes the configured URL, applying localhost fallbacks when appropriate and
51
+ * handling URL parameter overrides if specified.
52
+ * @returns The formatted websocket URL string or null if no valid URL could be determined
53
+ * @internal
54
+ */
37
55
  getWebsocketUrl(): string | null {
38
56
 
39
57
  let socketurl = this.url ? Networking.GetUrl(this.url, this.localhost) : null;
@@ -58,7 +76,13 @@ export class Networking extends Behaviour implements INetworkingWebsocketUrlProv
58
76
  return "wss://" + match?.groups["url"];
59
77
  }
60
78
 
61
-
79
+ /**
80
+ * Processes a URL string applying various transformations based on network environment.
81
+ * Handles relative paths and localhost fallbacks for local network environments.
82
+ * @param url The original URL to process
83
+ * @param localhostFallback Alternative URL to use when on a local network
84
+ * @returns The processed URL string or null/undefined if input was invalid
85
+ */
62
86
  public static GetUrl(url: string | null | undefined, localhostFallback?: string | null): string | null | undefined {
63
87
 
64
88
  let result = url;
@@ -78,6 +102,13 @@ export class Networking extends Behaviour implements INetworkingWebsocketUrlProv
78
102
  return result;
79
103
  }
80
104
 
105
+ /**
106
+ * Determines if the current connection is on a local network.
107
+ * Useful for applying different networking configurations in local development environments.
108
+ * This is the same as calling {@link isLocalNetwork}.
109
+ * @param hostname Optional hostname to check instead of the current window location
110
+ * @returns True if the connection is on a local network, false otherwise
111
+ */
81
112
  public static IsLocalNetwork(hostname = window.location.hostname) {
82
113
  return isLocalNetwork(hostname);
83
114
  }
@@ -46,6 +46,10 @@ export type LoadSceneEvent = {
46
46
  index: number;
47
47
  }
48
48
 
49
+ export type LoadSceneProgressEvent = LoadSceneEvent & {
50
+ progress: number,
51
+ }
52
+
49
53
  /**
50
54
  * The ISceneEventListener is called by the {@link SceneSwitcher} when a scene is loaded or unloaded.
51
55
  * It must be added to the root object of your scene (that is being loaded) or on the same object as the SceneSwitcher
@@ -225,6 +229,15 @@ export class SceneSwitcher extends Behaviour {
225
229
  */
226
230
  get currentlyLoadedScene() { return this._currentScene; }
227
231
 
232
+ /**
233
+ * Called when a scene starts loading
234
+ */
235
+ @serializable(EventList)
236
+ sceneLoadingStart: EventList<LoadSceneEvent> = new EventList();
237
+
238
+ @serializable(EventList)
239
+ sceneLoadingProgress: EventList<ProgressEvent> = new EventList();
240
+
228
241
  /**
229
242
  * The sceneLoaded event is called when a scene/glTF is loaded and added to the scene
230
243
  */
@@ -281,7 +294,7 @@ export class SceneSwitcher extends Behaviour {
281
294
  this._preloadScheduler.maxLoadAhead = this.preloadNext;
282
295
  this._preloadScheduler.maxLoadBehind = this.preloadPrevious;
283
296
  this._preloadScheduler.maxConcurrent = this.preloadConcurrent;
284
- this._preloadScheduler.begin();
297
+ this._preloadScheduler.begin(2000);
285
298
 
286
299
  // Begin loading the loading scene
287
300
  if (this.autoLoadFirstScene && this._currentIndex === -1 && !await this.tryLoadFromQueryParam()) {
@@ -602,11 +615,13 @@ export class SceneSwitcher extends Behaviour {
602
615
 
603
616
  const loadStartEvt = new CustomEvent<LoadSceneEvent>("loadscene-start", { detail: { scene: scene, switcher: this, index: index } })
604
617
  this.dispatchEvent(loadStartEvt);
618
+ this.sceneLoadingStart?.invoke(loadStartEvt.detail);
605
619
  await this.onStartLoading();
606
620
  // start loading and wait for the scene to be loaded
607
621
  await scene.loadAssetAsync((_, prog) => {
608
622
  this._currentLoadingProgress = prog;
609
623
  this.dispatchEvent(prog);
624
+ this.sceneLoadingProgress?.invoke(prog);
610
625
  }).catch(console.error);
611
626
  await this.onEndLoading();
612
627
  const finishedEvt = new CustomEvent<LoadSceneEvent>("loadscene-finished", { detail: { scene: scene, switcher: this, index: index } });
@@ -842,9 +857,18 @@ function sceneUriToName(uri: string): string {
842
857
 
843
858
 
844
859
 
860
+ /**
861
+ * PreLoadScheduler is responsible for managing preloading of scenes.
862
+ * It handles scheduling and limiting concurrent downloads of scenes based on specified parameters.
863
+ */
845
864
  class PreLoadScheduler {
865
+ /** Maximum number of scenes to preload ahead of the current scene */
846
866
  maxLoadAhead: number;
867
+
868
+ /** Maximum number of scenes to preload behind the current scene */
847
869
  maxLoadBehind: number;
870
+
871
+ /** Maximum number of scenes that can be preloaded concurrently */
848
872
  maxConcurrent: number;
849
873
 
850
874
  private _isRunning: boolean = false;
@@ -852,6 +876,13 @@ class PreLoadScheduler {
852
876
  private _loadTasks: LoadTask[] = [];
853
877
  private _maxConcurrentLoads: number = 1;
854
878
 
879
+ /**
880
+ * Creates a new PreLoadScheduler instance
881
+ * @param rooms The SceneSwitcher that this scheduler belongs to
882
+ * @param ahead Number of scenes to preload ahead of current scene
883
+ * @param behind Number of scenes to preload behind current scene
884
+ * @param maxConcurrent Maximum number of concurrent preloads allowed
885
+ */
855
886
  constructor(rooms: SceneSwitcher, ahead: number = 1, behind: number = 1, maxConcurrent: number = 2) {
856
887
  this._switcher = rooms;
857
888
  this.maxLoadAhead = ahead;
@@ -859,26 +890,34 @@ class PreLoadScheduler {
859
890
  this.maxConcurrent = maxConcurrent;
860
891
  }
861
892
 
862
- begin() {
893
+ /**
894
+ * Starts the preloading process after a specified delay
895
+ * @param delay Time in milliseconds to wait before starting preload
896
+ */
897
+ begin(delay: number) {
863
898
  if (this._isRunning) return;
864
- if (debug) console.log("Preload begin")
899
+ if (debug) console.log("Preload begin", { delay })
865
900
  this._isRunning = true;
866
- let lastRoom: number = -1;
901
+ let lastRoom: number = -10;
867
902
  let searchDistance: number;
868
903
  let searchCall: number;
869
904
  const array = this._switcher.scenes;
905
+ const startTime = Date.now() + delay;
870
906
  const interval = setInterval(() => {
871
907
  if (this.allLoaded()) {
872
908
  if (debug)
873
- console.log("All scenes loaded");
909
+ console.log("All scenes (pre-)loaded");
874
910
  this.stop();
875
911
  }
876
912
  if (!this._isRunning) {
877
913
  clearInterval(interval);
878
914
  return;
879
915
  }
916
+
917
+ if (Date.now() < startTime) return;
918
+
880
919
  if (this.canLoadNewScene() === false) return;
881
- if (lastRoom !== this._switcher.currentIndex) {
920
+ if (lastRoom === -10 || lastRoom !== this._switcher.currentIndex) {
882
921
  lastRoom = this._switcher.currentIndex;
883
922
  searchCall = 0;
884
923
  searchDistance = 0;
@@ -892,19 +931,33 @@ class PreLoadScheduler {
892
931
  if (roomIndex < 0) return;
893
932
  // if (roomIndex < 0) roomIndex = array.length + roomIndex;
894
933
  if (roomIndex < 0 || roomIndex >= array.length) return;
895
- const scene = array[roomIndex];
896
- new LoadTask(roomIndex, scene, this._loadTasks);
934
+ if (!this._loadTasks.some(t => t.index === roomIndex)) {
935
+ const scene = array[roomIndex];
936
+ if (debug) console.log("Preload scene", { roomIndex, searchForward, lastRoom, currentIndex: this._switcher.currentIndex, tasks: this._loadTasks.length }, scene?.url);
937
+ new LoadTask(roomIndex, scene, this._loadTasks);
938
+ }
897
939
  }, 200);
898
940
  }
899
941
 
942
+ /**
943
+ * Stops the preloading process
944
+ */
900
945
  stop() {
901
946
  this._isRunning = false;
902
947
  }
903
948
 
949
+ /**
950
+ * Checks if a new scene can be loaded based on current load limits
951
+ * @returns True if a new scene can be loaded, false otherwise
952
+ */
904
953
  canLoadNewScene(): boolean {
905
954
  return this._loadTasks.length < this._maxConcurrentLoads;
906
955
  }
907
956
 
957
+ /**
958
+ * Checks if all scenes in the SceneSwitcher have been loaded
959
+ * @returns True if all scenes are loaded, false otherwise
960
+ */
908
961
  allLoaded(): boolean {
909
962
  if (this._switcher.scenes) {
910
963
  for (const scene of this._switcher.scenes) {
@@ -915,12 +968,25 @@ class PreLoadScheduler {
915
968
  }
916
969
  }
917
970
 
971
+ /**
972
+ * Represents a single preloading task for a scene
973
+ */
918
974
  class LoadTask {
919
-
975
+ /** The index of the scene in the scenes array */
920
976
  index: number;
977
+
978
+ /** The AssetReference to be loaded */
921
979
  asset: AssetReference;
980
+
981
+ /** The collection of active load tasks this task belongs to */
922
982
  tasks: LoadTask[];
923
983
 
984
+ /**
985
+ * Creates a new LoadTask and begins loading immediately
986
+ * @param index The index of the scene in the scenes array
987
+ * @param asset The AssetReference to preload
988
+ * @param tasks The collection of active load tasks
989
+ */
924
990
  constructor(index: number, asset: AssetReference, tasks: LoadTask[]) {
925
991
  this.index = index;
926
992
  this.asset = asset;
@@ -929,6 +995,9 @@ class LoadTask {
929
995
  this.awaitLoading();
930
996
  }
931
997
 
998
+ /**
999
+ * Asynchronously loads the asset and removes this task from the active tasks list when complete
1000
+ */
932
1001
  private async awaitLoading() {
933
1002
  if (this.asset && !this.asset.isLoaded()) {
934
1003
  if (debug)
@@ -8,8 +8,15 @@ import { EventList } from "./EventList.js";
8
8
 
9
9
  const debug = getParam("debugspatialtrigger");
10
10
 
11
+ /** Layer instances used for mask comparison */
11
12
  const layer1 = new Layers();
12
13
  const layer2 = new Layers();
14
+ /**
15
+ * Tests if two layer masks intersect
16
+ * @param mask1 First layer mask
17
+ * @param mask2 Second layer mask
18
+ * @returns True if the layers intersect
19
+ */
13
20
  function testMask(mask1, mask2) {
14
21
  layer1.mask = mask1;
15
22
  layer2.mask = mask2;
@@ -17,25 +24,45 @@ function testMask(mask1, mask2) {
17
24
  }
18
25
 
19
26
  /**
27
+ * Component that receives and responds to spatial events, like entering or exiting a trigger zone.
28
+ * Used in conjunction with {@link SpatialTrigger} to create interactive spatial events.
20
29
  * @category Interactivity
21
30
  * @group Components
22
31
  */
23
32
  export class SpatialTriggerReceiver extends Behaviour {
24
33
 
25
- // currently Layers in unity but maybe this should be a string or plane number? Or should it be a bitmask to allow receivers use multiple triggers?
34
+ /**
35
+ * Bitmask determining which triggers this receiver responds to
36
+ * Only triggers with matching masks will interact with this receiver
37
+ */
26
38
  @serializable()
27
39
  triggerMask: number = 0;
40
+
41
+ /** Event invoked when this object enters a trigger zone */
28
42
  @serializable(EventList)
29
43
  onEnter?: EventList<any>;
44
+
45
+ /** Event invoked continuously while this object is inside a trigger zone */
30
46
  @serializable(EventList)
31
47
  onStay?: EventList<any>;
48
+
49
+ /** Event invoked when this object exits a trigger zone */
32
50
  @serializable(EventList)
33
51
  onExit?: EventList<any>;
34
52
 
53
+ /**
54
+ * Initializes the receiver and logs debug info if enabled
55
+ * @internal
56
+ */
35
57
  start() {
36
58
  if (debug) console.log(this.name, this.triggerMask, this);
37
59
  }
38
60
 
61
+ /**
62
+ * Checks for intersections with spatial triggers and fires appropriate events
63
+ * Handles enter, stay, and exit events for all relevant triggers
64
+ * @internal
65
+ */
39
66
  update(): void {
40
67
  this.currentIntersected.length = 0;
41
68
  for (const trigger of SpatialTrigger.triggers) {
@@ -59,23 +86,38 @@ export class SpatialTriggerReceiver extends Behaviour {
59
86
  }
60
87
  this.lastIntersected.length = 0;
61
88
  this.lastIntersected.push(...this.currentIntersected);
62
-
63
89
  }
64
90
 
91
+ /** Array of triggers currently intersecting with this receiver */
65
92
  readonly currentIntersected: SpatialTrigger[] = [];
93
+
94
+ /** Array of triggers that intersected with this receiver in the previous frame */
66
95
  readonly lastIntersected: SpatialTrigger[] = [];
67
96
 
97
+ /**
98
+ * Handles trigger enter events.
99
+ * @param trigger The spatial trigger that was entered
100
+ */
68
101
  onEnterTrigger(trigger: SpatialTrigger): void {
69
102
  if(debug) console.log("ENTER TRIGGER", this.name, trigger.name, this, trigger);
70
103
  trigger.raiseOnEnterEvent(this);
71
104
  this.onEnter?.invoke();
72
105
  }
106
+
107
+ /**
108
+ * Handles trigger exit events.
109
+ * @param trigger The spatial trigger that was exited
110
+ */
73
111
  onExitTrigger(trigger: SpatialTrigger): void {
74
112
  if(debug) console.log("EXIT TRIGGER", this.name, trigger.name, );
75
113
  trigger.raiseOnExitEvent(this);
76
114
  this.onExit?.invoke();
77
115
  }
78
116
 
117
+ /**
118
+ * Handles trigger stay events.
119
+ * @param trigger The spatial trigger that the receiver is staying in
120
+ */
79
121
  onStayTrigger(trigger: SpatialTrigger): void {
80
122
  trigger.raiseOnStayEvent(this);
81
123
  this.onStay?.invoke();
@@ -83,24 +125,38 @@ export class SpatialTriggerReceiver extends Behaviour {
83
125
  }
84
126
 
85
127
  /**
86
- * A trigger that can be used to detect if an object is inside a box.
128
+ * A spatial trigger component that detects objects within a box-shaped area.
129
+ * Used to trigger events when objects enter, stay in, or exit the defined area
87
130
  * @category Interactivity
88
131
  * @group Components
89
132
  */
90
133
  export class SpatialTrigger extends Behaviour {
91
134
 
135
+ /** Global registry of all active spatial triggers in the scene */
92
136
  static triggers: SpatialTrigger[] = [];
93
137
 
138
+ /**
139
+ * Bitmask determining which receivers this trigger affects.
140
+ * Only receivers with matching masks will be triggered.
141
+ */
142
+ // currently Layers in unity but maybe this should be a string or plane number? Or should it be a bitmask to allow receivers use multiple triggers?
94
143
  @serializable()
95
144
  triggerMask?: number;
96
145
 
146
+ /** Box helper component used to visualize and calculate the trigger area */
97
147
  private boxHelper?: BoxHelperComponent;
98
148
 
149
+ /**
150
+ * Initializes the trigger and logs debug info if enabled
151
+ */
99
152
  start() {
100
153
  if (debug)
101
154
  console.log(this.name, this.triggerMask, this);
102
155
  }
103
156
 
157
+ /**
158
+ * Registers this trigger in the global registry and sets up debug visualization if enabled
159
+ */
104
160
  onEnable(): void {
105
161
  SpatialTrigger.triggers.push(this);
106
162
  if (!this.boxHelper) {
@@ -108,10 +164,19 @@ export class SpatialTrigger extends Behaviour {
108
164
  this.boxHelper?.showHelper(null, debug as boolean);
109
165
  }
110
166
  }
167
+
168
+ /**
169
+ * Removes this trigger from the global registry when disabled
170
+ */
111
171
  onDisable(): void {
112
172
  SpatialTrigger.triggers.splice(SpatialTrigger.triggers.indexOf(this), 1);
113
173
  }
114
174
 
175
+ /**
176
+ * Tests if an object is inside this trigger's box
177
+ * @param obj The object to test against this trigger
178
+ * @returns True if the object is inside the trigger box
179
+ */
115
180
  test(obj: Object3D): boolean {
116
181
  if (!this.boxHelper) return false;
117
182
  return this.boxHelper.isInBox(obj) ?? false;
@@ -119,6 +184,10 @@ export class SpatialTrigger extends Behaviour {
119
184
 
120
185
  // private args: SpatialTriggerEventArgs = new SpatialTriggerEventArgs();
121
186
 
187
+ /**
188
+ * Raises the onEnter event on any SpatialTriggerReceiver components attached to this trigger's GameObject
189
+ * @param rec The receiver that entered this trigger
190
+ */
122
191
  raiseOnEnterEvent(rec: SpatialTriggerReceiver) {
123
192
  // this.args.trigger = this;
124
193
  // this.args.source = rec;
@@ -130,6 +199,10 @@ export class SpatialTrigger extends Behaviour {
130
199
  }, false);
131
200
  }
132
201
 
202
+ /**
203
+ * Raises the onStay event on any SpatialTriggerReceiver components attached to this trigger's GameObject
204
+ * @param rec The receiver that is staying in this trigger
205
+ */
133
206
  raiseOnStayEvent(rec: SpatialTriggerReceiver) {
134
207
  // this.args.trigger = this;
135
208
  // this.args.source = rec;
@@ -141,6 +214,10 @@ export class SpatialTrigger extends Behaviour {
141
214
  }, false);
142
215
  }
143
216
 
217
+ /**
218
+ * Raises the onExit event on any SpatialTriggerReceiver components attached to this trigger's GameObject
219
+ * @param rec The receiver that exited this trigger
220
+ */
144
221
  raiseOnExitEvent(rec: SpatialTriggerReceiver) {
145
222
  GameObject.foreachComponent(this.gameObject, c => {
146
223
  if (c === rec) return;