dacha 0.18.0-alpha.9 → 0.18.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 (66) hide show
  1. package/build/contrib/components/bitmap-text/index.d.ts +11 -11
  2. package/build/contrib/components/bitmap-text/index.js +13 -10
  3. package/build/contrib/components/index.d.ts +1 -0
  4. package/build/contrib/components/index.js +1 -0
  5. package/build/contrib/components/interpolation/index.d.ts +85 -0
  6. package/build/contrib/components/interpolation/index.js +101 -0
  7. package/build/contrib/components/mesh/index.d.ts +13 -13
  8. package/build/contrib/components/mesh/index.js +12 -9
  9. package/build/contrib/components/pixi-view/index.d.ts +5 -5
  10. package/build/contrib/components/pixi-view/index.js +5 -2
  11. package/build/contrib/components/rigid-body/index.d.ts +8 -0
  12. package/build/contrib/components/rigid-body/index.js +8 -0
  13. package/build/contrib/components/shape/index.d.ts +20 -36
  14. package/build/contrib/components/shape/index.js +27 -23
  15. package/build/contrib/components/sprite/index.d.ts +22 -14
  16. package/build/contrib/components/sprite/index.js +23 -10
  17. package/build/contrib/systems/index.d.ts +2 -0
  18. package/build/contrib/systems/index.js +1 -0
  19. package/build/contrib/systems/interpolator/api.d.ts +40 -0
  20. package/build/contrib/systems/interpolator/api.js +70 -0
  21. package/build/contrib/systems/interpolator/index.d.ts +3 -0
  22. package/build/contrib/systems/interpolator/index.js +2 -0
  23. package/build/contrib/systems/interpolator/system.d.ts +31 -0
  24. package/build/contrib/systems/interpolator/system.js +96 -0
  25. package/build/contrib/systems/interpolator/tests/helpers.d.ts +13 -0
  26. package/build/contrib/systems/interpolator/tests/helpers.js +45 -0
  27. package/build/contrib/systems/interpolator/utils.d.ts +5 -0
  28. package/build/contrib/systems/interpolator/utils.js +41 -0
  29. package/build/contrib/systems/physics-system/api.d.ts +45 -1
  30. package/build/contrib/systems/physics-system/api.js +49 -0
  31. package/build/contrib/systems/physics-system/consts.d.ts +3 -1
  32. package/build/contrib/systems/physics-system/consts.js +4 -1
  33. package/build/contrib/systems/physics-system/physics-system.js +7 -1
  34. package/build/contrib/systems/physics-system/subsystems/collision-detection/dynamic-aabb-tree/index.d.ts +1 -1
  35. package/build/contrib/systems/physics-system/subsystems/collision-detection/dynamic-aabb-tree/index.js +21 -15
  36. package/build/contrib/systems/physics-system/subsystems/collision-detection/index.d.ts +11 -11
  37. package/build/contrib/systems/physics-system/subsystems/collision-detection/index.js +130 -124
  38. package/build/contrib/systems/physics-system/subsystems/collision-detection/query-utils.d.ts +5 -0
  39. package/build/contrib/systems/physics-system/subsystems/collision-detection/query-utils.js +97 -81
  40. package/build/contrib/systems/physics-system/subsystems/collision-detection/types.d.ts +0 -15
  41. package/build/contrib/systems/physics-system/subsystems/collision-detection/utils.d.ts +3 -0
  42. package/build/contrib/systems/physics-system/subsystems/collision-detection/utils.js +9 -0
  43. package/build/contrib/systems/physics-system/subsystems/physics/mass-properties.js +1 -1
  44. package/build/contrib/systems/physics-system/types.d.ts +12 -2
  45. package/build/contrib/systems/renderer/actor-render-tree.js +15 -6
  46. package/build/contrib/systems/renderer/builders/mesh-builder/index.d.ts +0 -1
  47. package/build/contrib/systems/renderer/builders/mesh-builder/index.js +0 -21
  48. package/build/contrib/systems/renderer/builders/pixi-view-builder/index.js +2 -1
  49. package/build/contrib/systems/renderer/builders/sprite-builder/index.js +8 -0
  50. package/build/contrib/systems/renderer/material/consts.d.ts +1 -1
  51. package/build/contrib/systems/renderer/material/consts.js +3 -1
  52. package/build/contrib/systems/renderer/material/index.js +11 -5
  53. package/build/engine/data-lib/index.d.ts +1 -1
  54. package/build/engine/data-lib/index.js +1 -1
  55. package/build/engine/data-lib/pool.d.ts +9 -0
  56. package/build/engine/data-lib/pool.js +19 -0
  57. package/build/engine/game-loop.js +3 -3
  58. package/build/engine/math-lib/math/ops.d.ts +16 -0
  59. package/build/engine/math-lib/math/ops.js +25 -0
  60. package/package.json +1 -1
  61. package/build/contrib/systems/physics-system/subsystems/collision-detection/dispersion-calculator/index.d.ts +0 -11
  62. package/build/contrib/systems/physics-system/subsystems/collision-detection/dispersion-calculator/index.js +0 -41
  63. package/build/engine/data-lib/sort/index.d.ts +0 -1
  64. package/build/engine/data-lib/sort/index.js +0 -1
  65. package/build/engine/data-lib/sort/insertion-sort.d.ts +0 -1
  66. package/build/engine/data-lib/sort/insertion-sort.js +0 -14
@@ -4,6 +4,7 @@ export { GameStatsMeter } from './game-stats-meter';
4
4
  export { KeyboardInputSystem } from './keyboard-input-system';
5
5
  export { KeyboardControlSystem } from './keyboard-control-system';
6
6
  export { CharacterController } from './character-controller';
7
+ export { Interpolator, InterpolatorAPI } from './interpolator';
7
8
  export { MouseControlSystem } from './mouse-control-system';
8
9
  export { MouseInputSystem } from './mouse-input-system';
9
10
  export { PhysicsSystem, PhysicsAPI } from './physics-system';
@@ -0,0 +1,40 @@
1
+ import type { Actor } from '../../../engine/actor';
2
+ import type { Time } from '../../../engine/time';
3
+ export interface RenderTransform {
4
+ x: number;
5
+ y: number;
6
+ rotation: number;
7
+ }
8
+ interface InterpolatorAPIOptions {
9
+ time: Time;
10
+ }
11
+ /**
12
+ * Public API for reading render-facing (visually smoothed) transforms.
13
+ *
14
+ * Retrieve it via `world.systemApi.get(InterpolatorAPI)`. Use it in
15
+ * systems and behaviors that position visuals against moving actors —
16
+ * e.g. a camera-follow script — instead of reading the authoritative
17
+ * Transform, which advances in fixed steps.
18
+ *
19
+ * Values are computed from the current `Time.alpha`, so this API is only
20
+ * meaningful during the update phase (not inside `fixedUpdate`).
21
+ *
22
+ * @category Systems
23
+ */
24
+ export declare class InterpolatorAPI {
25
+ private time;
26
+ constructor(options: InterpolatorAPIOptions);
27
+ /**
28
+ * Returns the world-space transform the actor is rendered with this
29
+ * frame. Falls back to the Transform for actors (and
30
+ * ancestors) without an active Interpolation component.
31
+ */
32
+ getRenderTransform(actor: Actor): RenderTransform;
33
+ /**
34
+ * Requests an immediate visual jump to the actor's
35
+ * Transform. Call right after teleporting the actor.
36
+ */
37
+ snap(actor: Actor): void;
38
+ private composeRenderWorldMatrix;
39
+ }
40
+ export {};
@@ -0,0 +1,70 @@
1
+ import { Matrix } from '../../../engine/math-lib';
2
+ import { Interpolation } from '../../components/interpolation';
3
+ import { Transform } from '../../components/transform';
4
+ import { computeRenderValues } from './utils';
5
+ /**
6
+ * Public API for reading render-facing (visually smoothed) transforms.
7
+ *
8
+ * Retrieve it via `world.systemApi.get(InterpolatorAPI)`. Use it in
9
+ * systems and behaviors that position visuals against moving actors —
10
+ * e.g. a camera-follow script — instead of reading the authoritative
11
+ * Transform, which advances in fixed steps.
12
+ *
13
+ * Values are computed from the current `Time.alpha`, so this API is only
14
+ * meaningful during the update phase (not inside `fixedUpdate`).
15
+ *
16
+ * @category Systems
17
+ */
18
+ export class InterpolatorAPI {
19
+ time;
20
+ constructor(options) {
21
+ this.time = options.time;
22
+ }
23
+ /**
24
+ * Returns the world-space transform the actor is rendered with this
25
+ * frame. Falls back to the Transform for actors (and
26
+ * ancestors) without an active Interpolation component.
27
+ */
28
+ getRenderTransform(actor) {
29
+ const matrix = this.composeRenderWorldMatrix(actor);
30
+ return {
31
+ x: matrix.tx,
32
+ y: matrix.ty,
33
+ rotation: Math.atan2(matrix.b, matrix.a),
34
+ };
35
+ }
36
+ /**
37
+ * Requests an immediate visual jump to the actor's
38
+ * Transform. Call right after teleporting the actor.
39
+ */
40
+ snap(actor) {
41
+ const interpolation = actor.getComponent(Interpolation);
42
+ interpolation?.snap();
43
+ }
44
+ composeRenderWorldMatrix(actor) {
45
+ const transform = actor.getComponent(Transform);
46
+ const interpolation = actor.getComponent(Interpolation);
47
+ const useRender = interpolation !== undefined &&
48
+ !interpolation.disabled &&
49
+ interpolation.initialized;
50
+ if (useRender) {
51
+ computeRenderValues(actor, interpolation, this.time.alpha, this.time.fixedDeltaTime);
52
+ }
53
+ const x = useRender ? interpolation.renderX : transform.local.position.x;
54
+ const y = useRender ? interpolation.renderY : transform.local.position.y;
55
+ const rotation = useRender
56
+ ? interpolation.renderRotation
57
+ : transform.local.rotation;
58
+ const { scale } = transform.local;
59
+ const cos = Math.cos(rotation);
60
+ const sin = Math.sin(rotation);
61
+ const localMatrix = new Matrix(cos * scale.x, sin * scale.x, -sin * scale.y, cos * scale.y, x, y);
62
+ const parentTransform = transform.getParentComponent();
63
+ if (!parentTransform?.actor) {
64
+ return localMatrix;
65
+ }
66
+ const result = new Matrix(1, 0, 0, 1, 0, 0);
67
+ Matrix.multiply(result, this.composeRenderWorldMatrix(parentTransform.actor), localMatrix);
68
+ return result;
69
+ }
70
+ }
@@ -0,0 +1,3 @@
1
+ export { Interpolator } from './system';
2
+ export { InterpolatorAPI } from './api';
3
+ export type { RenderTransform } from './api';
@@ -0,0 +1,2 @@
1
+ export { Interpolator } from './system';
2
+ export { InterpolatorAPI } from './api';
@@ -0,0 +1,31 @@
1
+ import { SceneSystem } from '../../../engine/system';
2
+ import type { SceneSystemOptions } from '../../../engine/system';
3
+ /**
4
+ * System that produces smooth render-facing transforms for actors moved
5
+ * during fixed updates.
6
+ *
7
+ * During `fixedUpdate` it snapshots the local Transform of every actor with
8
+ * an Interpolation component. During `update` it blends the last two
9
+ * snapshots using `Time.alpha` and writes the result into the component's
10
+ * `renderX`/`renderY`/`renderRotation`, which the renderer prefers over the
11
+ * Transform.
12
+ *
13
+ * Ordering: place this system AFTER every system that moves transforms in
14
+ * fixed updates (`PhysicsSystem`, `CharacterController`, behavior scripts
15
+ * that move actors in `fixedUpdate`) and BEFORE `Renderer` in the system
16
+ * configuration.
17
+ *
18
+ * @category Systems
19
+ */
20
+ export declare class Interpolator extends SceneSystem {
21
+ private actorQuery;
22
+ private time;
23
+ private world;
24
+ private api;
25
+ constructor(options: SceneSystemOptions);
26
+ onSceneEnter(): void;
27
+ onSceneExit(): void;
28
+ onSceneDestroy(): void;
29
+ fixedUpdate(): void;
30
+ update(): void;
31
+ }
@@ -0,0 +1,96 @@
1
+ import { ActorQuery } from '../../../engine/actor';
2
+ import { SceneSystem } from '../../../engine/system';
3
+ import { Interpolation } from '../../components/interpolation';
4
+ import { Transform } from '../../components/transform';
5
+ import { InterpolatorAPI } from './api';
6
+ import { snapToTransform, computeRenderValues } from './utils';
7
+ /**
8
+ * System that produces smooth render-facing transforms for actors moved
9
+ * during fixed updates.
10
+ *
11
+ * During `fixedUpdate` it snapshots the local Transform of every actor with
12
+ * an Interpolation component. During `update` it blends the last two
13
+ * snapshots using `Time.alpha` and writes the result into the component's
14
+ * `renderX`/`renderY`/`renderRotation`, which the renderer prefers over the
15
+ * Transform.
16
+ *
17
+ * Ordering: place this system AFTER every system that moves transforms in
18
+ * fixed updates (`PhysicsSystem`, `CharacterController`, behavior scripts
19
+ * that move actors in `fixedUpdate`) and BEFORE `Renderer` in the system
20
+ * configuration.
21
+ *
22
+ * @category Systems
23
+ */
24
+ export class Interpolator extends SceneSystem {
25
+ actorQuery;
26
+ time;
27
+ world;
28
+ api;
29
+ constructor(options) {
30
+ super();
31
+ this.time = options.time;
32
+ this.world = options.world;
33
+ this.actorQuery = new ActorQuery({
34
+ scene: options.scene,
35
+ filter: [Interpolation, Transform],
36
+ });
37
+ this.api = new InterpolatorAPI({ time: options.time });
38
+ }
39
+ onSceneEnter() {
40
+ this.world.systemApi.register(this.api);
41
+ }
42
+ onSceneExit() {
43
+ this.world.systemApi.unregister(InterpolatorAPI);
44
+ }
45
+ onSceneDestroy() {
46
+ this.actorQuery.destroy();
47
+ }
48
+ fixedUpdate() {
49
+ for (const actor of this.actorQuery.getActors()) {
50
+ const interpolation = actor.getComponent(Interpolation);
51
+ const { local } = actor.getComponent(Transform);
52
+ if (interpolation.disabled) {
53
+ interpolation._initialized = false;
54
+ continue;
55
+ }
56
+ if (!interpolation._initialized || interpolation._snapRequested) {
57
+ snapToTransform(interpolation, local);
58
+ continue;
59
+ }
60
+ interpolation._prevX = interpolation._currX;
61
+ interpolation._prevY = interpolation._currY;
62
+ interpolation._prevRotation = interpolation._currRotation;
63
+ interpolation._currX = local.position.x;
64
+ interpolation._currY = local.position.y;
65
+ interpolation._currRotation = local.rotation;
66
+ const threshold = interpolation.snapThreshold;
67
+ if (threshold > 0) {
68
+ const dx = interpolation._currX - interpolation._prevX;
69
+ const dy = interpolation._currY - interpolation._prevY;
70
+ if (dx * dx + dy * dy > threshold * threshold) {
71
+ interpolation._prevX = interpolation._currX;
72
+ interpolation._prevY = interpolation._currY;
73
+ interpolation._prevRotation = interpolation._currRotation;
74
+ }
75
+ }
76
+ }
77
+ }
78
+ update() {
79
+ const { alpha, fixedDeltaTime } = this.time;
80
+ for (const actor of this.actorQuery.getActors()) {
81
+ const interpolation = actor.getComponent(Interpolation);
82
+ if (interpolation.disabled) {
83
+ continue;
84
+ }
85
+ if (interpolation._snapRequested) {
86
+ snapToTransform(interpolation, actor.getComponent(Transform).local);
87
+ continue;
88
+ }
89
+ if (!interpolation._initialized) {
90
+ continue;
91
+ }
92
+ computeRenderValues(actor, interpolation, alpha, fixedDeltaTime);
93
+ }
94
+ }
95
+ }
96
+ Interpolator.systemName = 'Interpolator';
@@ -0,0 +1,13 @@
1
+ import { Actor } from '../../../../engine/actor';
2
+ import { Scene } from '../../../../engine/scene';
3
+ import { World } from '../../../../engine/world';
4
+ import { Time } from '../../../../engine/time';
5
+ import type { InterpolationConfig } from '../../../components/interpolation';
6
+ import { Interpolator } from '../system';
7
+ export declare const createScene: () => Scene;
8
+ export declare const createInterpolator: (scene: Scene) => {
9
+ system: Interpolator;
10
+ world: World;
11
+ time: Time;
12
+ };
13
+ export declare const createInterpolatedActor: (id: string, x: number, y: number, config?: InterpolationConfig) => Actor;
@@ -0,0 +1,45 @@
1
+ import { Actor, ActorCreator, ActorSpawner } from '../../../../engine/actor';
2
+ import { Scene } from '../../../../engine/scene';
3
+ import { TemplateCollection } from '../../../../engine/template';
4
+ import { World } from '../../../../engine/world';
5
+ import { Time } from '../../../../engine/time';
6
+ import { Interpolation } from '../../../components/interpolation';
7
+ import { Transform } from '../../../components/transform';
8
+ import { Interpolator } from '../system';
9
+ export const createScene = () => {
10
+ const templateCollection = new TemplateCollection();
11
+ const actorCreator = new ActorCreator([], templateCollection);
12
+ return new Scene({
13
+ id: 'scene',
14
+ name: 'scene',
15
+ actors: [],
16
+ actorCreator,
17
+ templateCollection,
18
+ });
19
+ };
20
+ export const createInterpolator = (scene) => {
21
+ const world = new World({ id: 'world', name: 'world' });
22
+ const templateCollection = new TemplateCollection();
23
+ const actorCreator = new ActorCreator([], templateCollection);
24
+ const time = new Time();
25
+ time.fixedDeltaTime = 0.1;
26
+ world.appendChild(scene);
27
+ const system = new Interpolator({
28
+ scene,
29
+ world,
30
+ actorSpawner: new ActorSpawner(actorCreator),
31
+ globalOptions: {},
32
+ templateCollection,
33
+ time,
34
+ });
35
+ system.onSceneEnter?.();
36
+ return { system, world, time };
37
+ };
38
+ export const createInterpolatedActor = (id, x, y, config = {}) => {
39
+ const actor = new Actor({ id, name: id });
40
+ const transform = actor.getComponent(Transform);
41
+ transform.local.position.x = x;
42
+ transform.local.position.y = y;
43
+ actor.setComponent(new Interpolation(config));
44
+ return actor;
45
+ };
@@ -0,0 +1,5 @@
1
+ import type { Actor } from '../../../engine/actor';
2
+ import type { Interpolation } from '../../components/interpolation';
3
+ import type { LocalTransform } from '../../components/transform/local-transform';
4
+ export declare const snapToTransform: (interpolation: Interpolation, local: LocalTransform) => void;
5
+ export declare const computeRenderValues: (actor: Actor, interpolation: Interpolation, alpha: number, fixedDeltaTime: number) => void;
@@ -0,0 +1,41 @@
1
+ import { MathOps } from '../../../engine/math-lib';
2
+ import { RigidBody } from '../../components/rigid-body';
3
+ export const snapToTransform = (interpolation, local) => {
4
+ interpolation._prevX = local.position.x;
5
+ interpolation._prevY = local.position.y;
6
+ interpolation._prevRotation = local.rotation;
7
+ interpolation._currX = local.position.x;
8
+ interpolation._currY = local.position.y;
9
+ interpolation._currRotation = local.rotation;
10
+ interpolation.renderX = local.position.x;
11
+ interpolation.renderY = local.position.y;
12
+ interpolation.renderRotation = local.rotation;
13
+ interpolation._initialized = true;
14
+ interpolation._snapRequested = false;
15
+ };
16
+ export const computeRenderValues = (actor, interpolation, alpha, fixedDeltaTime) => {
17
+ if (interpolation.mode === 'extrapolate') {
18
+ const rigidBody = actor.getComponent(RigidBody);
19
+ if (rigidBody !== undefined && !rigidBody.disabled) {
20
+ const timeAhead = alpha * fixedDeltaTime;
21
+ interpolation.renderX =
22
+ interpolation._currX + rigidBody.linearVelocity.x * timeAhead;
23
+ interpolation.renderY =
24
+ interpolation._currY + rigidBody.linearVelocity.y * timeAhead;
25
+ interpolation.renderRotation = rigidBody.lockRotation
26
+ ? interpolation._currRotation
27
+ : interpolation._currRotation + rigidBody.angularVelocity * timeAhead;
28
+ return;
29
+ }
30
+ }
31
+ interpolation.renderX =
32
+ interpolation._prevX +
33
+ (interpolation._currX - interpolation._prevX) * alpha;
34
+ interpolation.renderY =
35
+ interpolation._prevY +
36
+ (interpolation._currY - interpolation._prevY) * alpha;
37
+ interpolation.renderRotation =
38
+ interpolation._prevRotation +
39
+ MathOps.getAngleDelta(interpolation._prevRotation, interpolation._currRotation) *
40
+ alpha;
41
+ };
@@ -1,5 +1,5 @@
1
1
  import type { Vector2 } from '../../../engine/math-lib';
2
- import type { CastHit, OverlapHit, RaycastParams, OverlapParams, OverlapActorParams, ShapeCastParams, CastActorParams } from './types';
2
+ import type { CastHit, OverlapHit, RaycastParams, OverlapParams, OverlapActorParams, ShapeCastParams, CastActorParams, CastHitCallback, OverlapHitCallback } from './types';
3
3
  export interface PhysicsAPIHandlers {
4
4
  raycast(params: RaycastParams): CastHit | null;
5
5
  raycastAll(params: RaycastParams): CastHit[];
@@ -9,6 +9,11 @@ export interface PhysicsAPIHandlers {
9
9
  shapeCastAll(params: ShapeCastParams): CastHit[];
10
10
  castActor(params: CastActorParams): CastHit | null;
11
11
  castActorAll(params: CastActorParams): CastHit[];
12
+ raycastEach(params: RaycastParams, callback: CastHitCallback): void;
13
+ shapeCastEach(params: ShapeCastParams, callback: CastHitCallback): void;
14
+ overlapEach(params: OverlapParams, callback: OverlapHitCallback): void;
15
+ castActorEach(params: CastActorParams, callback: CastHitCallback): void;
16
+ overlapActorEach(params: OverlapActorParams, callback: OverlapHitCallback): void;
12
17
  getGravity(): Vector2;
13
18
  setGravity(gravity: Vector2): void;
14
19
  }
@@ -55,6 +60,13 @@ export declare class PhysicsAPI {
55
60
  * @returns All hits sorted from nearest to farthest
56
61
  */
57
62
  raycastAll(params: RaycastParams): CastHit[];
63
+ /**
64
+ * Casts a ray and invokes `callback` for every hit.
65
+ *
66
+ * @param params - Raycast parameters
67
+ * @param callback - Invoked once per hit with a reused hit object
68
+ */
69
+ raycastEach(params: RaycastParams, callback: CastHitCallback): void;
58
70
  /**
59
71
  * Returns all collider intersections for the given query shape.
60
72
  *
@@ -62,6 +74,13 @@ export declare class PhysicsAPI {
62
74
  * @returns All overlap hits
63
75
  */
64
76
  overlapShape(params: OverlapParams): OverlapHit[];
77
+ /**
78
+ * Invokes `callback` for every collider intersecting the query shape.
79
+ *
80
+ * @param params - Overlap parameters
81
+ * @param callback - Invoked once per overlap with a reused hit object
82
+ */
83
+ overlapEach(params: OverlapParams, callback: OverlapHitCallback): void;
65
84
  /**
66
85
  * Returns all collider intersections for an actor's collider.
67
86
  *
@@ -69,6 +88,13 @@ export declare class PhysicsAPI {
69
88
  * @returns All overlap hits
70
89
  */
71
90
  overlapActor(params: OverlapActorParams): OverlapHit[];
91
+ /**
92
+ * Invokes `callback` for every collider intersecting an actor's collider.
93
+ *
94
+ * @param params - Actor overlap parameters
95
+ * @param callback - Invoked once per overlap with a reused hit object
96
+ */
97
+ overlapActorEach(params: OverlapActorParams, callback: OverlapHitCallback): void;
72
98
  /**
73
99
  * Casts a shape and returns the nearest hit, if any.
74
100
  *
@@ -83,6 +109,13 @@ export declare class PhysicsAPI {
83
109
  * @returns All hits sorted from nearest to farthest
84
110
  */
85
111
  shapeCastAll(params: ShapeCastParams): CastHit[];
112
+ /**
113
+ * Casts a shape and invokes `callback` for every hit.
114
+ *
115
+ * @param params - Shape cast parameters
116
+ * @param callback - Invoked once per hit with a reused hit object
117
+ */
118
+ shapeCastEach(params: ShapeCastParams, callback: CastHitCallback): void;
86
119
  /**
87
120
  * Casts an actor's collider and returns the nearest hit, if any.
88
121
  *
@@ -97,4 +130,15 @@ export declare class PhysicsAPI {
97
130
  * @returns All hits sorted from nearest to farthest
98
131
  */
99
132
  castActorAll(params: CastActorParams): CastHit[];
133
+ /**
134
+ * Casts an actor's collider and invokes `callback` for every hit.
135
+ *
136
+ * The `hit` passed to `callback` is a single object reused across
137
+ * invocations and is only valid for the duration of the call. Copy any
138
+ * fields you need to keep. Hits are delivered in arbitrary order.
139
+ *
140
+ * @param params - Actor cast parameters
141
+ * @param callback - Invoked once per hit with a reused hit object
142
+ */
143
+ castActorEach(params: CastActorParams, callback: CastHitCallback): void;
100
144
  }
@@ -51,6 +51,15 @@ export class PhysicsAPI {
51
51
  raycastAll(params) {
52
52
  return this.handlers.raycastAll(params);
53
53
  }
54
+ /**
55
+ * Casts a ray and invokes `callback` for every hit.
56
+ *
57
+ * @param params - Raycast parameters
58
+ * @param callback - Invoked once per hit with a reused hit object
59
+ */
60
+ raycastEach(params, callback) {
61
+ this.handlers.raycastEach(params, callback);
62
+ }
54
63
  /**
55
64
  * Returns all collider intersections for the given query shape.
56
65
  *
@@ -60,6 +69,15 @@ export class PhysicsAPI {
60
69
  overlapShape(params) {
61
70
  return this.handlers.overlapShape(params);
62
71
  }
72
+ /**
73
+ * Invokes `callback` for every collider intersecting the query shape.
74
+ *
75
+ * @param params - Overlap parameters
76
+ * @param callback - Invoked once per overlap with a reused hit object
77
+ */
78
+ overlapEach(params, callback) {
79
+ this.handlers.overlapEach(params, callback);
80
+ }
63
81
  /**
64
82
  * Returns all collider intersections for an actor's collider.
65
83
  *
@@ -69,6 +87,15 @@ export class PhysicsAPI {
69
87
  overlapActor(params) {
70
88
  return this.handlers.overlapActor(params);
71
89
  }
90
+ /**
91
+ * Invokes `callback` for every collider intersecting an actor's collider.
92
+ *
93
+ * @param params - Actor overlap parameters
94
+ * @param callback - Invoked once per overlap with a reused hit object
95
+ */
96
+ overlapActorEach(params, callback) {
97
+ this.handlers.overlapActorEach(params, callback);
98
+ }
72
99
  /**
73
100
  * Casts a shape and returns the nearest hit, if any.
74
101
  *
@@ -87,6 +114,15 @@ export class PhysicsAPI {
87
114
  shapeCastAll(params) {
88
115
  return this.handlers.shapeCastAll(params);
89
116
  }
117
+ /**
118
+ * Casts a shape and invokes `callback` for every hit.
119
+ *
120
+ * @param params - Shape cast parameters
121
+ * @param callback - Invoked once per hit with a reused hit object
122
+ */
123
+ shapeCastEach(params, callback) {
124
+ this.handlers.shapeCastEach(params, callback);
125
+ }
90
126
  /**
91
127
  * Casts an actor's collider and returns the nearest hit, if any.
92
128
  *
@@ -105,4 +141,17 @@ export class PhysicsAPI {
105
141
  castActorAll(params) {
106
142
  return this.handlers.castActorAll(params);
107
143
  }
144
+ /**
145
+ * Casts an actor's collider and invokes `callback` for every hit.
146
+ *
147
+ * The `hit` passed to `callback` is a single object reused across
148
+ * invocations and is only valid for the duration of the call. Copy any
149
+ * fields you need to keep. Hits are delivered in arbitrary order.
150
+ *
151
+ * @param params - Actor cast parameters
152
+ * @param callback - Invoked once per hit with a reused hit object
153
+ */
154
+ castActorEach(params, callback) {
155
+ this.handlers.castActorEach(params, callback);
156
+ }
108
157
  }
@@ -1,5 +1,5 @@
1
1
  export declare const DEFAULT_SOLVER_ITERATIONS = 8;
2
- export declare const DEFAULT_LINEAR_SLEEP_THRESHOLD = 2;
2
+ export declare const DEFAULT_LINEAR_SLEEP_THRESHOLD = 1;
3
3
  export declare const DEFAULT_ANGULAR_SLEEP_THRESHOLD = 0.05;
4
4
  export declare const DEFAULT_SLEEP_TIME_THRESHOLD = 0.5;
5
5
  export declare const DEFAULT_MAX_BIAS_VELOCITY = 60;
@@ -9,3 +9,5 @@ export declare const BIAS_ANGULAR_SLEEP_MULTIPLIER = 2;
9
9
  export declare const RESTITUTION_VELOCITY_THRESHOLD = 1;
10
10
  export declare const PENETRATION_SLEEP_MARGIN = 0.25;
11
11
  export declare const SUPPORT_MIN_GRAVITY_DOT = 0.5;
12
+ export declare const DEFAULT_GRAVITY_X = 0;
13
+ export declare const DEFAULT_GRAVITY_Y = 980;
@@ -1,5 +1,5 @@
1
1
  export const DEFAULT_SOLVER_ITERATIONS = 8;
2
- export const DEFAULT_LINEAR_SLEEP_THRESHOLD = 2;
2
+ export const DEFAULT_LINEAR_SLEEP_THRESHOLD = 1;
3
3
  export const DEFAULT_ANGULAR_SLEEP_THRESHOLD = 0.05;
4
4
  export const DEFAULT_SLEEP_TIME_THRESHOLD = 0.5;
5
5
  export const DEFAULT_MAX_BIAS_VELOCITY = 60;
@@ -12,3 +12,6 @@ export const RESTITUTION_VELOCITY_THRESHOLD = 1;
12
12
  export const PENETRATION_SLEEP_MARGIN = 0.25;
13
13
  // 0.5 treats surfaces up to ~60° from horizontal as holding a body up.
14
14
  export const SUPPORT_MIN_GRAVITY_DOT = 0.5;
15
+ // Default gravity in world units (px/s²).
16
+ export const DEFAULT_GRAVITY_X = 0;
17
+ export const DEFAULT_GRAVITY_Y = 980;
@@ -2,6 +2,7 @@ import { SceneSystem } from '../../../engine/system';
2
2
  import { Vector2 } from '../../../engine/math-lib';
3
3
  import { PhysicsSubsystem, CollisionDetectionSubsystem, CollisionBroadcastSubsystem, ConstraintSolver, } from './subsystems';
4
4
  import { PhysicsAPI } from './api';
5
+ import { DEFAULT_GRAVITY_X, DEFAULT_GRAVITY_Y } from './consts';
5
6
  /**
6
7
  * Physics system that handles 2D physics simulation and collision detection
7
8
  *
@@ -21,7 +22,7 @@ export class PhysicsSystem extends SceneSystem {
21
22
  gravity;
22
23
  constructor(options) {
23
24
  super();
24
- const { gravityX = 0, gravityY = 0, solverIterations, linearSleepThreshold, angularSleepThreshold, sleepTimeThreshold, maxAllowedPenetration, maxBiasVelocity, } = options;
25
+ const { gravityX = DEFAULT_GRAVITY_X, gravityY = DEFAULT_GRAVITY_Y, solverIterations, linearSleepThreshold, angularSleepThreshold, sleepTimeThreshold, maxAllowedPenetration, maxBiasVelocity, } = options;
25
26
  this.gravity = new Vector2(gravityX, gravityY);
26
27
  this.world = options.world;
27
28
  this.physicsSubsystem = new PhysicsSubsystem({
@@ -51,6 +52,11 @@ export class PhysicsSystem extends SceneSystem {
51
52
  shapeCastAll: (params) => this.collisionDetectionSubsystem.shapeCastAll(params),
52
53
  castActor: (params) => this.collisionDetectionSubsystem.castActor(params),
53
54
  castActorAll: (params) => this.collisionDetectionSubsystem.castActorAll(params),
55
+ raycastEach: (params, callback) => this.collisionDetectionSubsystem.raycastEach(params, callback),
56
+ shapeCastEach: (params, callback) => this.collisionDetectionSubsystem.shapeCastEach(params, callback),
57
+ overlapEach: (params, callback) => this.collisionDetectionSubsystem.overlapEach(params, callback),
58
+ castActorEach: (params, callback) => this.collisionDetectionSubsystem.castActorEach(params, callback),
59
+ overlapActorEach: (params, callback) => this.collisionDetectionSubsystem.overlapActorEach(params, callback),
54
60
  getGravity: () => this.gravity,
55
61
  setGravity: (gravity) => {
56
62
  this.gravity = gravity;
@@ -4,7 +4,7 @@ export declare class DynamicAABBTree<T> {
4
4
  private root;
5
5
  private nodesById;
6
6
  private freeInternalNodes;
7
- private queryStack;
7
+ private stackPool;
8
8
  private nextId;
9
9
  get size(): number;
10
10
  insert(aabb: AABB, value: T): DynamicAABBTreeEntryId;
@@ -1,3 +1,4 @@
1
+ import { Pool } from '../../../../../../engine/data-lib';
1
2
  const isLeaf = (node) => node.child1 === null;
2
3
  const createAABB = () => ({
3
4
  min: { x: 0, y: 0 },
@@ -26,7 +27,9 @@ export class DynamicAABBTree {
26
27
  root = null;
27
28
  nodesById = new Map();
28
29
  freeInternalNodes = [];
29
- queryStack = [];
30
+ stackPool = new Pool(() => [], (stack) => {
31
+ stack.length = 0;
32
+ });
30
33
  nextId = 1;
31
34
  get size() {
32
35
  return this.nodesById.size;
@@ -52,22 +55,25 @@ export class DynamicAABBTree {
52
55
  if (!this.root) {
53
56
  return;
54
57
  }
55
- const stack = this.queryStack;
56
- stack.length = 0;
58
+ const stack = this.stackPool.acquire();
57
59
  stack.push(this.root);
58
- while (stack.length > 0) {
59
- const node = stack.pop();
60
- if (!overlaps(node.aabb, aabb)) {
61
- continue;
62
- }
63
- if (isLeaf(node)) {
64
- if (visitor(node.value) === false) {
65
- stack.length = 0;
66
- return;
60
+ try {
61
+ while (stack.length > 0) {
62
+ const node = stack.pop();
63
+ if (!overlaps(node.aabb, aabb)) {
64
+ continue;
67
65
  }
68
- continue;
66
+ if (isLeaf(node)) {
67
+ if (visitor(node.value) === false) {
68
+ return;
69
+ }
70
+ continue;
71
+ }
72
+ stack.push(node.child1, node.child2);
69
73
  }
70
- stack.push(node.child1, node.child2);
74
+ }
75
+ finally {
76
+ this.stackPool.release(stack);
71
77
  }
72
78
  }
73
79
  queryAll(aabb, output = []) {
@@ -80,7 +86,7 @@ export class DynamicAABBTree {
80
86
  this.root = null;
81
87
  this.nodesById.clear();
82
88
  this.freeInternalNodes.length = 0;
83
- this.queryStack.length = 0;
89
+ this.stackPool.clear();
84
90
  }
85
91
  createNode(aabb, value) {
86
92
  return {