dacha 0.17.2 → 0.18.0-alpha.0

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 (53) hide show
  1. package/build/contrib/components/collider/index.d.ts +6 -2
  2. package/build/contrib/components/collider/index.js +7 -2
  3. package/build/contrib/components/rigid-body/index.d.ts +29 -17
  4. package/build/contrib/components/rigid-body/index.js +64 -21
  5. package/build/contrib/events/index.d.ts +10 -75
  6. package/build/contrib/events/index.js +0 -36
  7. package/build/contrib/systems/physics-system/index.d.ts +0 -1
  8. package/build/contrib/systems/physics-system/index.js +6 -11
  9. package/build/contrib/systems/physics-system/subsystems/collision-broadcast/collision.d.ts +5 -3
  10. package/build/contrib/systems/physics-system/subsystems/collision-broadcast/collision.js +7 -5
  11. package/build/contrib/systems/physics-system/subsystems/collision-broadcast/index.d.ts +4 -6
  12. package/build/contrib/systems/physics-system/subsystems/collision-broadcast/index.js +20 -17
  13. package/build/contrib/systems/physics-system/subsystems/collision-detection/index.d.ts +12 -9
  14. package/build/contrib/systems/physics-system/subsystems/collision-detection/index.js +107 -93
  15. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-box/check-boxes-intersection.d.ts +10 -0
  16. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-box/check-boxes-intersection.js +36 -0
  17. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-box/utils.d.ts +23 -0
  18. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-box/utils.js +143 -0
  19. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-circle/check-box-and-circle-intersection.d.ts +9 -0
  20. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-circle/check-box-and-circle-intersection.js +47 -0
  21. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/circle-circle/check-circles-intersection.d.ts +12 -0
  22. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/circle-circle/check-circles-intersection.js +49 -0
  23. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/index.d.ts +2 -2
  24. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/index.js +3 -3
  25. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/tests/helpers.d.ts +17 -0
  26. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/tests/helpers.js +66 -0
  27. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/utils.d.ts +4 -0
  28. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/utils.js +9 -0
  29. package/build/contrib/systems/physics-system/subsystems/collision-detection/reorientation-checkers/check-collider.js +3 -0
  30. package/build/contrib/systems/physics-system/subsystems/collision-detection/types.d.ts +14 -5
  31. package/build/contrib/systems/physics-system/subsystems/collision-detection/types.js +0 -3
  32. package/build/contrib/systems/physics-system/subsystems/constraint-solver/index.d.ts +7 -10
  33. package/build/contrib/systems/physics-system/subsystems/constraint-solver/index.js +117 -79
  34. package/build/contrib/systems/physics-system/subsystems/index.d.ts +0 -1
  35. package/build/contrib/systems/physics-system/subsystems/index.js +0 -1
  36. package/build/contrib/systems/physics-system/subsystems/physics/index.d.ts +3 -9
  37. package/build/contrib/systems/physics-system/subsystems/physics/index.js +57 -93
  38. package/build/contrib/systems/physics-system/types.d.ts +9 -0
  39. package/build/engine/math-lib/vector/ops.d.ts +3 -3
  40. package/build/engine/math-lib/vector/ops.js +8 -5
  41. package/build/engine/math-lib/vector/vector2.d.ts +22 -7
  42. package/build/engine/math-lib/vector/vector2.js +29 -5
  43. package/build/events/index.d.ts +2 -2
  44. package/build/events/index.js +1 -1
  45. package/package.json +1 -1
  46. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/check-box-and-circle-intersection.d.ts +0 -16
  47. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/check-box-and-circle-intersection.js +0 -80
  48. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/check-boxes-intersection.d.ts +0 -6
  49. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/check-boxes-intersection.js +0 -72
  50. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/check-circles-intersection.d.ts +0 -11
  51. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/check-circles-intersection.js +0 -39
  52. package/build/contrib/systems/physics-system/subsystems/collision-solver/index.d.ts +0 -10
  53. package/build/contrib/systems/physics-system/subsystems/collision-solver/index.js +0 -50
@@ -1,6 +1,6 @@
1
- import { checkBoxAndCircleIntersection } from './check-box-and-circle-intersection';
2
- import { checkBoxesIntersection } from './check-boxes-intersection';
3
- import { checkCirclesIntersection } from './check-circles-intersection';
1
+ import { checkBoxAndCircleIntersection } from './box-circle/check-box-and-circle-intersection';
2
+ import { checkBoxesIntersection } from './box-box/check-boxes-intersection';
3
+ import { checkCirclesIntersection } from './circle-circle/check-circles-intersection';
4
4
  export const intersectionCheckers = {
5
5
  box: {
6
6
  box: checkBoxesIntersection,
@@ -0,0 +1,17 @@
1
+ import type { BoxGeometry, CircleGeometry, Intersection, Proxy } from '../../types';
2
+ export declare const createBoxGeometry: (centerX: number, centerY: number, sizeX: number, sizeY: number) => BoxGeometry;
3
+ export declare const createRotatedBoxGeometry: (centerX: number, centerY: number, sizeX: number, sizeY: number, rotation: number) => BoxGeometry;
4
+ export declare const createCircleGeometry: (centerX: number, centerY: number, radius: number) => CircleGeometry;
5
+ export declare const createProxy: (type: 'box' | 'circle', geometry: BoxGeometry | CircleGeometry) => Proxy;
6
+ export declare const expectToBeClose: (point: {
7
+ x: number;
8
+ y: number;
9
+ }, x: number, y: number, digits?: number) => void;
10
+ export declare const expectIntersection: (intersection: Intersection | false) => Intersection;
11
+ export declare const sortPoints: (points: {
12
+ x: number;
13
+ y: number;
14
+ }[]) => {
15
+ x: number;
16
+ y: number;
17
+ }[];
@@ -0,0 +1,66 @@
1
+ import { VectorOps } from '../../../../../../../engine/math-lib';
2
+ export const createBoxGeometry = (centerX, centerY, sizeX, sizeY) => {
3
+ return createRotatedBoxGeometry(centerX, centerY, sizeX, sizeY, 0);
4
+ };
5
+ export const createRotatedBoxGeometry = (centerX, centerY, sizeX, sizeY, rotation) => {
6
+ const halfX = sizeX / 2;
7
+ const halfY = sizeY / 2;
8
+ const cos = Math.cos(rotation);
9
+ const sin = Math.sin(rotation);
10
+ const points = [
11
+ { x: -halfX, y: -halfY },
12
+ { x: -halfX, y: halfY },
13
+ { x: halfX, y: halfY },
14
+ { x: halfX, y: -halfY },
15
+ ];
16
+ for (const point of points) {
17
+ const rotatedX = point.x * cos - point.y * sin;
18
+ const rotatedY = point.x * sin + point.y * cos;
19
+ point.x = rotatedX + centerX;
20
+ point.y = rotatedY + centerY;
21
+ }
22
+ const edges = points.map((point1, index, array) => {
23
+ const point2 = array[(index + 1) % array.length];
24
+ return {
25
+ point1,
26
+ point2,
27
+ normal: VectorOps.getNormal(point1.x, point2.x, point1.y, point2.y),
28
+ };
29
+ });
30
+ return {
31
+ center: {
32
+ x: centerX,
33
+ y: centerY,
34
+ },
35
+ points,
36
+ edges,
37
+ };
38
+ };
39
+ export const createCircleGeometry = (centerX, centerY, radius) => ({
40
+ center: { x: centerX, y: centerY },
41
+ radius,
42
+ });
43
+ export const createProxy = (type, geometry) => ({
44
+ actor: {
45
+ id: `${type}-${Math.random()}`,
46
+ getComponent: () => ({ type }),
47
+ },
48
+ geometry,
49
+ });
50
+ export const expectToBeClose = (point, x, y, digits = 6) => {
51
+ expect(point.x).toBeCloseTo(x, digits);
52
+ expect(point.y).toBeCloseTo(y, digits);
53
+ };
54
+ export const expectIntersection = (intersection) => {
55
+ expect(intersection).not.toBe(false);
56
+ if (intersection === false) {
57
+ throw new Error('Expected intersection, received false');
58
+ }
59
+ return intersection;
60
+ };
61
+ export const sortPoints = (points) => [...points].sort((point1, point2) => {
62
+ if (point1.x !== point2.x) {
63
+ return point1.x - point2.x;
64
+ }
65
+ return point1.y - point2.y;
66
+ });
@@ -0,0 +1,4 @@
1
+ import { Vector2 } from '../../../../../../engine/math-lib';
2
+ import type { Point } from '../types';
3
+ export declare const INTERSECTION_EPSILON = 0.000001;
4
+ export declare const orientNormal: (normal: Vector2, from: Point, to: Point) => Vector2;
@@ -0,0 +1,9 @@
1
+ import { Vector2, VectorOps } from '../../../../../../engine/math-lib';
2
+ export const INTERSECTION_EPSILON = 1e-6;
3
+ export const orientNormal = (normal, from, to) => {
4
+ const direction = new Vector2(to.x - from.x, to.y - from.y);
5
+ if (VectorOps.dotProduct(direction, normal) < 0) {
6
+ return normal.clone().multiplyNumber(-1);
7
+ }
8
+ return normal;
9
+ };
@@ -2,6 +2,9 @@ export const checkCollider = (collider, colliderOld) => {
2
2
  if (collider.type !== colliderOld.type) {
3
3
  return true;
4
4
  }
5
+ if (collider.layer !== colliderOld.layer) {
6
+ return true;
7
+ }
5
8
  if (collider.type === 'box') {
6
9
  return collider.centerX !== colliderOld.centerX
7
10
  || collider.centerY !== colliderOld.centerY
@@ -41,9 +41,10 @@ export interface OrientationData {
41
41
  radius?: number;
42
42
  sizeX?: number;
43
43
  sizeY?: number;
44
+ layer: string;
44
45
  };
45
46
  }
46
- export interface CollisionEntry {
47
+ export interface Proxy {
47
48
  actor: Actor;
48
49
  aabb: AABB;
49
50
  geometry: Geometry;
@@ -51,7 +52,7 @@ export interface CollisionEntry {
51
52
  edges: Record<Axis, [SortedItem, SortedItem]>;
52
53
  }
53
54
  export interface SortedItem {
54
- entry: CollisionEntry;
55
+ proxy: Proxy;
55
56
  value: number;
56
57
  }
57
58
  export interface AxisEntry {
@@ -63,8 +64,16 @@ export interface Axes {
63
64
  x: AxisEntry;
64
65
  y: AxisEntry;
65
66
  }
66
- export type CollisionPair = [CollisionEntry, CollisionEntry];
67
+ export type ProxyPair = [Proxy, Proxy];
68
+ export interface Contact {
69
+ actor1: Actor;
70
+ actor2: Actor;
71
+ normal: Vector2;
72
+ penetration: number;
73
+ contactPoints: Point[];
74
+ }
67
75
  export interface Intersection {
68
- mtv1: Vector2;
69
- mtv2: Vector2;
76
+ normal: Vector2;
77
+ penetration: number;
78
+ contactPoints: Point[];
70
79
  }
@@ -1,13 +1,10 @@
1
- import type { SceneSystemOptions } from '../../../../../engine/system';
1
+ import type { Contact } from '../collision-detection/types';
2
2
  export declare class ConstraintSolver {
3
- private scene;
4
- private processedPairs;
5
- private mtvMap;
6
- constructor(options: SceneSystemOptions);
7
- destroy(): void;
8
- private handleCollision;
3
+ private validContacts;
9
4
  private validateCollision;
10
- private setMtv;
11
- private resolveCollision;
12
- update(): void;
5
+ private getInverseMass;
6
+ private applyNormalImpulse;
7
+ private applyFrictionImpulse;
8
+ private applyPositionCorrection;
9
+ update(contacts: Contact[]): void;
13
10
  }
@@ -1,36 +1,12 @@
1
- import { Vector2 } from '../../../../../engine/math-lib';
2
1
  import { RigidBody } from '../../../../components/rigid-body';
3
2
  import { Transform } from '../../../../components/transform';
4
- import { Collision } from '../../../../events';
5
3
  import { RIGID_BODY_TYPE } from '../../consts';
4
+ const SOLVER_ITERATIONS = 8;
5
+ const POSITION_CORRECTION_PERCENT = 0.8;
6
+ const PENETRATION_SLOP = 0.01;
7
+ const DEFAULT_CONTACT_FRICTION = 0.6;
6
8
  export class ConstraintSolver {
7
- scene;
8
- processedPairs;
9
- mtvMap;
10
- constructor(options) {
11
- this.scene = options.scene;
12
- this.processedPairs = new Map();
13
- this.mtvMap = new Map();
14
- this.scene.addEventListener(Collision, this.handleCollision);
15
- }
16
- destroy() {
17
- this.scene.removeEventListener(Collision, this.handleCollision);
18
- }
19
- handleCollision = (event) => {
20
- const { actor1, actor2, mtv1, mtv2 } = event;
21
- if (this.processedPairs.has(actor2) &&
22
- this.processedPairs.get(actor2).has(actor1)) {
23
- return;
24
- }
25
- if (!this.processedPairs.has(actor1)) {
26
- this.processedPairs.set(actor1, new Set());
27
- }
28
- this.processedPairs.get(actor1).add(actor2);
29
- if (!this.validateCollision(actor1, actor2)) {
30
- return;
31
- }
32
- this.resolveCollision(actor1, actor2, mtv1, mtv2);
33
- };
9
+ validContacts = [];
34
10
  validateCollision(actor1, actor2) {
35
11
  const rigidBody1 = actor1.getComponent(RigidBody);
36
12
  const rigidBody2 = actor2.getComponent(RigidBody);
@@ -41,68 +17,130 @@ export class ConstraintSolver {
41
17
  rigidBody2.type === RIGID_BODY_TYPE.STATIC) {
42
18
  return false;
43
19
  }
44
- if (rigidBody1.type === RIGID_BODY_TYPE.STATIC ||
45
- rigidBody2.type === RIGID_BODY_TYPE.STATIC) {
46
- return !rigidBody1.ghost && !rigidBody2.ghost;
20
+ return true;
21
+ }
22
+ getInverseMass(rigidBody) {
23
+ if (rigidBody.disabled || rigidBody.type === RIGID_BODY_TYPE.STATIC) {
24
+ return 0;
25
+ }
26
+ return rigidBody.inverseMass;
27
+ }
28
+ applyNormalImpulse(contact) {
29
+ const { actor1, actor2, normal } = contact;
30
+ const rigidBody1 = actor1.getComponent(RigidBody);
31
+ const rigidBody2 = actor2.getComponent(RigidBody);
32
+ const inverseMass1 = this.getInverseMass(rigidBody1);
33
+ const inverseMass2 = this.getInverseMass(rigidBody2);
34
+ const inverseMassSum = inverseMass1 + inverseMass2;
35
+ if (inverseMassSum === 0) {
36
+ return 0;
37
+ }
38
+ const relativeVelocityX = rigidBody2.linearVelocity.x - rigidBody1.linearVelocity.x;
39
+ const relativeVelocityY = rigidBody2.linearVelocity.y - rigidBody1.linearVelocity.y;
40
+ const velocityAlongNormal = relativeVelocityX * normal.x + relativeVelocityY * normal.y;
41
+ if (velocityAlongNormal >= 0) {
42
+ return 0;
43
+ }
44
+ const impulseMagnitude = -velocityAlongNormal / inverseMassSum;
45
+ const impulseX = normal.x * impulseMagnitude;
46
+ const impulseY = normal.y * impulseMagnitude;
47
+ if (inverseMass1 > 0) {
48
+ rigidBody1.linearVelocity.x -= impulseX * inverseMass1;
49
+ rigidBody1.linearVelocity.y -= impulseY * inverseMass1;
47
50
  }
48
- return (!rigidBody1.ghost &&
49
- !rigidBody1.isPermeable &&
50
- !rigidBody2.ghost &&
51
- !rigidBody2.isPermeable);
51
+ if (inverseMass2 > 0) {
52
+ rigidBody2.linearVelocity.x += impulseX * inverseMass2;
53
+ rigidBody2.linearVelocity.y += impulseY * inverseMass2;
54
+ }
55
+ return impulseMagnitude;
52
56
  }
53
- setMtv(actor, mtvX, mtvY, type) {
54
- if (!this.mtvMap.has(actor)) {
55
- this.mtvMap.set(actor, {});
57
+ applyFrictionImpulse(contact, normalImpulseMagnitude) {
58
+ if (normalImpulseMagnitude <= 0) {
59
+ return;
60
+ }
61
+ const { normal } = contact;
62
+ const rigidBody1 = contact.actor1.getComponent(RigidBody);
63
+ const rigidBody2 = contact.actor2.getComponent(RigidBody);
64
+ const inverseMass1 = this.getInverseMass(rigidBody1);
65
+ const inverseMass2 = this.getInverseMass(rigidBody2);
66
+ const inverseMassSum = inverseMass1 + inverseMass2;
67
+ if (inverseMassSum === 0) {
68
+ return;
56
69
  }
57
- const mtvs = this.mtvMap.get(actor);
58
- if (!mtvs?.[type]) {
59
- mtvs[type] = new Vector2(mtvX, mtvY);
70
+ const relativeVelocityX = rigidBody2.linearVelocity.x - rigidBody1.linearVelocity.x;
71
+ const relativeVelocityY = rigidBody2.linearVelocity.y - rigidBody1.linearVelocity.y;
72
+ const velocityAlongNormal = relativeVelocityX * normal.x + relativeVelocityY * normal.y;
73
+ const tangentX = relativeVelocityX - normal.x * velocityAlongNormal;
74
+ const tangentY = relativeVelocityY - normal.y * velocityAlongNormal;
75
+ const tangentMagnitude = Math.sqrt(tangentX ** 2 + tangentY ** 2);
76
+ if (tangentMagnitude === 0) {
60
77
  return;
61
78
  }
62
- switch (type) {
63
- case 'static':
64
- mtvs[type].x =
65
- Math.abs(mtvX) > Math.abs(mtvs[type].x) ? mtvX : mtvs[type].x;
66
- mtvs[type].y =
67
- Math.abs(mtvY) > Math.abs(mtvs[type].y) ? mtvY : mtvs[type].y;
68
- break;
69
- case 'dynamic':
70
- mtvs[type].x += mtvX;
71
- mtvs[type].y += mtvY;
79
+ const normalizedTangentX = tangentX / tangentMagnitude;
80
+ const normalizedTangentY = tangentY / tangentMagnitude;
81
+ const velocityAlongTangent = relativeVelocityX * normalizedTangentX +
82
+ relativeVelocityY * normalizedTangentY;
83
+ const unclampedImpulseMagnitude = -velocityAlongTangent / inverseMassSum;
84
+ const maxFrictionImpulseMagnitude = DEFAULT_CONTACT_FRICTION * normalImpulseMagnitude;
85
+ const impulseMagnitude = Math.max(-maxFrictionImpulseMagnitude, Math.min(unclampedImpulseMagnitude, maxFrictionImpulseMagnitude));
86
+ const impulseX = normalizedTangentX * impulseMagnitude;
87
+ const impulseY = normalizedTangentY * impulseMagnitude;
88
+ if (inverseMass1 > 0) {
89
+ rigidBody1.linearVelocity.x -= impulseX * inverseMass1;
90
+ rigidBody1.linearVelocity.y -= impulseY * inverseMass1;
91
+ }
92
+ if (inverseMass2 > 0) {
93
+ rigidBody2.linearVelocity.x += impulseX * inverseMass2;
94
+ rigidBody2.linearVelocity.y += impulseY * inverseMass2;
72
95
  }
73
96
  }
74
- resolveCollision(actor1, actor2, mtv1, mtv2) {
97
+ applyPositionCorrection(contact) {
98
+ const { actor1, actor2, normal, penetration } = contact;
75
99
  const rigidBody1 = actor1.getComponent(RigidBody);
76
100
  const rigidBody2 = actor2.getComponent(RigidBody);
77
- if (rigidBody1.type === RIGID_BODY_TYPE.STATIC) {
78
- this.setMtv(actor2, mtv2.x, mtv2.y, rigidBody1.type);
101
+ const inverseMass1 = this.getInverseMass(rigidBody1);
102
+ const inverseMass2 = this.getInverseMass(rigidBody2);
103
+ const inverseMassSum = inverseMass1 + inverseMass2;
104
+ if (inverseMassSum === 0) {
105
+ return;
79
106
  }
80
- else if (rigidBody2.type === RIGID_BODY_TYPE.STATIC) {
81
- this.setMtv(actor1, mtv1.x, mtv1.y, rigidBody2.type);
107
+ const correctionMagnitude = (Math.max(penetration - PENETRATION_SLOP, 0) *
108
+ POSITION_CORRECTION_PERCENT) /
109
+ inverseMassSum;
110
+ if (correctionMagnitude === 0) {
111
+ return;
112
+ }
113
+ const correctionX = normal.x * correctionMagnitude;
114
+ const correctionY = normal.y * correctionMagnitude;
115
+ const transform1 = actor1.getComponent(Transform);
116
+ const transform2 = actor2.getComponent(Transform);
117
+ if (inverseMass1 > 0) {
118
+ transform1.world.position.x -= correctionX * inverseMass1;
119
+ transform1.world.position.y -= correctionY * inverseMass1;
82
120
  }
83
- else {
84
- this.setMtv(actor1, mtv1.x / 2, mtv1.y / 2, rigidBody2.type);
85
- this.setMtv(actor2, mtv2.x / 2, mtv2.y / 2, rigidBody1.type);
121
+ if (inverseMass2 > 0) {
122
+ transform2.world.position.x += correctionX * inverseMass2;
123
+ transform2.world.position.y += correctionY * inverseMass2;
86
124
  }
87
125
  }
88
- update() {
89
- for (const [actor, entry] of this.mtvMap) {
90
- const transform = actor.getComponent(Transform);
91
- const { static: staticMtv, dynamic: dynamicMtv } = entry;
92
- /*
93
- * TODO:: Enable this part when it will be possible to run
94
- * phycics pipeline several times per single game loop iteration
95
- */
96
- // transform.world.position.x += Math.sign(staticMtv.x) === Math.sign(dynamicMtv.x)
97
- // ? staticMtv.x + dynamicMtv.x
98
- // : staticMtv.x || dynamicMtv.x;
99
- // transform.world.position.y += Math.sign(staticMtv.y) === Math.sign(dynamicMtv.y)
100
- // ? staticMtv.y + dynamicMtv.y
101
- // : staticMtv.y || dynamicMtv.y;
102
- transform.world.position.x += (staticMtv?.x ?? 0) + (dynamicMtv?.x ?? 0);
103
- transform.world.position.y += (staticMtv?.y ?? 0) + (dynamicMtv?.y ?? 0);
104
- }
105
- this.processedPairs.clear();
106
- this.mtvMap.clear();
126
+ update(contacts) {
127
+ let validContactsCount = 0;
128
+ contacts.forEach((contact) => {
129
+ if (!this.validateCollision(contact.actor1, contact.actor2)) {
130
+ return;
131
+ }
132
+ this.validContacts[validContactsCount] = contact;
133
+ validContactsCount += 1;
134
+ });
135
+ this.validContacts.length = validContactsCount;
136
+ for (let iteration = 0; iteration < SOLVER_ITERATIONS; iteration += 1) {
137
+ this.validContacts.forEach((contact) => {
138
+ const normalImpulseMagnitude = this.applyNormalImpulse(contact);
139
+ this.applyFrictionImpulse(contact, normalImpulseMagnitude);
140
+ });
141
+ }
142
+ this.validContacts.forEach((contact) => {
143
+ this.applyPositionCorrection(contact);
144
+ });
107
145
  }
108
146
  }
@@ -1,5 +1,4 @@
1
1
  export { PhysicsSubsystem } from './physics';
2
2
  export { CollisionDetectionSubsystem } from './collision-detection';
3
3
  export { CollisionBroadcastSubsystem } from './collision-broadcast';
4
- export { CollisionSolver } from './collision-solver';
5
4
  export { ConstraintSolver } from './constraint-solver';
@@ -1,5 +1,4 @@
1
1
  export { PhysicsSubsystem } from './physics';
2
2
  export { CollisionDetectionSubsystem } from './collision-detection';
3
3
  export { CollisionBroadcastSubsystem } from './collision-broadcast';
4
- export { CollisionSolver } from './collision-solver';
5
4
  export { ConstraintSolver } from './constraint-solver';
@@ -1,17 +1,11 @@
1
1
  import type { SceneSystemOptions, UpdateOptions } from '../../../../../engine/system';
2
2
  export declare class PhysicsSubsystem {
3
- private scene;
4
3
  private actorQuery;
5
4
  private gravity;
6
- private actorVectors;
7
5
  constructor(options: SceneSystemOptions);
8
6
  destroy(): void;
9
- private handleActorRemove;
10
- private handleStopMovement;
11
- private handleAddForce;
12
- private handleAddImpulse;
13
- private setUpVectors;
14
- private applyDragForce;
15
- private getGravityForce;
7
+ private applyLinearDamping;
8
+ private integrateVelocities;
9
+ private integratePositions;
16
10
  update(options: UpdateOptions): void;
17
11
  }
@@ -1,125 +1,89 @@
1
- import { Vector2 } from '../../../../../engine/math-lib';
2
1
  import { ActorQuery } from '../../../../../engine/actor';
3
2
  import { RigidBody } from '../../../../components/rigid-body';
4
3
  import { Transform } from '../../../../components/transform';
5
- import { AddForce, AddImpulse, StopMovement } from '../../../../events';
6
- import { RemoveActor } from '../../../../../engine/events';
7
- const DIRECTION_VECTOR = {
8
- UP: new Vector2(0, -1),
9
- LEFT: new Vector2(-1, 0),
10
- RIGHT: new Vector2(1, 0),
11
- DOWN: new Vector2(0, 1),
12
- };
13
4
  export class PhysicsSubsystem {
14
- scene;
15
5
  actorQuery;
16
6
  gravity;
17
- actorVectors;
18
7
  constructor(options) {
19
8
  const { gravity, scene } = options;
20
- this.scene = scene;
21
9
  this.actorQuery = new ActorQuery({ scene, filter: [RigidBody, Transform] });
22
10
  this.gravity = gravity;
23
- this.actorVectors = {};
24
- this.actorQuery.addEventListener(RemoveActor, this.handleActorRemove);
25
- this.scene.addEventListener(StopMovement, this.handleStopMovement);
26
- this.scene.addEventListener(AddForce, this.handleAddForce);
27
- this.scene.addEventListener(AddImpulse, this.handleAddImpulse);
28
11
  }
29
12
  destroy() {
30
- this.actorQuery.removeEventListener(RemoveActor, this.handleActorRemove);
31
- this.scene.removeEventListener(StopMovement, this.handleStopMovement);
32
- this.scene.removeEventListener(AddForce, this.handleAddForce);
33
- this.scene.removeEventListener(AddImpulse, this.handleAddImpulse);
13
+ this.actorQuery.destroy();
34
14
  }
35
- handleActorRemove = (event) => {
36
- const { actor } = event;
37
- delete this.actorVectors[actor.id];
38
- };
39
- handleStopMovement = (event) => {
40
- const { target } = event;
41
- if (!this.actorVectors[target.id]) {
42
- this.setUpVectors(target);
43
- }
44
- this.actorVectors[target.id].velocity.multiplyNumber(0);
45
- };
46
- handleAddForce = (event) => {
47
- const { target, value } = event;
48
- if (!this.actorVectors[target.id]) {
49
- this.setUpVectors(target);
50
- }
51
- this.actorVectors[target.id].force.add(value);
52
- };
53
- handleAddImpulse = (event) => {
54
- const { target, value } = event;
55
- if (!this.actorVectors[target.id]) {
56
- this.setUpVectors(target);
57
- }
58
- this.actorVectors[target.id].impulse.add(value);
59
- };
60
- setUpVectors(actor) {
61
- this.actorVectors[actor.id] = {
62
- velocity: new Vector2(0, 0),
63
- force: new Vector2(0, 0),
64
- impulse: new Vector2(0, 0),
65
- };
66
- }
67
- applyDragForce(actor, deltaTime) {
68
- const { mass, drag } = actor.getComponent(RigidBody);
69
- const velocity = this.actorVectors[actor.id]?.velocity;
70
- if (!drag || !velocity || (!velocity.x && !velocity.y)) {
15
+ applyLinearDamping(rigidBody, deltaTime) {
16
+ const { mass, linearDamping, linearVelocity } = rigidBody;
17
+ if (!linearDamping || (!linearVelocity.x && !linearVelocity.y)) {
71
18
  return;
72
19
  }
73
- const velocitySignX = Math.sign(velocity.x);
74
- const velocitySignY = Math.sign(velocity.y);
20
+ const velocitySignX = Math.sign(linearVelocity.x);
21
+ const velocitySignY = Math.sign(linearVelocity.y);
75
22
  const reactionForceValue = mass * this.gravity;
76
- const dragForceValue = -1 * drag * reactionForceValue;
23
+ const dragForceValue = -1 * linearDamping * reactionForceValue;
77
24
  const forceToVelocityMultiplier = deltaTime / mass;
78
25
  const slowdownValue = dragForceValue * forceToVelocityMultiplier;
79
- const normalizationMultiplier = 1 / velocity.magnitude;
80
- const slowdown = velocity.clone();
81
- slowdown.multiplyNumber(slowdownValue * normalizationMultiplier);
82
- velocity.add(slowdown);
83
- if (Math.sign(velocity.x) !== velocitySignX &&
84
- Math.sign(velocity.y) !== velocitySignY) {
85
- velocity.multiplyNumber(0);
26
+ const normalizationMultiplier = 1 / linearVelocity.magnitude;
27
+ const slowdownMultiplier = slowdownValue * normalizationMultiplier;
28
+ linearVelocity.x += linearVelocity.x * slowdownMultiplier;
29
+ linearVelocity.y += linearVelocity.y * slowdownMultiplier;
30
+ if (Math.sign(linearVelocity.x) !== velocitySignX &&
31
+ Math.sign(linearVelocity.y) !== velocitySignY) {
32
+ linearVelocity.multiplyNumber(0);
86
33
  }
87
34
  }
88
- getGravityForce(rigidBody) {
89
- const { mass, useGravity } = rigidBody;
90
- const gravityVector = new Vector2(0, 0);
91
- if (useGravity) {
92
- gravityVector.add(DIRECTION_VECTOR.DOWN);
93
- gravityVector.multiplyNumber(mass * this.gravity);
94
- }
95
- return gravityVector;
96
- }
97
- update(options) {
98
- const { deltaTime } = options;
99
- const deltaTimeInMsec = deltaTime;
100
- const deltaTimeInSeconds = deltaTimeInMsec / 1000;
35
+ integrateVelocities(deltaTimeInSeconds) {
101
36
  this.actorQuery.getActors().forEach((actor) => {
102
37
  const rigidBody = actor.getComponent(RigidBody);
103
- const transform = actor.getComponent(Transform);
104
- const { mass } = rigidBody;
105
- if (!this.actorVectors[actor.id]) {
106
- this.setUpVectors(actor);
38
+ const { mass, inverseMass } = rigidBody;
39
+ if (rigidBody.disabled || rigidBody.type === 'static' || mass <= 0) {
40
+ rigidBody.clearForces();
41
+ return;
42
+ }
43
+ const { force, impulse } = rigidBody;
44
+ const velocity = rigidBody.linearVelocity;
45
+ if (rigidBody.sleeping) {
46
+ if (force.x || force.y || impulse.x || impulse.y) {
47
+ rigidBody.wakeUp();
48
+ }
49
+ else {
50
+ return;
51
+ }
52
+ }
53
+ if (rigidBody.gravityScale) {
54
+ force.y += mass * this.gravity * rigidBody.gravityScale;
107
55
  }
108
- const { velocity, force, impulse } = this.actorVectors[actor.id];
109
- force.add(this.getGravityForce(rigidBody));
110
56
  if (force.x || force.y) {
111
- force.multiplyNumber(deltaTimeInSeconds / mass);
57
+ force.multiplyNumber(deltaTimeInSeconds * inverseMass);
112
58
  velocity.add(force);
113
59
  }
114
60
  if (impulse.x || impulse.y) {
115
- impulse.multiplyNumber(1 / mass);
61
+ impulse.multiplyNumber(inverseMass);
116
62
  velocity.add(impulse);
117
63
  }
118
- this.applyDragForce(actor, deltaTimeInSeconds);
119
- transform.world.position.x += velocity.x * deltaTimeInSeconds;
120
- transform.world.position.y += velocity.y * deltaTimeInSeconds;
121
- force.multiplyNumber(0);
122
- impulse.multiplyNumber(0);
64
+ this.applyLinearDamping(rigidBody, deltaTimeInSeconds);
65
+ rigidBody.clearForces();
123
66
  });
124
67
  }
68
+ integratePositions(deltaTimeInSeconds) {
69
+ this.actorQuery.getActors().forEach((actor) => {
70
+ const rigidBody = actor.getComponent(RigidBody);
71
+ const transform = actor.getComponent(Transform);
72
+ if (rigidBody.disabled ||
73
+ rigidBody.type === 'static' ||
74
+ rigidBody.mass <= 0 ||
75
+ rigidBody.sleeping) {
76
+ return;
77
+ }
78
+ transform.world.position.x +=
79
+ rigidBody.linearVelocity.x * deltaTimeInSeconds;
80
+ transform.world.position.y +=
81
+ rigidBody.linearVelocity.y * deltaTimeInSeconds;
82
+ });
83
+ }
84
+ update(options) {
85
+ const deltaTimeInSeconds = options.deltaTime / 1000;
86
+ this.integrateVelocities(deltaTimeInSeconds);
87
+ this.integratePositions(deltaTimeInSeconds);
88
+ }
125
89
  }
@@ -2,3 +2,12 @@ import type { SceneSystemOptions } from '../../../engine/system';
2
2
  export interface PhysicsSystemOptions extends SceneSystemOptions {
3
3
  gravity: number;
4
4
  }
5
+ export interface CollisionLayer {
6
+ id: string;
7
+ name: string;
8
+ }
9
+ export type CollisionMatrix = Record<string, Record<string, boolean>>;
10
+ export interface PhysicsSettings {
11
+ collisionLayers: CollisionLayer[];
12
+ collisionMatrix: CollisionMatrix;
13
+ }