dacha 0.18.0-alpha.4 → 0.18.0-alpha.6

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 (64) hide show
  1. package/build/contrib/components/audio-source/index.d.ts +1 -1
  2. package/build/contrib/components/audio-source/index.js +1 -1
  3. package/build/contrib/components/character-body/index.d.ts +72 -0
  4. package/build/contrib/components/character-body/index.js +90 -0
  5. package/build/contrib/components/collider/index.d.ts +9 -9
  6. package/build/contrib/components/collider/index.js +6 -6
  7. package/build/contrib/components/index.d.ts +1 -0
  8. package/build/contrib/components/index.js +1 -0
  9. package/build/contrib/events/index.d.ts +19 -39
  10. package/build/contrib/events/index.js +3 -21
  11. package/build/contrib/systems/audio-system/api.d.ts +38 -0
  12. package/build/contrib/systems/audio-system/api.js +44 -0
  13. package/build/contrib/systems/audio-system/index.d.ts +4 -3
  14. package/build/contrib/systems/audio-system/index.js +24 -30
  15. package/build/contrib/systems/character-controller/index.d.ts +32 -0
  16. package/build/contrib/systems/character-controller/index.js +262 -0
  17. package/build/contrib/systems/character-controller/utils.d.ts +2 -0
  18. package/build/contrib/systems/character-controller/utils.js +9 -0
  19. package/build/contrib/systems/index.d.ts +2 -1
  20. package/build/contrib/systems/index.js +2 -1
  21. package/build/contrib/systems/physics-system/api.d.ts +50 -8
  22. package/build/contrib/systems/physics-system/api.js +51 -10
  23. package/build/contrib/systems/physics-system/physics-system.d.ts +1 -0
  24. package/build/contrib/systems/physics-system/physics-system.js +22 -3
  25. package/build/contrib/systems/physics-system/subsystems/collision-detection/geometry-builders/build-box-cast-geometry.d.ts +4 -2
  26. package/build/contrib/systems/physics-system/subsystems/collision-detection/geometry-builders/build-box-cast-geometry.js +7 -4
  27. package/build/contrib/systems/physics-system/subsystems/collision-detection/geometry-builders/build-box-geometry.d.ts +4 -4
  28. package/build/contrib/systems/physics-system/subsystems/collision-detection/geometry-builders/build-box-geometry.js +22 -22
  29. package/build/contrib/systems/physics-system/subsystems/collision-detection/geometry-builders/build-capsule-cast-geometry.d.ts +4 -2
  30. package/build/contrib/systems/physics-system/subsystems/collision-detection/geometry-builders/build-capsule-cast-geometry.js +14 -10
  31. package/build/contrib/systems/physics-system/subsystems/collision-detection/geometry-builders/build-capsule-geometry.d.ts +4 -4
  32. package/build/contrib/systems/physics-system/subsystems/collision-detection/geometry-builders/build-capsule-geometry.js +27 -15
  33. package/build/contrib/systems/physics-system/subsystems/collision-detection/geometry-builders/build-circle-cast-geometry.d.ts +4 -2
  34. package/build/contrib/systems/physics-system/subsystems/collision-detection/geometry-builders/build-circle-cast-geometry.js +10 -6
  35. package/build/contrib/systems/physics-system/subsystems/collision-detection/geometry-builders/build-circle-geometry.d.ts +4 -4
  36. package/build/contrib/systems/physics-system/subsystems/collision-detection/geometry-builders/build-circle-geometry.js +24 -18
  37. package/build/contrib/systems/physics-system/subsystems/collision-detection/geometry-builders/build-ray-geometry.js +1 -1
  38. package/build/contrib/systems/physics-system/subsystems/collision-detection/geometry-builders/build-segment-geometry.d.ts +2 -2
  39. package/build/contrib/systems/physics-system/subsystems/collision-detection/geometry-builders/build-segment-geometry.js +9 -5
  40. package/build/contrib/systems/physics-system/subsystems/collision-detection/geometry-builders/index.d.ts +2 -2
  41. package/build/contrib/systems/physics-system/subsystems/collision-detection/index.d.ts +5 -3
  42. package/build/contrib/systems/physics-system/subsystems/collision-detection/index.js +34 -1
  43. package/build/contrib/systems/physics-system/subsystems/collision-detection/query-utils.d.ts +8 -5
  44. package/build/contrib/systems/physics-system/subsystems/collision-detection/query-utils.js +116 -48
  45. package/build/contrib/systems/physics-system/subsystems/collision-detection/types.d.ts +7 -0
  46. package/build/contrib/systems/physics-system/subsystems/constraint-solver/index.d.ts +2 -5
  47. package/build/contrib/systems/physics-system/subsystems/constraint-solver/index.js +11 -45
  48. package/build/contrib/systems/physics-system/subsystems/physics/index.d.ts +9 -3
  49. package/build/contrib/systems/physics-system/subsystems/physics/index.js +8 -5
  50. package/build/contrib/systems/physics-system/tests/helpers.d.ts +1 -1
  51. package/build/contrib/systems/physics-system/tests/helpers.js +3 -2
  52. package/build/contrib/systems/physics-system/types.d.ts +87 -43
  53. package/build/contrib/systems/renderer/builders/shape-builder/utils.js +38 -62
  54. package/build/contrib/utils/one-way-validator.d.ts +12 -0
  55. package/build/contrib/utils/one-way-validator.js +53 -0
  56. package/build/engine/consts.d.ts +2 -0
  57. package/build/engine/consts.js +2 -0
  58. package/build/engine/game-loop.d.ts +6 -2
  59. package/build/engine/game-loop.js +27 -6
  60. package/build/engine/math-lib/vector/vector2.d.ts +8 -0
  61. package/build/engine/math-lib/vector/vector2.js +11 -1
  62. package/build/events/index.d.ts +2 -2
  63. package/build/events/index.js +1 -1
  64. package/package.json +1 -1
@@ -0,0 +1,262 @@
1
+ import { ActorQuery } from '../../../engine/actor';
2
+ import { SceneSystem } from '../../../engine/system';
3
+ import { Vector2, VectorOps } from '../../../engine/math-lib';
4
+ import { Collider, CharacterBody, RigidBody, Transform, } from '../../components';
5
+ import { PhysicsAPI } from '../physics-system';
6
+ import { OneWayValidator } from '../../utils/one-way-validator';
7
+ import { CharacterHit } from '../../events';
8
+ import { clipAgainstNormal } from './utils';
9
+ const DISTANCE_EPSILON = 0.000001;
10
+ const SNAP_EPSILON = 0.000001;
11
+ /**
12
+ * Kinematic character controller system with sweep/slide collision movement.
13
+ *
14
+ * The system expects actors to have `CharacterBody`, `Transform`,
15
+ * `Collider`, and a kinematic `RigidBody`. Put this system before
16
+ * `PhysicsSystem` in system configuration so `movePosition()` targets are
17
+ * consumed by physics in the same fixed step.
18
+ *
19
+ * @category Systems
20
+ */
21
+ export class CharacterController extends SceneSystem {
22
+ actorQuery;
23
+ world;
24
+ oneWayValidator;
25
+ constructor(options) {
26
+ super();
27
+ this.world = options.world;
28
+ this.actorQuery = new ActorQuery({
29
+ scene: options.scene,
30
+ filter: [CharacterBody, Transform, Collider, RigidBody],
31
+ });
32
+ this.oneWayValidator = new OneWayValidator();
33
+ }
34
+ onSceneDestroy() {
35
+ this.actorQuery.destroy();
36
+ }
37
+ isBlockingHit(actor, hit) {
38
+ const rigidBody = hit.actor.getComponent(RigidBody);
39
+ if (!rigidBody || rigidBody.disabled) {
40
+ return false;
41
+ }
42
+ if (!rigidBody?.oneWay || !rigidBody.oneWayNormal) {
43
+ return true;
44
+ }
45
+ return this.oneWayValidator.validate(hit.actor, actor, hit.normal);
46
+ }
47
+ isWalkable(actor, normal) {
48
+ const character = actor.getComponent(CharacterBody);
49
+ return (VectorOps.dotProduct(normal, character.upDirection) >=
50
+ Math.cos(character.maxSlopeAngle));
51
+ }
52
+ isRecoverableOverlap(actor, hit) {
53
+ const rigidBody = hit.actor.getComponent(RigidBody);
54
+ if (!rigidBody || rigidBody.disabled || rigidBody.type === 'dynamic') {
55
+ return false;
56
+ }
57
+ if (!rigidBody.oneWay || !rigidBody.oneWayNormal) {
58
+ return true;
59
+ }
60
+ return this.oneWayValidator.validate(hit.actor, actor, hit.normal);
61
+ }
62
+ resetGroundState(character) {
63
+ if (character.onGround) {
64
+ character.groundActor = null;
65
+ character.groundNormal = character.upDirection.clone();
66
+ character.onGround = false;
67
+ }
68
+ }
69
+ resetState(character) {
70
+ this.resetGroundState(character);
71
+ character.onWall = false;
72
+ character.onCeiling = false;
73
+ }
74
+ cast(actor, position, displacement, hitFilter) {
75
+ if (displacement.squaredMagnitude <= DISTANCE_EPSILON) {
76
+ return null;
77
+ }
78
+ const physicsApi = this.world.systemApi.get(PhysicsAPI);
79
+ const character = actor.getComponent(CharacterBody);
80
+ const transform = actor.getComponent(Transform);
81
+ const distance = displacement.magnitude;
82
+ return physicsApi.castActor({
83
+ actor,
84
+ offset: {
85
+ x: position.x - transform.world.position.x,
86
+ y: position.y - transform.world.position.y,
87
+ },
88
+ direction: displacement,
89
+ maxDistance: distance + character.skinWidth,
90
+ hitFilter,
91
+ });
92
+ }
93
+ castMotion(actor, position, displacement) {
94
+ return this.cast(actor, position, displacement, (hit) => {
95
+ if (hit.distance <= DISTANCE_EPSILON &&
96
+ VectorOps.dotProduct(displacement, hit.normal) > DISTANCE_EPSILON) {
97
+ return false;
98
+ }
99
+ return this.isBlockingHit(actor, hit);
100
+ });
101
+ }
102
+ castGround(actor, position, displacement) {
103
+ return this.cast(actor, position, displacement, (hit) => this.isBlockingHit(actor, hit) && this.isWalkable(actor, hit.normal));
104
+ }
105
+ recoverOverlaps(actor, position) {
106
+ const character = actor.getComponent(CharacterBody);
107
+ if (!character._needsRecovery) {
108
+ return;
109
+ }
110
+ const transform = actor.getComponent(Transform);
111
+ const physicsApi = this.world.systemApi.get(PhysicsAPI);
112
+ for (let i = 0; i < character.maxRecoveries; i += 1) {
113
+ const hits = physicsApi.overlapActor({
114
+ actor,
115
+ offset: {
116
+ x: position.x - transform.world.position.x,
117
+ y: position.y - transform.world.position.y,
118
+ },
119
+ });
120
+ let recovered = false;
121
+ for (const hit of hits) {
122
+ if (!this.isRecoverableOverlap(actor, hit)) {
123
+ continue;
124
+ }
125
+ const correction = hit.penetration + character.skinWidth;
126
+ if (correction <= DISTANCE_EPSILON) {
127
+ continue;
128
+ }
129
+ position.x += hit.normal.x * correction;
130
+ position.y += hit.normal.y * correction;
131
+ recovered = true;
132
+ }
133
+ if (!recovered) {
134
+ character._needsRecovery = false;
135
+ break;
136
+ }
137
+ }
138
+ }
139
+ handleHit(actor, hit) {
140
+ const character = actor.getComponent(CharacterBody);
141
+ let kind;
142
+ if (character.motionMode === 'surface') {
143
+ const upDot = VectorOps.dotProduct(hit.normal, character.upDirection);
144
+ const threshold = Math.cos(character.maxSlopeAngle);
145
+ if (upDot >= threshold) {
146
+ kind = 'ground';
147
+ character.onGround = true;
148
+ character.groundNormal = hit.normal;
149
+ character.groundActor = hit.actor;
150
+ }
151
+ else if (upDot <= -threshold) {
152
+ kind = 'ceiling';
153
+ character.onCeiling = true;
154
+ }
155
+ else {
156
+ kind = 'wall';
157
+ character.onWall = true;
158
+ }
159
+ }
160
+ else {
161
+ kind = 'wall';
162
+ character.onWall = true;
163
+ }
164
+ actor.dispatchEvent(CharacterHit, {
165
+ actor: hit.actor,
166
+ point: hit.point,
167
+ normal: hit.normal,
168
+ distance: hit.distance,
169
+ kind,
170
+ });
171
+ }
172
+ move(actor, position, displacement) {
173
+ if (displacement.squaredMagnitude <= DISTANCE_EPSILON) {
174
+ return;
175
+ }
176
+ const character = actor.getComponent(CharacterBody);
177
+ for (let i = 0; i < character.maxSlides; i += 1) {
178
+ if (displacement.squaredMagnitude <= DISTANCE_EPSILON) {
179
+ break;
180
+ }
181
+ const distance = displacement.magnitude;
182
+ const hit = this.castMotion(actor, position, displacement);
183
+ if (!hit) {
184
+ position.add(displacement);
185
+ break;
186
+ }
187
+ const safeDistance = Math.max(hit.distance - character.skinWidth, 0);
188
+ const direction = displacement.clone().normalize();
189
+ position.x += direction.x * safeDistance;
190
+ position.y += direction.y * safeDistance;
191
+ this.handleHit(actor, hit);
192
+ clipAgainstNormal(character.velocity, hit.normal);
193
+ const remainingDistance = Math.max(distance - safeDistance, 0);
194
+ displacement.x = direction.x * remainingDistance;
195
+ displacement.y = direction.y * remainingDistance;
196
+ clipAgainstNormal(displacement, hit.normal);
197
+ }
198
+ }
199
+ updateGroundState(actor, target, skipSnapping) {
200
+ const character = actor.getComponent(CharacterBody);
201
+ if (skipSnapping) {
202
+ this.resetGroundState(character);
203
+ return;
204
+ }
205
+ const probeDistance = Math.max(character.groundSnapDistance, character.skinWidth);
206
+ const probeDirection = character.upDirection
207
+ .clone()
208
+ .multiplyNumber(-probeDistance);
209
+ const hit = this.castGround(actor, target, probeDirection);
210
+ if (!hit) {
211
+ this.resetGroundState(character);
212
+ return;
213
+ }
214
+ character.onGround = true;
215
+ character.groundNormal = hit.normal;
216
+ character.groundActor = hit.actor;
217
+ const snapDistance = Math.max(hit.distance - character.skinWidth, 0);
218
+ const snapDirection = character.upDirection
219
+ .clone()
220
+ .multiplyNumber(-snapDistance);
221
+ target.x += snapDirection.x;
222
+ target.y += snapDirection.y;
223
+ }
224
+ fixedUpdate(options) {
225
+ if (!this.world.systemApi.has(PhysicsAPI)) {
226
+ return;
227
+ }
228
+ const deltaTimeInSeconds = options.deltaTime / 1000;
229
+ this.oneWayValidator.update();
230
+ this.actorQuery.getActors().forEach((actor) => {
231
+ const transform = actor.getComponent(Transform);
232
+ const character = actor.getComponent(CharacterBody);
233
+ const rigidBody = actor.getComponent(RigidBody);
234
+ const collider = actor.getComponent(Collider);
235
+ this.resetState(character);
236
+ if (character.disabled ||
237
+ rigidBody.disabled ||
238
+ collider.disabled ||
239
+ rigidBody.type !== 'kinematic') {
240
+ character._displacement.multiplyNumber(0);
241
+ return;
242
+ }
243
+ const position = new Vector2(transform.world.position.x, transform.world.position.y);
244
+ this.recoverOverlaps(actor, position);
245
+ const displacement = character.velocity
246
+ .clone()
247
+ .multiplyNumber(deltaTimeInSeconds)
248
+ .add(character._displacement);
249
+ const movingUp = VectorOps.dotProduct(displacement, character.upDirection) >
250
+ SNAP_EPSILON;
251
+ this.move(actor, position, displacement);
252
+ this.updateGroundState(actor, position, movingUp || character.motionMode === 'free');
253
+ if (Math.abs(position.x - transform.world.position.x) > DISTANCE_EPSILON ||
254
+ Math.abs(position.y - transform.world.position.y) > DISTANCE_EPSILON) {
255
+ rigidBody.movePosition(position);
256
+ }
257
+ character._displacement.multiplyNumber(0);
258
+ });
259
+ this.oneWayValidator.lateUpdate();
260
+ }
261
+ }
262
+ CharacterController.systemName = 'CharacterController';
@@ -0,0 +1,2 @@
1
+ import { type Point } from '../../../engine/math-lib';
2
+ export declare const clipAgainstNormal: (vector: Point, normal: Point) => void;
@@ -0,0 +1,9 @@
1
+ import { VectorOps } from '../../../engine/math-lib';
2
+ export const clipAgainstNormal = (vector, normal) => {
3
+ const dot = VectorOps.dotProduct(vector, normal);
4
+ if (dot >= 0) {
5
+ return;
6
+ }
7
+ vector.x -= normal.x * dot;
8
+ vector.y -= normal.y * dot;
9
+ };
@@ -3,6 +3,7 @@ export { CameraSystem, CameraAPI } from './camera-system';
3
3
  export { GameStatsMeter } from './game-stats-meter';
4
4
  export { KeyboardInputSystem } from './keyboard-input-system';
5
5
  export { KeyboardControlSystem } from './keyboard-control-system';
6
+ export { CharacterController } from './character-controller';
6
7
  export { MouseControlSystem } from './mouse-control-system';
7
8
  export { MouseInputSystem } from './mouse-input-system';
8
9
  export { PhysicsSystem, PhysicsAPI } from './physics-system';
@@ -10,5 +11,5 @@ export { BehaviorSystem, Behavior } from './behavior-system';
10
11
  export type { BehaviorOptions } from './behavior-system';
11
12
  export { UIBridge } from './ui-bridge';
12
13
  export type { UIOptions, UIInitFn, UIDestroyFn, LoadUIFn } from './ui-bridge';
13
- export { AudioSystem } from './audio-system';
14
+ export { AudioSystem, AudioAPI } from './audio-system';
14
15
  export { Renderer, RendererAPI, FilterEffect, Shader } from './renderer';
@@ -3,10 +3,11 @@ export { CameraSystem, CameraAPI } from './camera-system';
3
3
  export { GameStatsMeter } from './game-stats-meter';
4
4
  export { KeyboardInputSystem } from './keyboard-input-system';
5
5
  export { KeyboardControlSystem } from './keyboard-control-system';
6
+ export { CharacterController } from './character-controller';
6
7
  export { MouseControlSystem } from './mouse-control-system';
7
8
  export { MouseInputSystem } from './mouse-input-system';
8
9
  export { PhysicsSystem, PhysicsAPI } from './physics-system';
9
10
  export { BehaviorSystem, Behavior } from './behavior-system';
10
11
  export { UIBridge } from './ui-bridge';
11
- export { AudioSystem } from './audio-system';
12
+ export { AudioSystem, AudioAPI } from './audio-system';
12
13
  export { Renderer, RendererAPI, FilterEffect, Shader } from './renderer';
@@ -1,6 +1,17 @@
1
- import type { Actor } from '../../../engine/actor';
2
- import type { CollisionDetectionSubsystem } from './subsystems';
3
- import type { CastHit, RaycastParams, OverlapParams, ShapeCastParams } from './types';
1
+ import type { Vector2 } from '../../../engine/math-lib';
2
+ import type { CastHit, OverlapHit, RaycastParams, OverlapParams, OverlapActorParams, ShapeCastParams, CastActorParams } from './types';
3
+ export interface PhysicsAPIHandlers {
4
+ raycast(params: RaycastParams): CastHit | null;
5
+ raycastAll(params: RaycastParams): CastHit[];
6
+ overlapShape(params: OverlapParams): OverlapHit[];
7
+ overlapActor(params: OverlapActorParams): OverlapHit[];
8
+ shapeCast(params: ShapeCastParams): CastHit | null;
9
+ shapeCastAll(params: ShapeCastParams): CastHit[];
10
+ castActor(params: CastActorParams): CastHit | null;
11
+ castActorAll(params: CastActorParams): CastHit[];
12
+ getGravity(): Vector2;
13
+ setGravity(gravity: Vector2): void;
14
+ }
4
15
  /**
5
16
  * API that provides methods for performing physics queries such as raycasting and overlap tests.
6
17
  *
@@ -12,8 +23,18 @@ import type { CastHit, RaycastParams, OverlapParams, ShapeCastParams } from './t
12
23
  * @category Systems
13
24
  */
14
25
  export declare class PhysicsAPI {
15
- private collisionDetectionSubsystem;
16
- constructor(collisionDetectionSubsystem: CollisionDetectionSubsystem);
26
+ private handlers;
27
+ constructor(handlers: PhysicsAPIHandlers);
28
+ /**
29
+ * Current gravity vector.
30
+ */
31
+ get gravity(): Vector2;
32
+ /**
33
+ * Sets the gravity vector.
34
+ *
35
+ * @param gravity - New gravity vector
36
+ */
37
+ set gravity(gravity: Vector2);
17
38
  /**
18
39
  * Casts a ray and returns the nearest hit, if any.
19
40
  *
@@ -35,12 +56,19 @@ export declare class PhysicsAPI {
35
56
  */
36
57
  raycastAll(params: RaycastParams): CastHit[];
37
58
  /**
38
- * Returns all actors whose colliders overlap the given query shape.
59
+ * Returns all collider intersections for the given query shape.
39
60
  *
40
61
  * @param params - Overlap parameters
41
- * @returns Actors whose colliders overlap the shape
62
+ * @returns All overlap hits
42
63
  */
43
- overlapShape(params: OverlapParams): Actor[];
64
+ overlapShape(params: OverlapParams): OverlapHit[];
65
+ /**
66
+ * Returns all collider intersections for an actor's collider.
67
+ *
68
+ * @param params - Actor overlap parameters
69
+ * @returns All overlap hits
70
+ */
71
+ overlapActor(params: OverlapActorParams): OverlapHit[];
44
72
  /**
45
73
  * Casts a shape and returns the nearest hit, if any.
46
74
  *
@@ -55,4 +83,18 @@ export declare class PhysicsAPI {
55
83
  * @returns All hits sorted from nearest to farthest
56
84
  */
57
85
  shapeCastAll(params: ShapeCastParams): CastHit[];
86
+ /**
87
+ * Casts an actor's collider and returns the nearest hit, if any.
88
+ *
89
+ * @param params - Actor cast parameters
90
+ * @returns The nearest hit or `null` when nothing is hit
91
+ */
92
+ castActor(params: CastActorParams): CastHit | null;
93
+ /**
94
+ * Casts an actor's collider and returns all hits sorted by distance.
95
+ *
96
+ * @param params - Actor cast parameters
97
+ * @returns All hits sorted from nearest to farthest
98
+ */
99
+ castActorAll(params: CastActorParams): CastHit[];
58
100
  }
@@ -9,9 +9,23 @@
9
9
  * @category Systems
10
10
  */
11
11
  export class PhysicsAPI {
12
- collisionDetectionSubsystem;
13
- constructor(collisionDetectionSubsystem) {
14
- this.collisionDetectionSubsystem = collisionDetectionSubsystem;
12
+ handlers;
13
+ constructor(handlers) {
14
+ this.handlers = handlers;
15
+ }
16
+ /**
17
+ * Current gravity vector.
18
+ */
19
+ get gravity() {
20
+ return this.handlers.getGravity();
21
+ }
22
+ /**
23
+ * Sets the gravity vector.
24
+ *
25
+ * @param gravity - New gravity vector
26
+ */
27
+ set gravity(gravity) {
28
+ this.handlers.setGravity(gravity);
15
29
  }
16
30
  /**
17
31
  * Casts a ray and returns the nearest hit, if any.
@@ -23,7 +37,7 @@ export class PhysicsAPI {
23
37
  * @returns The nearest hit or `null` when nothing is hit
24
38
  */
25
39
  raycast(params) {
26
- return this.collisionDetectionSubsystem.raycast(params);
40
+ return this.handlers.raycast(params);
27
41
  }
28
42
  /**
29
43
  * Casts a ray and returns all hits sorted by distance.
@@ -35,16 +49,25 @@ export class PhysicsAPI {
35
49
  * @returns All hits sorted from nearest to farthest
36
50
  */
37
51
  raycastAll(params) {
38
- return this.collisionDetectionSubsystem.raycastAll(params);
52
+ return this.handlers.raycastAll(params);
39
53
  }
40
54
  /**
41
- * Returns all actors whose colliders overlap the given query shape.
55
+ * Returns all collider intersections for the given query shape.
42
56
  *
43
57
  * @param params - Overlap parameters
44
- * @returns Actors whose colliders overlap the shape
58
+ * @returns All overlap hits
45
59
  */
46
60
  overlapShape(params) {
47
- return this.collisionDetectionSubsystem.overlapShape(params);
61
+ return this.handlers.overlapShape(params);
62
+ }
63
+ /**
64
+ * Returns all collider intersections for an actor's collider.
65
+ *
66
+ * @param params - Actor overlap parameters
67
+ * @returns All overlap hits
68
+ */
69
+ overlapActor(params) {
70
+ return this.handlers.overlapActor(params);
48
71
  }
49
72
  /**
50
73
  * Casts a shape and returns the nearest hit, if any.
@@ -53,7 +76,7 @@ export class PhysicsAPI {
53
76
  * @returns The nearest hit or `null` when nothing is hit
54
77
  */
55
78
  shapeCast(params) {
56
- return this.collisionDetectionSubsystem.shapeCast(params);
79
+ return this.handlers.shapeCast(params);
57
80
  }
58
81
  /**
59
82
  * Casts a shape and returns all hits sorted by distance.
@@ -62,6 +85,24 @@ export class PhysicsAPI {
62
85
  * @returns All hits sorted from nearest to farthest
63
86
  */
64
87
  shapeCastAll(params) {
65
- return this.collisionDetectionSubsystem.shapeCastAll(params);
88
+ return this.handlers.shapeCastAll(params);
89
+ }
90
+ /**
91
+ * Casts an actor's collider and returns the nearest hit, if any.
92
+ *
93
+ * @param params - Actor cast parameters
94
+ * @returns The nearest hit or `null` when nothing is hit
95
+ */
96
+ castActor(params) {
97
+ return this.handlers.castActor(params);
98
+ }
99
+ /**
100
+ * Casts an actor's collider and returns all hits sorted by distance.
101
+ *
102
+ * @param params - Actor cast parameters
103
+ * @returns All hits sorted from nearest to farthest
104
+ */
105
+ castActorAll(params) {
106
+ return this.handlers.castActorAll(params);
66
107
  }
67
108
  }
@@ -16,6 +16,7 @@ export declare class PhysicsSystem extends SceneSystem {
16
16
  private collisionBroadcastSubsystem;
17
17
  private constraintSolver;
18
18
  private physicsApi;
19
+ private gravity;
19
20
  constructor(options: SceneSystemOptions);
20
21
  onSceneEnter(): void;
21
22
  onSceneExit(): void;
@@ -1,4 +1,5 @@
1
1
  import { SceneSystem } from '../../../engine/system';
2
+ import { Vector2 } from '../../../engine/math-lib';
2
3
  import { PhysicsSubsystem, CollisionDetectionSubsystem, CollisionBroadcastSubsystem, ConstraintSolver, } from './subsystems';
3
4
  import { PhysicsAPI } from './api';
4
5
  /**
@@ -17,14 +18,33 @@ export class PhysicsSystem extends SceneSystem {
17
18
  collisionBroadcastSubsystem;
18
19
  constraintSolver;
19
20
  physicsApi;
21
+ gravity;
20
22
  constructor(options) {
21
23
  super();
24
+ const { gravityX = 0, gravityY = 0 } = options;
25
+ this.gravity = new Vector2(gravityX, gravityY);
22
26
  this.world = options.world;
23
- this.physicsSubsystem = new PhysicsSubsystem(options);
27
+ this.physicsSubsystem = new PhysicsSubsystem({
28
+ scene: options.scene,
29
+ getGravity: () => this.gravity,
30
+ });
24
31
  this.collisionDetectionSubsystem = new CollisionDetectionSubsystem(options);
25
32
  this.collisionBroadcastSubsystem = new CollisionBroadcastSubsystem();
26
33
  this.constraintSolver = new ConstraintSolver();
27
- this.physicsApi = new PhysicsAPI(this.collisionDetectionSubsystem);
34
+ this.physicsApi = new PhysicsAPI({
35
+ raycast: (params) => this.collisionDetectionSubsystem.raycast(params),
36
+ raycastAll: (params) => this.collisionDetectionSubsystem.raycastAll(params),
37
+ overlapShape: (params) => this.collisionDetectionSubsystem.overlapShape(params),
38
+ overlapActor: (params) => this.collisionDetectionSubsystem.overlapActor(params),
39
+ shapeCast: (params) => this.collisionDetectionSubsystem.shapeCast(params),
40
+ shapeCastAll: (params) => this.collisionDetectionSubsystem.shapeCastAll(params),
41
+ castActor: (params) => this.collisionDetectionSubsystem.castActor(params),
42
+ castActorAll: (params) => this.collisionDetectionSubsystem.castActorAll(params),
43
+ getGravity: () => this.gravity,
44
+ setGravity: (gravity) => {
45
+ this.gravity = gravity;
46
+ },
47
+ });
28
48
  }
29
49
  onSceneEnter() {
30
50
  this.world.systemApi.register(this.physicsApi);
@@ -33,7 +53,6 @@ export class PhysicsSystem extends SceneSystem {
33
53
  this.world.systemApi.unregister(PhysicsAPI);
34
54
  }
35
55
  onSceneDestroy() {
36
- this.world.systemApi.unregister(PhysicsAPI);
37
56
  this.physicsSubsystem.destroy();
38
57
  this.collisionDetectionSubsystem.destroy();
39
58
  }
@@ -1,3 +1,5 @@
1
+ import type { Collider, Transform } from '../../../../../components';
1
2
  import type { BoxCastGeometry } from '../types';
2
- import type { BoxCastParams } from '../../../types';
3
- export declare function buildBoxCastGeometry(shapeCast: BoxCastParams): BoxCastGeometry;
3
+ import type { BoxCastParams, CastActorParams } from '../../../types';
4
+ export declare function buildBoxCastGeometry(castParams: BoxCastParams): BoxCastGeometry;
5
+ export declare function buildBoxCastGeometry(collider: Collider, transform: Transform, castParams: CastActorParams): BoxCastGeometry;
@@ -1,6 +1,9 @@
1
1
  import { buildBoxGeometry } from './build-box-geometry';
2
- export function buildBoxCastGeometry(shapeCast) {
3
- const box = buildBoxGeometry(shapeCast);
2
+ export function buildBoxCastGeometry(castParamsOrCollider, transform, castParams) {
3
+ const params = castParams ?? castParamsOrCollider;
4
+ const box = transform !== undefined
5
+ ? buildBoxGeometry(castParamsOrCollider, transform, castParams)
6
+ : buildBoxGeometry(castParamsOrCollider);
4
7
  let minX = Infinity;
5
8
  let maxX = -Infinity;
6
9
  let minY = Infinity;
@@ -14,8 +17,8 @@ export function buildBoxCastGeometry(shapeCast) {
14
17
  return {
15
18
  ...box,
16
19
  origin: box.center,
17
- direction: shapeCast.direction.normalize(),
18
- maxDistance: shapeCast.maxDistance,
20
+ direction: params.direction.clone().normalize(),
21
+ maxDistance: params.maxDistance,
19
22
  halfExtents: {
20
23
  x: (maxX - minX) / 2,
21
24
  y: (maxY - minY) / 2,
@@ -1,5 +1,5 @@
1
1
  import type { Collider, Transform } from '../../../../../components';
2
- import type { BoxGeometry } from '../types';
3
- import type { OverlapBoxParams } from '../../../types';
4
- export declare function buildBoxGeometry(overlap: OverlapBoxParams): BoxGeometry;
5
- export declare function buildBoxGeometry(collider: Collider, transform: Transform): BoxGeometry;
2
+ import type { ActorGeometryParams, BoxGeometry } from '../types';
3
+ import type { OverlapBoxParams, BoxCastParams } from '../../../types';
4
+ export declare function buildBoxGeometry(queryParams: OverlapBoxParams | BoxCastParams): BoxGeometry;
5
+ export declare function buildBoxGeometry(collider: Collider, transform: Transform, params?: ActorGeometryParams): BoxGeometry;
@@ -1,7 +1,7 @@
1
1
  import { VectorOps } from '../../../../../../engine/math-lib';
2
- export function buildBoxGeometry(colliderOrOverlap, transform) {
3
- let centerX;
4
- let centerY;
2
+ export function buildBoxGeometry(colliderOrQueryParams, transform, params) {
3
+ let offsetX;
4
+ let offsetY;
5
5
  let sizeX;
6
6
  let sizeY;
7
7
  let positionX;
@@ -10,29 +10,29 @@ export function buildBoxGeometry(colliderOrOverlap, transform) {
10
10
  let scaleX;
11
11
  let scaleY;
12
12
  if (transform !== undefined) {
13
- const collider = colliderOrOverlap;
13
+ const collider = colliderOrQueryParams;
14
14
  if (collider.shape.type !== 'box') {
15
15
  throw new Error(`Expected box collider, got ${collider.shape.type}.`);
16
16
  }
17
- centerX = collider.offset.x;
18
- centerY = collider.offset.y;
17
+ offsetX = collider.offset.x;
18
+ offsetY = collider.offset.y;
19
19
  sizeX = collider.shape.size.x;
20
20
  sizeY = collider.shape.size.y;
21
- positionX = transform.world.position.x;
22
- positionY = transform.world.position.y;
21
+ positionX = transform.world.position.x + (params?.offset?.x ?? 0);
22
+ positionY = transform.world.position.y + (params?.offset?.y ?? 0);
23
23
  rotation = transform.world.rotation;
24
24
  scaleX = transform.world.scale.x;
25
25
  scaleY = transform.world.scale.y;
26
26
  }
27
27
  else {
28
- const overlap = colliderOrOverlap.shape;
29
- centerX = 0;
30
- centerY = 0;
31
- sizeX = overlap.size.x;
32
- sizeY = overlap.size.y;
33
- positionX = overlap.center.x;
34
- positionY = overlap.center.y;
35
- rotation = overlap.rotation ?? 0;
28
+ const queryShape = colliderOrQueryParams.shape;
29
+ offsetX = 0;
30
+ offsetY = 0;
31
+ sizeX = queryShape.size.x;
32
+ sizeY = queryShape.size.y;
33
+ positionX = queryShape.center.x;
34
+ positionY = queryShape.center.y;
35
+ rotation = queryShape.rotation ?? 0;
36
36
  scaleX = 1;
37
37
  scaleY = 1;
38
38
  }
@@ -42,12 +42,12 @@ export function buildBoxGeometry(colliderOrOverlap, transform) {
42
42
  const y2 = sizeY / 2;
43
43
  const cos = Math.cos(rotation);
44
44
  const sin = Math.sin(rotation);
45
- centerX += positionX;
46
- centerY += positionY;
47
- const center = {
48
- x: centerX,
49
- y: centerY,
50
- };
45
+ const center = VectorOps.rotatePoint({
46
+ x: offsetX * scaleX,
47
+ y: offsetY * scaleY,
48
+ }, rotation);
49
+ center.x += positionX;
50
+ center.y += positionY;
51
51
  const points = [
52
52
  { x: x1, y: y1 },
53
53
  { x: x1, y: y2 },
@@ -1,3 +1,5 @@
1
+ import type { Collider, Transform } from '../../../../../components';
1
2
  import type { CapsuleCastGeometry } from '../types';
2
- import type { CapsuleCastParams } from '../../../types';
3
- export declare function buildCapsuleCastGeometry(shapeCast: CapsuleCastParams): CapsuleCastGeometry;
3
+ import type { CapsuleCastParams, CastActorParams } from '../../../types';
4
+ export declare function buildCapsuleCastGeometry(castParams: CapsuleCastParams): CapsuleCastGeometry;
5
+ export declare function buildCapsuleCastGeometry(collider: Collider, transform: Transform, castParams: CastActorParams): CapsuleCastGeometry;