dacha 0.18.0-alpha.8 → 0.18.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 (110) hide show
  1. package/build/contrib/components/index.d.ts +1 -0
  2. package/build/contrib/components/index.js +1 -0
  3. package/build/contrib/components/interpolation/index.d.ts +85 -0
  4. package/build/contrib/components/interpolation/index.js +101 -0
  5. package/build/contrib/components/rigid-body/index.d.ts +108 -14
  6. package/build/contrib/components/rigid-body/index.js +191 -22
  7. package/build/contrib/systems/animator/index.d.ts +3 -2
  8. package/build/contrib/systems/animator/index.js +6 -3
  9. package/build/contrib/systems/behavior-system/system.d.ts +4 -2
  10. package/build/contrib/systems/behavior-system/system.js +11 -3
  11. package/build/contrib/systems/behavior-system/types.d.ts +7 -10
  12. package/build/contrib/systems/character-controller/index.d.ts +4 -2
  13. package/build/contrib/systems/character-controller/index.js +22 -17
  14. package/build/contrib/systems/character-controller/one-way-validator.d.ts +15 -0
  15. package/build/contrib/systems/character-controller/one-way-validator.js +73 -0
  16. package/build/contrib/systems/game-stats-meter/index.d.ts +4 -3
  17. package/build/contrib/systems/game-stats-meter/index.js +11 -10
  18. package/build/contrib/systems/index.d.ts +2 -0
  19. package/build/contrib/systems/index.js +1 -0
  20. package/build/contrib/systems/interpolator/api.d.ts +40 -0
  21. package/build/contrib/systems/interpolator/api.js +70 -0
  22. package/build/contrib/systems/interpolator/index.d.ts +3 -0
  23. package/build/contrib/systems/interpolator/index.js +2 -0
  24. package/build/contrib/systems/interpolator/system.d.ts +31 -0
  25. package/build/contrib/systems/interpolator/system.js +96 -0
  26. package/build/contrib/systems/interpolator/tests/helpers.d.ts +13 -0
  27. package/build/contrib/systems/interpolator/tests/helpers.js +45 -0
  28. package/build/contrib/systems/interpolator/utils.d.ts +5 -0
  29. package/build/contrib/systems/interpolator/utils.js +41 -0
  30. package/build/contrib/systems/physics-system/api.d.ts +45 -1
  31. package/build/contrib/systems/physics-system/api.js +49 -0
  32. package/build/contrib/systems/physics-system/consts.d.ts +13 -0
  33. package/build/contrib/systems/physics-system/consts.js +17 -0
  34. package/build/contrib/systems/physics-system/physics-system.d.ts +2 -2
  35. package/build/contrib/systems/physics-system/physics-system.js +24 -4
  36. package/build/contrib/systems/physics-system/subsystems/collision-detection/dynamic-aabb-tree/index.d.ts +1 -1
  37. package/build/contrib/systems/physics-system/subsystems/collision-detection/dynamic-aabb-tree/index.js +21 -15
  38. package/build/contrib/systems/physics-system/subsystems/collision-detection/index.d.ts +11 -11
  39. package/build/contrib/systems/physics-system/subsystems/collision-detection/index.js +133 -125
  40. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-box/check-boxes-intersection.js +5 -3
  41. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-box/utils.d.ts +1 -0
  42. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-box/utils.js +2 -1
  43. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-capsule/check-box-and-capsule-intersection.js +4 -1
  44. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/box-segment/check-box-and-segment-intersection.js +4 -2
  45. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/common/points.d.ts +2 -0
  46. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/common/points.js +16 -0
  47. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/index.js +2 -0
  48. package/build/contrib/systems/physics-system/subsystems/collision-detection/intersection-checkers/tests/helpers.js +3 -14
  49. package/build/contrib/systems/physics-system/subsystems/collision-detection/query-utils.d.ts +5 -0
  50. package/build/contrib/systems/physics-system/subsystems/collision-detection/query-utils.js +97 -81
  51. package/build/contrib/systems/physics-system/subsystems/collision-detection/shape-cast-checkers/tests/helpers.js +2 -7
  52. package/build/contrib/systems/physics-system/subsystems/collision-detection/tests/assertions.d.ts +1 -0
  53. package/build/contrib/systems/physics-system/subsystems/collision-detection/tests/assertions.js +7 -0
  54. package/build/contrib/systems/physics-system/subsystems/collision-detection/types.d.ts +0 -15
  55. package/build/contrib/systems/physics-system/subsystems/collision-detection/utils.d.ts +3 -0
  56. package/build/contrib/systems/physics-system/subsystems/collision-detection/utils.js +9 -0
  57. package/build/contrib/systems/physics-system/subsystems/constraint-solver/contact-state-manager.d.ts +63 -0
  58. package/build/contrib/systems/physics-system/subsystems/constraint-solver/contact-state-manager.js +170 -0
  59. package/build/contrib/systems/physics-system/subsystems/constraint-solver/contact-utils.d.ts +5 -0
  60. package/build/contrib/systems/physics-system/subsystems/constraint-solver/contact-utils.js +41 -0
  61. package/build/contrib/systems/physics-system/subsystems/constraint-solver/impulse-utils.d.ts +16 -0
  62. package/build/contrib/systems/physics-system/subsystems/constraint-solver/impulse-utils.js +77 -0
  63. package/build/contrib/systems/physics-system/subsystems/constraint-solver/index.d.ts +53 -4
  64. package/build/contrib/systems/physics-system/subsystems/constraint-solver/index.js +334 -116
  65. package/build/contrib/systems/physics-system/subsystems/constraint-solver/one-way-validator.d.ts +11 -0
  66. package/build/contrib/{utils → systems/physics-system/subsystems/constraint-solver}/one-way-validator.js +22 -25
  67. package/build/contrib/systems/physics-system/subsystems/constraint-solver/sleep-support-tracker.d.ts +13 -0
  68. package/build/contrib/systems/physics-system/subsystems/constraint-solver/sleep-support-tracker.js +42 -0
  69. package/build/contrib/systems/physics-system/subsystems/constraint-solver/tests/helpers.d.ts +4 -0
  70. package/build/contrib/systems/physics-system/subsystems/constraint-solver/tests/helpers.js +20 -0
  71. package/build/contrib/systems/physics-system/subsystems/physics/index.d.ts +15 -4
  72. package/build/contrib/systems/physics-system/subsystems/physics/index.js +131 -53
  73. package/build/contrib/systems/physics-system/subsystems/physics/mass-properties.d.ts +2 -0
  74. package/build/contrib/systems/physics-system/subsystems/physics/mass-properties.js +55 -0
  75. package/build/contrib/systems/physics-system/tests/helpers.d.ts +5 -2
  76. package/build/contrib/systems/physics-system/tests/helpers.js +11 -2
  77. package/build/contrib/systems/physics-system/types.d.ts +18 -2
  78. package/build/contrib/systems/renderer/actor-render-tree.js +15 -6
  79. package/build/contrib/systems/renderer/filters/index.d.ts +1 -1
  80. package/build/contrib/systems/renderer/filters/index.js +1 -1
  81. package/build/contrib/systems/renderer/material/index.d.ts +2 -1
  82. package/build/contrib/systems/renderer/material/index.js +1 -1
  83. package/build/contrib/systems/renderer/renderer.d.ts +2 -2
  84. package/build/contrib/systems/renderer/renderer.js +3 -4
  85. package/build/contrib/systems/renderer/types.d.ts +0 -3
  86. package/build/engine/data-lib/index.d.ts +1 -1
  87. package/build/engine/data-lib/index.js +1 -1
  88. package/build/engine/data-lib/pool.d.ts +9 -0
  89. package/build/engine/data-lib/pool.js +19 -0
  90. package/build/engine/engine.js +4 -1
  91. package/build/engine/game-loop.d.ts +3 -1
  92. package/build/engine/game-loop.js +11 -8
  93. package/build/engine/math-lib/math/ops.d.ts +16 -0
  94. package/build/engine/math-lib/math/ops.js +25 -0
  95. package/build/engine/scene/scene-manager.d.ts +4 -1
  96. package/build/engine/scene/scene-manager.js +5 -1
  97. package/build/engine/system/index.d.ts +1 -1
  98. package/build/engine/system/system.d.ts +10 -11
  99. package/build/engine/system/system.js +5 -2
  100. package/build/engine/time.d.ts +54 -0
  101. package/build/engine/time.js +77 -0
  102. package/build/index.d.ts +2 -1
  103. package/package.json +1 -1
  104. package/build/contrib/systems/physics-system/subsystems/collision-detection/dispersion-calculator/index.d.ts +0 -11
  105. package/build/contrib/systems/physics-system/subsystems/collision-detection/dispersion-calculator/index.js +0 -41
  106. package/build/contrib/utils/one-way-validator.d.ts +0 -12
  107. package/build/engine/data-lib/sort/index.d.ts +0 -1
  108. package/build/engine/data-lib/sort/index.js +0 -1
  109. package/build/engine/data-lib/sort/insertion-sort.d.ts +0 -1
  110. package/build/engine/data-lib/sort/insertion-sort.js +0 -14
@@ -1,26 +1,11 @@
1
- import { RigidBody, Transform } from '../components';
2
- import { VectorOps } from '../../engine/math-lib';
1
+ import { RigidBody, Transform } from '../../../../components';
2
+ import { VectorOps } from '../../../../../engine/math-lib';
3
3
  export class OneWayValidator {
4
4
  ignoredOneWayContacts;
5
- oneWayContactUpdateIndex;
5
+ version;
6
6
  constructor() {
7
7
  this.ignoredOneWayContacts = new Map();
8
- this.oneWayContactUpdateIndex = 0;
9
- }
10
- clearOneWayContacts() {
11
- this.ignoredOneWayContacts.forEach((ignoredContacts, oneWayActor) => {
12
- ignoredContacts.forEach((lastSeenUpdate, otherActor) => {
13
- if (lastSeenUpdate !== this.oneWayContactUpdateIndex) {
14
- ignoredContacts.delete(otherActor);
15
- }
16
- });
17
- if (ignoredContacts.size === 0) {
18
- this.ignoredOneWayContacts.delete(oneWayActor);
19
- }
20
- });
21
- if (this.ignoredOneWayContacts.size === 0) {
22
- this.oneWayContactUpdateIndex = 0;
23
- }
8
+ this.version = 0;
24
9
  }
25
10
  trackOneWayContact(oneWayActor, otherActor) {
26
11
  let ignoredContacts = this.ignoredOneWayContacts.get(oneWayActor);
@@ -28,9 +13,9 @@ export class OneWayValidator {
28
13
  ignoredContacts = new Map();
29
14
  this.ignoredOneWayContacts.set(oneWayActor, ignoredContacts);
30
15
  }
31
- ignoredContacts.set(otherActor, this.oneWayContactUpdateIndex);
16
+ ignoredContacts.set(otherActor, this.version);
32
17
  }
33
- validate(oneWayActor, otherActor, normal) {
18
+ shouldBlock(oneWayActor, otherActor, normal) {
34
19
  if (this.ignoredOneWayContacts.get(oneWayActor)?.has(otherActor)) {
35
20
  this.trackOneWayContact(oneWayActor, otherActor);
36
21
  return false;
@@ -44,10 +29,22 @@ export class OneWayValidator {
44
29
  this.trackOneWayContact(oneWayActor, otherActor);
45
30
  return false;
46
31
  }
47
- update() {
48
- this.oneWayContactUpdateIndex += 1;
32
+ updateVersion() {
33
+ this.version += 1;
49
34
  }
50
- lateUpdate() {
51
- this.clearOneWayContacts();
35
+ clearOneWayContacts() {
36
+ this.ignoredOneWayContacts.forEach((ignoredContacts, oneWayActor) => {
37
+ ignoredContacts.forEach((lastSeenUpdate, otherActor) => {
38
+ if (lastSeenUpdate !== this.version) {
39
+ ignoredContacts.delete(otherActor);
40
+ }
41
+ });
42
+ if (ignoredContacts.size === 0) {
43
+ this.ignoredOneWayContacts.delete(oneWayActor);
44
+ }
45
+ });
46
+ if (this.ignoredOneWayContacts.size === 0) {
47
+ this.version = 0;
48
+ }
52
49
  }
53
50
  }
@@ -0,0 +1,13 @@
1
+ import type { Vector2 } from '../../../../../engine/math-lib';
2
+ import type { Contact } from '../collision-detection/types';
3
+ export declare class SleepSupportTracker {
4
+ private currentSupportedActors;
5
+ private previousSupportedActors;
6
+ private getGravity;
7
+ constructor(getGravity: () => Vector2);
8
+ beginFrame(): void;
9
+ trackContact(contact: Contact): void;
10
+ wakeUnsupportedBodies(): void;
11
+ endFrame(): void;
12
+ private trackActor;
13
+ }
@@ -0,0 +1,42 @@
1
+ import { RigidBody } from '../../../../components/rigid-body';
2
+ import { isSleepSupportContact } from './contact-utils';
3
+ export class SleepSupportTracker {
4
+ currentSupportedActors;
5
+ previousSupportedActors;
6
+ getGravity;
7
+ constructor(getGravity) {
8
+ this.currentSupportedActors = new Set();
9
+ this.previousSupportedActors = new Set();
10
+ this.getGravity = getGravity;
11
+ }
12
+ beginFrame() {
13
+ this.currentSupportedActors.clear();
14
+ }
15
+ trackContact(contact) {
16
+ this.trackActor(contact.actor1, contact);
17
+ this.trackActor(contact.actor2, contact);
18
+ }
19
+ wakeUnsupportedBodies() {
20
+ this.previousSupportedActors.forEach((actor) => {
21
+ if (this.currentSupportedActors.has(actor)) {
22
+ return;
23
+ }
24
+ const rigidBody = actor.getComponent(RigidBody);
25
+ rigidBody?.wakeUp();
26
+ });
27
+ }
28
+ endFrame() {
29
+ const previousSupportedActors = this.previousSupportedActors;
30
+ this.previousSupportedActors = this.currentSupportedActors;
31
+ this.currentSupportedActors = previousSupportedActors;
32
+ }
33
+ trackActor(actor, contact) {
34
+ const gravity = this.getGravity();
35
+ const rigidBody = actor.getComponent(RigidBody);
36
+ if (rigidBody.type !== 'dynamic' ||
37
+ !isSleepSupportContact(contact, actor, gravity)) {
38
+ return;
39
+ }
40
+ this.currentSupportedActors.add(actor);
41
+ }
42
+ }
@@ -0,0 +1,4 @@
1
+ import { Actor } from '../../../../../../engine/actor';
2
+ import { type RigidBodyConfig, type RigidBodyType } from '../../../../../components/rigid-body';
3
+ export declare const createActor: (id: string, type: RigidBodyType, config?: Partial<RigidBodyConfig>) => Actor;
4
+ export declare const createActorWithInertia: (id: string, type: RigidBodyType, config?: Partial<RigidBodyConfig>) => Actor;
@@ -0,0 +1,20 @@
1
+ import { Actor } from '../../../../../../engine/actor';
2
+ import { RigidBody, } from '../../../../../components/rigid-body';
3
+ export const createActor = (id, type, config = {}) => {
4
+ const actor = new Actor({ id, name: id });
5
+ actor.setComponent(new RigidBody({
6
+ type,
7
+ mass: 1,
8
+ gravityScale: 0,
9
+ linearDamping: 0,
10
+ disabled: false,
11
+ oneWay: false,
12
+ ...config,
13
+ }));
14
+ return actor;
15
+ };
16
+ export const createActorWithInertia = (id, type, config = {}) => {
17
+ const actor = createActor(id, type, config);
18
+ actor.getComponent(RigidBody).inertia = 1;
19
+ return actor;
20
+ };
@@ -1,19 +1,30 @@
1
- import type { UpdateOptions } from '../../../../../engine/system';
2
1
  import type { Scene } from '../../../../../engine/scene';
2
+ import type { Time } from '../../../../../engine/time';
3
3
  import type { Vector2 } from '../../../../../engine/math-lib';
4
4
  export interface PhysicsSubsystemOptions {
5
5
  scene: Scene;
6
+ time: Time;
6
7
  getGravity: () => Vector2;
8
+ linearSleepThreshold?: number;
9
+ angularSleepThreshold?: number;
10
+ sleepTimeThreshold?: number;
7
11
  }
8
12
  export declare class PhysicsSubsystem {
9
13
  private actorQuery;
14
+ private time;
10
15
  private getGravity;
16
+ private linearSleepThreshold;
17
+ private angularSleepThreshold;
18
+ private biasAngularSleepThreshold;
19
+ private sleepTimeThreshold;
11
20
  private kinematicMovedActors;
12
21
  constructor(options: PhysicsSubsystemOptions);
13
22
  destroy(): void;
14
23
  private applyLinearDamping;
15
- private integrateVelocities;
16
- private integratePositions;
17
- update(options: UpdateOptions): void;
24
+ private applyAngularDamping;
25
+ integrateVelocities(): void;
26
+ integrateKinematicPositions(): void;
27
+ integrateDynamicPositions(): void;
28
+ updateSleepTimers(): void;
18
29
  lateUpdate(): void;
19
30
  }
@@ -1,45 +1,61 @@
1
1
  import { ActorQuery } from '../../../../../engine/actor';
2
2
  import { RigidBody } from '../../../../components/rigid-body';
3
3
  import { Transform } from '../../../../components/transform';
4
+ import { Collider } from '../../../../components/collider';
5
+ import { DEFAULT_LINEAR_SLEEP_THRESHOLD, DEFAULT_ANGULAR_SLEEP_THRESHOLD, DEFAULT_SLEEP_TIME_THRESHOLD, BIAS_ANGULAR_SLEEP_MULTIPLIER, } from '../../consts';
6
+ import { calculateInertia } from './mass-properties';
4
7
  export class PhysicsSubsystem {
5
8
  actorQuery;
9
+ time;
6
10
  getGravity;
11
+ linearSleepThreshold;
12
+ angularSleepThreshold;
13
+ biasAngularSleepThreshold;
14
+ sleepTimeThreshold;
7
15
  kinematicMovedActors;
8
16
  constructor(options) {
9
- const { scene, getGravity } = options;
17
+ const { scene, time, getGravity } = options;
10
18
  this.actorQuery = new ActorQuery({ scene, filter: [RigidBody, Transform] });
19
+ this.time = time;
11
20
  this.getGravity = getGravity;
21
+ this.linearSleepThreshold =
22
+ options.linearSleepThreshold ?? DEFAULT_LINEAR_SLEEP_THRESHOLD;
23
+ this.angularSleepThreshold =
24
+ options.angularSleepThreshold ?? DEFAULT_ANGULAR_SLEEP_THRESHOLD;
25
+ this.biasAngularSleepThreshold =
26
+ this.angularSleepThreshold * BIAS_ANGULAR_SLEEP_MULTIPLIER;
27
+ this.sleepTimeThreshold =
28
+ options.sleepTimeThreshold ?? DEFAULT_SLEEP_TIME_THRESHOLD;
12
29
  this.kinematicMovedActors = new Set();
13
30
  }
14
31
  destroy() {
15
32
  this.actorQuery.destroy();
16
33
  }
17
34
  applyLinearDamping(rigidBody, deltaTime) {
18
- const { mass, linearDamping, linearVelocity } = rigidBody;
35
+ const { linearDamping, linearVelocity } = rigidBody;
19
36
  if (!linearDamping || (!linearVelocity.x && !linearVelocity.y)) {
20
37
  return;
21
38
  }
22
- const velocitySignX = Math.sign(linearVelocity.x);
23
- const velocitySignY = Math.sign(linearVelocity.y);
24
- const gravity = this.getGravity();
25
- const reactionForceValue = mass * gravity.magnitude;
26
- const dragForceValue = -1 * linearDamping * reactionForceValue;
27
- const forceToVelocityMultiplier = deltaTime / mass;
28
- const slowdownValue = dragForceValue * forceToVelocityMultiplier;
29
- const normalizationMultiplier = 1 / linearVelocity.magnitude;
30
- const slowdownMultiplier = slowdownValue * normalizationMultiplier;
31
- linearVelocity.x += linearVelocity.x * slowdownMultiplier;
32
- linearVelocity.y += linearVelocity.y * slowdownMultiplier;
33
- if (Math.sign(linearVelocity.x) !== velocitySignX &&
34
- Math.sign(linearVelocity.y) !== velocitySignY) {
35
- linearVelocity.multiplyNumber(0);
39
+ linearVelocity.multiplyNumber(Math.max(0, 1 - linearDamping * deltaTime));
40
+ }
41
+ applyAngularDamping(rigidBody, deltaTime) {
42
+ const { angularDamping, angularVelocity } = rigidBody;
43
+ if (!angularDamping || !angularVelocity) {
44
+ return;
36
45
  }
46
+ rigidBody.angularVelocity *= Math.max(0, 1 - angularDamping * deltaTime);
37
47
  }
38
- integrateVelocities(deltaTimeInSeconds) {
48
+ integrateVelocities() {
49
+ const deltaTime = this.time.fixedDeltaTime;
39
50
  this.actorQuery.getActors().forEach((actor) => {
40
51
  const rigidBody = actor.getComponent(RigidBody);
52
+ const collider = actor.getComponent(Collider);
41
53
  const transform = actor.getComponent(Transform);
42
- const { mass, inverseMass } = rigidBody;
54
+ rigidBody._biasLinearVelocity.multiplyNumber(0);
55
+ rigidBody._biasAngularVelocity = 0;
56
+ rigidBody._prevLinearVelocity.x = rigidBody.linearVelocity.x;
57
+ rigidBody._prevLinearVelocity.y = rigidBody.linearVelocity.y;
58
+ rigidBody._prevAngularVelocity = rigidBody.angularVelocity;
43
59
  if (rigidBody.disabled) {
44
60
  rigidBody._movementTarget = null;
45
61
  rigidBody.clearForces();
@@ -54,70 +70,132 @@ export class PhysicsSubsystem {
54
70
  return;
55
71
  }
56
72
  rigidBody.linearVelocity.x =
57
- (_movementTarget.x - transform.world.position.x) / deltaTimeInSeconds;
73
+ (_movementTarget.x - transform.world.position.x) / deltaTime;
58
74
  rigidBody.linearVelocity.y =
59
- (_movementTarget.y - transform.world.position.y) / deltaTimeInSeconds;
75
+ (_movementTarget.y - transform.world.position.y) / deltaTime;
60
76
  this.kinematicMovedActors.add(actor);
61
77
  return;
62
78
  }
63
- if (mass <= 0) {
79
+ if (rigidBody.mass <= 0) {
64
80
  rigidBody.clearForces();
65
81
  return;
66
82
  }
67
- const { force, impulse } = rigidBody;
68
- const velocity = rigidBody.linearVelocity;
69
- if (rigidBody.sleeping) {
70
- if (force.x || force.y || impulse.x || impulse.y) {
71
- rigidBody.wakeUp();
72
- }
73
- else {
74
- return;
75
- }
83
+ rigidBody.inertia = calculateInertia(rigidBody.mass, collider, transform);
84
+ rigidBody._pointForces.forEach(({ force, position }) => {
85
+ rigidBody._centralForce.add(force);
86
+ rigidBody._torque +=
87
+ (position.x - transform.world.position.x) * force.y -
88
+ (position.y - transform.world.position.y) * force.x;
89
+ });
90
+ rigidBody._pointImpulses.forEach(({ impulse, position }) => {
91
+ rigidBody._centralImpulse.add(impulse);
92
+ rigidBody._angularImpulse +=
93
+ (position.x - transform.world.position.x) * impulse.y -
94
+ (position.y - transform.world.position.y) * impulse.x;
95
+ });
96
+ const { _centralForce, _centralImpulse, linearVelocity, mass, inverseMass, inverseInertia, gravityScale, lockRotation, sleeping, } = rigidBody;
97
+ if (sleeping) {
98
+ return;
76
99
  }
77
- if (rigidBody.gravityScale) {
100
+ if (gravityScale) {
78
101
  const gravity = this.getGravity();
79
- force.x += mass * gravity.x * rigidBody.gravityScale;
80
- force.y += mass * gravity.y * rigidBody.gravityScale;
102
+ _centralForce.x += mass * gravity.x * gravityScale;
103
+ _centralForce.y += mass * gravity.y * gravityScale;
104
+ }
105
+ if (_centralForce.x || _centralForce.y) {
106
+ _centralForce.multiplyNumber(deltaTime * inverseMass);
107
+ linearVelocity.add(_centralForce);
81
108
  }
82
- if (force.x || force.y) {
83
- force.multiplyNumber(deltaTimeInSeconds * inverseMass);
84
- velocity.add(force);
109
+ if (_centralImpulse.x || _centralImpulse.y) {
110
+ _centralImpulse.multiplyNumber(inverseMass);
111
+ linearVelocity.add(_centralImpulse);
85
112
  }
86
- if (impulse.x || impulse.y) {
87
- impulse.multiplyNumber(inverseMass);
88
- velocity.add(impulse);
113
+ if (lockRotation) {
114
+ rigidBody.angularVelocity = 0;
89
115
  }
90
- this.applyLinearDamping(rigidBody, deltaTimeInSeconds);
116
+ else {
117
+ if (rigidBody._torque && inverseInertia > 0) {
118
+ rigidBody.angularVelocity +=
119
+ rigidBody._torque * inverseInertia * deltaTime;
120
+ }
121
+ if (rigidBody._angularImpulse && inverseInertia > 0) {
122
+ rigidBody.angularVelocity +=
123
+ rigidBody._angularImpulse * inverseInertia;
124
+ }
125
+ this.applyAngularDamping(rigidBody, deltaTime);
126
+ }
127
+ this.applyLinearDamping(rigidBody, deltaTime);
91
128
  rigidBody.clearForces();
92
129
  });
93
130
  }
94
- integratePositions(deltaTimeInSeconds) {
131
+ integrateKinematicPositions() {
132
+ const deltaTime = this.time.fixedDeltaTime;
95
133
  this.actorQuery.getActors().forEach((actor) => {
96
134
  const rigidBody = actor.getComponent(RigidBody);
97
135
  const transform = actor.getComponent(Transform);
98
- if (rigidBody.disabled || rigidBody.type === 'static') {
136
+ if (rigidBody.disabled ||
137
+ rigidBody.type !== 'kinematic' ||
138
+ (!rigidBody.linearVelocity.x && !rigidBody.linearVelocity.y)) {
99
139
  return;
100
140
  }
101
- if (rigidBody.type === 'kinematic') {
102
- transform.world.position.x +=
103
- rigidBody.linearVelocity.x * deltaTimeInSeconds;
104
- transform.world.position.y +=
105
- rigidBody.linearVelocity.y * deltaTimeInSeconds;
141
+ transform.world.position.x += rigidBody.linearVelocity.x * deltaTime;
142
+ transform.world.position.y += rigidBody.linearVelocity.y * deltaTime;
143
+ });
144
+ }
145
+ integrateDynamicPositions() {
146
+ const deltaTime = this.time.fixedDeltaTime;
147
+ this.actorQuery.getActors().forEach((actor) => {
148
+ const rigidBody = actor.getComponent(RigidBody);
149
+ const transform = actor.getComponent(Transform);
150
+ if (rigidBody.disabled || rigidBody.type !== 'dynamic') {
106
151
  return;
107
152
  }
108
153
  if (rigidBody.mass <= 0 || rigidBody.sleeping) {
109
154
  return;
110
155
  }
111
156
  transform.world.position.x +=
112
- rigidBody.linearVelocity.x * deltaTimeInSeconds;
157
+ (rigidBody.linearVelocity.x + rigidBody._biasLinearVelocity.x) *
158
+ deltaTime;
113
159
  transform.world.position.y +=
114
- rigidBody.linearVelocity.y * deltaTimeInSeconds;
160
+ (rigidBody.linearVelocity.y + rigidBody._biasLinearVelocity.y) *
161
+ deltaTime;
162
+ if (!rigidBody.lockRotation) {
163
+ transform.world.rotation +=
164
+ (rigidBody.angularVelocity + rigidBody._biasAngularVelocity) *
165
+ deltaTime;
166
+ }
115
167
  });
116
168
  }
117
- update(options) {
118
- const deltaTimeInSeconds = options.deltaTime / 1000;
119
- this.integrateVelocities(deltaTimeInSeconds);
120
- this.integratePositions(deltaTimeInSeconds);
169
+ updateSleepTimers() {
170
+ const deltaTime = this.time.fixedDeltaTime;
171
+ this.actorQuery.getActors().forEach((actor) => {
172
+ const rigidBody = actor.getComponent(RigidBody);
173
+ if (rigidBody.disabled ||
174
+ rigidBody.type !== 'dynamic' ||
175
+ rigidBody.mass <= 0 ||
176
+ !rigidBody.autoSleep) {
177
+ rigidBody._sleepTime = 0;
178
+ return;
179
+ }
180
+ if (rigidBody.sleeping) {
181
+ return;
182
+ }
183
+ const realMotionIsSmall = rigidBody.linearVelocity.squaredMagnitude <=
184
+ this.linearSleepThreshold * this.linearSleepThreshold &&
185
+ Math.abs(rigidBody.angularVelocity) <= this.angularSleepThreshold;
186
+ const biasMotionIsSmall = rigidBody._biasLinearVelocity.squaredMagnitude <=
187
+ this.linearSleepThreshold * this.linearSleepThreshold &&
188
+ Math.abs(rigidBody._biasAngularVelocity) <=
189
+ this.biasAngularSleepThreshold;
190
+ if (!realMotionIsSmall || !biasMotionIsSmall) {
191
+ rigidBody._sleepTime = 0;
192
+ return;
193
+ }
194
+ rigidBody._sleepTime += deltaTime;
195
+ if (rigidBody._sleepTime >= this.sleepTimeThreshold) {
196
+ rigidBody.sleep();
197
+ }
198
+ });
121
199
  }
122
200
  lateUpdate() {
123
201
  this.kinematicMovedActors.forEach((actor) => {
@@ -0,0 +1,2 @@
1
+ import type { Collider, Transform } from '../../../../components';
2
+ export declare const calculateInertia: (mass: number, collider: Collider | undefined, transform: Transform) => number;
@@ -0,0 +1,55 @@
1
+ const getOffsetInertia = (mass, offsetX, offsetY) => {
2
+ return mass * (offsetX * offsetX + offsetY * offsetY);
3
+ };
4
+ export const calculateInertia = (mass, collider, transform) => {
5
+ if (mass <= 0 || !collider || collider.disabled) {
6
+ return 0;
7
+ }
8
+ const scaleX = Math.abs(transform.world.scale.x);
9
+ const scaleY = Math.abs(transform.world.scale.y);
10
+ const offsetX = collider.offset.x * scaleX;
11
+ const offsetY = collider.offset.y * scaleY;
12
+ let inertia = 0;
13
+ switch (collider.shape.type) {
14
+ case 'circle': {
15
+ const radius = collider.shape.radius * Math.max(scaleX, scaleY);
16
+ if (radius <= 0) {
17
+ return 0;
18
+ }
19
+ inertia = 0.5 * mass * radius * radius;
20
+ break;
21
+ }
22
+ case 'box': {
23
+ const width = collider.shape.size.x * scaleX;
24
+ const height = collider.shape.size.y * scaleY;
25
+ if (width <= 0 || height <= 0) {
26
+ return 0;
27
+ }
28
+ inertia = (mass * (width * width + height * height)) / 12;
29
+ break;
30
+ }
31
+ case 'capsule': {
32
+ const radius = collider.shape.radius * Math.max(scaleX, scaleY);
33
+ const width = radius * 2;
34
+ const height = collider.shape.height * scaleY + width;
35
+ if (radius <= 0 || height <= 0) {
36
+ return 0;
37
+ }
38
+ inertia = (mass * (width * width + height * height)) / 12;
39
+ break;
40
+ }
41
+ case 'segment': {
42
+ const point1 = collider.shape.point1;
43
+ const point2 = collider.shape.point2;
44
+ const dx = (point2.x - point1.x) * scaleX;
45
+ const dy = (point2.y - point1.y) * scaleY;
46
+ const length = Math.sqrt(dx * dx + dy * dy);
47
+ if (length <= 0) {
48
+ return 0;
49
+ }
50
+ inertia = (mass * length * length) / 12;
51
+ break;
52
+ }
53
+ }
54
+ return inertia + getOffsetInertia(mass, offsetX, offsetY);
55
+ };
@@ -1,13 +1,16 @@
1
1
  import { Actor } from '../../../../engine/actor';
2
2
  import { Scene } from '../../../../engine/scene';
3
3
  import { World } from '../../../../engine/world';
4
+ import { Time } from '../../../../engine/time';
4
5
  import { Collider } from '../../../components';
5
6
  import { PhysicsSystem } from '../index';
6
- import type { PhysicsSettings } from '../types';
7
+ import type { PhysicsSettings, PhysicsSystemOptions } from '../types';
8
+ export declare const createTime: (fixedDeltaTime?: number) => Time;
7
9
  export declare const createScene: () => Scene;
8
- export declare const createPhysicsSystem: (scene: Scene, settings?: PhysicsSettings, gravityY?: number, gravityX?: number) => {
10
+ export declare const createPhysicsSystem: (scene: Scene, settings?: PhysicsSettings, gravityY?: number, gravityX?: number, solverOptions?: Partial<Pick<PhysicsSystemOptions, 'solverIterations' | 'linearSleepThreshold' | 'angularSleepThreshold' | 'sleepTimeThreshold' | 'maxAllowedPenetration' | 'maxBiasVelocity'>>) => {
9
11
  physicsSystem: PhysicsSystem;
10
12
  world: World;
13
+ time: Time;
11
14
  };
12
15
  export declare const createBoxActor: (id: string, type: 'dynamic' | 'static' | 'kinematic', positionX: number, positionY: number, colliderConfig?: {
13
16
  layer: string;
@@ -2,9 +2,15 @@ import { ActorCreator, ActorSpawner, Actor } from '../../../../engine/actor';
2
2
  import { Scene } from '../../../../engine/scene';
3
3
  import { TemplateCollection } from '../../../../engine/template';
4
4
  import { World } from '../../../../engine/world';
5
+ import { Time } from '../../../../engine/time';
5
6
  import { Collider, RigidBody } from '../../../components';
6
7
  import { Transform } from '../../../components/transform';
7
8
  import { PhysicsSystem } from '../index';
9
+ export const createTime = (fixedDeltaTime = 0.1) => {
10
+ const time = new Time();
11
+ time.fixedDeltaTime = fixedDeltaTime;
12
+ return time;
13
+ };
8
14
  export const createScene = () => {
9
15
  const templateCollection = new TemplateCollection();
10
16
  const actorCreator = new ActorCreator([], templateCollection);
@@ -16,10 +22,11 @@ export const createScene = () => {
16
22
  templateCollection,
17
23
  });
18
24
  };
19
- export const createPhysicsSystem = (scene, settings, gravityY = 0, gravityX = 0) => {
25
+ export const createPhysicsSystem = (scene, settings, gravityY = 0, gravityX = 0, solverOptions = {}) => {
20
26
  const world = new World({ id: 'world', name: 'world' });
21
27
  const templateCollection = new TemplateCollection();
22
28
  const actorCreator = new ActorCreator([], templateCollection);
29
+ const time = createTime();
23
30
  world.appendChild(scene);
24
31
  const physicsSystem = new PhysicsSystem({
25
32
  scene,
@@ -29,9 +36,11 @@ export const createPhysicsSystem = (scene, settings, gravityY = 0, gravityX = 0)
29
36
  actorSpawner: new ActorSpawner(actorCreator),
30
37
  globalOptions: settings ? { physics: settings } : {},
31
38
  templateCollection,
39
+ time,
40
+ ...solverOptions,
32
41
  });
33
42
  physicsSystem.onSceneEnter?.();
34
- return { physicsSystem, world };
43
+ return { physicsSystem, world, time };
35
44
  };
36
45
  export const createBoxActor = (id, type, positionX, positionY, colliderConfig = { layer: 'default', oneWay: false }) => {
37
46
  const actor = new Actor({ id, name: id });
@@ -2,8 +2,14 @@ import type { SceneSystemOptions } from '../../../engine/system';
2
2
  import type { Actor } from '../../../engine/actor';
3
3
  import type { Vector2, Point } from '../../../engine/math-lib';
4
4
  export interface PhysicsSystemOptions extends SceneSystemOptions {
5
- gravityX: number;
6
- gravityY: number;
5
+ gravityX?: number;
6
+ gravityY?: number;
7
+ solverIterations?: number;
8
+ linearSleepThreshold?: number;
9
+ angularSleepThreshold?: number;
10
+ sleepTimeThreshold?: number;
11
+ maxAllowedPenetration?: number;
12
+ maxBiasVelocity?: number;
7
13
  }
8
14
  export interface CollisionLayer {
9
15
  id: string;
@@ -18,6 +24,14 @@ export interface PhysicsQueryFilter<T> {
18
24
  layer?: string;
19
25
  excludeActors?: Actor[];
20
26
  actorFilter?: (actor: Actor) => boolean;
27
+ /**
28
+ * Called once per candidate hit to decide whether to keep it.
29
+ *
30
+ * The `hit` object passed to this callback may be reused and is only valid
31
+ * for the duration of the call.
32
+ * Inspect it and return a boolean; do not retain the reference or store it
33
+ * in an external collection.
34
+ */
21
35
  hitFilter?: (hit: T) => boolean;
22
36
  }
23
37
  export interface CommonCastParams extends PhysicsQueryFilter<CastHit> {
@@ -39,6 +53,8 @@ export interface OverlapHit {
39
53
  penetration: number;
40
54
  contactPoints: Point[];
41
55
  }
56
+ export type CastHitCallback = (hit: CastHit) => void;
57
+ export type OverlapHitCallback = (hit: OverlapHit) => void;
42
58
  export interface PointQueryShape {
43
59
  type: 'point';
44
60
  point: Point;
@@ -1,6 +1,7 @@
1
1
  import { Container } from 'pixi.js';
2
2
  import { Actor } from '../../../engine/actor';
3
3
  import { Transform } from '../../components/transform';
4
+ import { Interpolation } from '../../components/interpolation';
4
5
  import { Sprite } from '../../components/sprite';
5
6
  import { Shape } from '../../components/shape';
6
7
  import { PixiView } from '../../components/pixi-view';
@@ -146,17 +147,25 @@ export class ActorRenderTree {
146
147
  this.actorParentMap.set(actor, actor.parent);
147
148
  }
148
149
  updatePosition(container, actor) {
149
- const { local: { position, rotation, scale }, } = actor.getComponent(Transform);
150
+ const { local } = actor.getComponent(Transform);
151
+ const interpolation = actor.getComponent(Interpolation);
152
+ const useRender = interpolation !== undefined &&
153
+ !interpolation.disabled &&
154
+ interpolation.initialized;
155
+ const positionX = useRender ? interpolation.renderX : local.position.x;
156
+ const positionY = useRender ? interpolation.renderY : local.position.y;
157
+ const rotation = useRender ? interpolation.renderRotation : local.rotation;
158
+ const { scale } = local;
150
159
  const meta = container.__dacha.meta;
151
160
  if (rotation !== meta.rotation) {
152
161
  container.rotation = rotation;
153
162
  meta.rotation = rotation;
154
163
  }
155
- if (!floatEquals(position.x, meta.positionX) ||
156
- !floatEquals(position.y, meta.positionY)) {
157
- container.position.set(position.x, position.y);
158
- meta.positionX = position.x;
159
- meta.positionY = position.y;
164
+ if (!floatEquals(positionX, meta.positionX) ||
165
+ !floatEquals(positionY, meta.positionY)) {
166
+ container.position.set(positionX, positionY);
167
+ meta.positionX = positionX;
168
+ meta.positionY = positionY;
160
169
  }
161
170
  if (scale.x !== meta.scaleX || scale.y !== meta.scaleY) {
162
171
  container.scale.set(scale.x, scale.y);
@@ -1,5 +1,5 @@
1
1
  import { type Application } from 'pixi.js';
2
- import type { Time } from '../types';
2
+ import type { Time } from '../../../../engine/time';
3
3
  import type { FilterEffectConstructor, FilterEffectConfig } from './filter-effect';
4
4
  export interface FiltersSystemOptions {
5
5
  application: Application;
@@ -58,7 +58,7 @@ export class FilterSystem {
58
58
  }
59
59
  });
60
60
  this.filtersMap.forEach((filter, config) => {
61
- this.effects[config.name]?.update?.(filter, config.options, this.time.elapsed);
61
+ this.effects[config.name]?.update?.(filter, config.options, this.time.elapsedTime);
62
62
  });
63
63
  }
64
64
  }