dacha 0.18.0-alpha.9 → 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 (49) 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 +8 -0
  6. package/build/contrib/components/rigid-body/index.js +8 -0
  7. package/build/contrib/systems/index.d.ts +2 -0
  8. package/build/contrib/systems/index.js +1 -0
  9. package/build/contrib/systems/interpolator/api.d.ts +40 -0
  10. package/build/contrib/systems/interpolator/api.js +70 -0
  11. package/build/contrib/systems/interpolator/index.d.ts +3 -0
  12. package/build/contrib/systems/interpolator/index.js +2 -0
  13. package/build/contrib/systems/interpolator/system.d.ts +31 -0
  14. package/build/contrib/systems/interpolator/system.js +96 -0
  15. package/build/contrib/systems/interpolator/tests/helpers.d.ts +13 -0
  16. package/build/contrib/systems/interpolator/tests/helpers.js +45 -0
  17. package/build/contrib/systems/interpolator/utils.d.ts +5 -0
  18. package/build/contrib/systems/interpolator/utils.js +41 -0
  19. package/build/contrib/systems/physics-system/api.d.ts +45 -1
  20. package/build/contrib/systems/physics-system/api.js +49 -0
  21. package/build/contrib/systems/physics-system/consts.d.ts +3 -1
  22. package/build/contrib/systems/physics-system/consts.js +4 -1
  23. package/build/contrib/systems/physics-system/physics-system.js +7 -1
  24. package/build/contrib/systems/physics-system/subsystems/collision-detection/dynamic-aabb-tree/index.d.ts +1 -1
  25. package/build/contrib/systems/physics-system/subsystems/collision-detection/dynamic-aabb-tree/index.js +21 -15
  26. package/build/contrib/systems/physics-system/subsystems/collision-detection/index.d.ts +11 -11
  27. package/build/contrib/systems/physics-system/subsystems/collision-detection/index.js +130 -124
  28. package/build/contrib/systems/physics-system/subsystems/collision-detection/query-utils.d.ts +5 -0
  29. package/build/contrib/systems/physics-system/subsystems/collision-detection/query-utils.js +97 -81
  30. package/build/contrib/systems/physics-system/subsystems/collision-detection/types.d.ts +0 -15
  31. package/build/contrib/systems/physics-system/subsystems/collision-detection/utils.d.ts +3 -0
  32. package/build/contrib/systems/physics-system/subsystems/collision-detection/utils.js +9 -0
  33. package/build/contrib/systems/physics-system/subsystems/physics/mass-properties.js +1 -1
  34. package/build/contrib/systems/physics-system/types.d.ts +12 -2
  35. package/build/contrib/systems/renderer/actor-render-tree.js +15 -6
  36. package/build/engine/data-lib/index.d.ts +1 -1
  37. package/build/engine/data-lib/index.js +1 -1
  38. package/build/engine/data-lib/pool.d.ts +9 -0
  39. package/build/engine/data-lib/pool.js +19 -0
  40. package/build/engine/game-loop.js +3 -3
  41. package/build/engine/math-lib/math/ops.d.ts +16 -0
  42. package/build/engine/math-lib/math/ops.js +25 -0
  43. package/package.json +1 -1
  44. package/build/contrib/systems/physics-system/subsystems/collision-detection/dispersion-calculator/index.d.ts +0 -11
  45. package/build/contrib/systems/physics-system/subsystems/collision-detection/dispersion-calculator/index.js +0 -41
  46. package/build/engine/data-lib/sort/index.d.ts +0 -1
  47. package/build/engine/data-lib/sort/index.js +0 -1
  48. package/build/engine/data-lib/sort/insertion-sort.d.ts +0 -1
  49. package/build/engine/data-lib/sort/insertion-sort.js +0 -14
@@ -3,6 +3,7 @@ export { KeyboardControl, type KeyboardControlConfig, } from './keyboard-control
3
3
  export { Collider, type ColliderConfig, type ColliderShape, type ColliderType, type BoxColliderShape, type CircleColliderShape, type SegmentColliderShape, type CapsuleColliderShape, } from './collider';
4
4
  export { RigidBody, type RigidBodyConfig } from './rigid-body';
5
5
  export { CharacterBody, type CharacterBodyConfig } from './character-body';
6
+ export { Interpolation, type InterpolationConfig, type InterpolationMode, } from './interpolation';
6
7
  export { Animatable, type AnimatableConfig } from './animatable';
7
8
  export { Sprite, type SpriteConfig } from './sprite';
8
9
  export { Transform, type TransformConfig } from './transform';
@@ -3,6 +3,7 @@ export { KeyboardControl, } from './keyboard-control';
3
3
  export { Collider, } from './collider';
4
4
  export { RigidBody } from './rigid-body';
5
5
  export { CharacterBody } from './character-body';
6
+ export { Interpolation, } from './interpolation';
6
7
  export { Animatable } from './animatable';
7
8
  export { Sprite } from './sprite';
8
9
  export { Transform } from './transform';
@@ -0,0 +1,85 @@
1
+ import { Component } from '../../../engine/component';
2
+ export type InterpolationMode = 'interpolate' | 'extrapolate';
3
+ export interface InterpolationConfig {
4
+ mode?: InterpolationMode;
5
+ snapThreshold?: number;
6
+ disabled?: boolean;
7
+ }
8
+ /**
9
+ * Component that smooths rendering of actors moved during fixed updates.
10
+ *
11
+ * Interpolator keeps local-space snapshots of the actor's Transform around
12
+ * each fixed step and blends them into the render-facing
13
+ * `renderX`/`renderY`/`renderRotation` values every render frame using
14
+ * `Time.alpha`. The renderer prefers these values over the Transform.
15
+ *
16
+ * The Transform component always holds the authoritative simulation state.
17
+ * Interpolated values are only visible to the renderer and to consumers of
18
+ * InterpolatorAPI, so simulation code can never accidentally read a
19
+ * visually smoothed but physically incorrect position.
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * actor.setComponent(new Interpolation({ mode: 'interpolate' }));
24
+ *
25
+ * // After teleporting the actor, skip smoothing for the jump:
26
+ * actor.getComponent(Transform).world.position.x = 500;
27
+ * actor.getComponent(Interpolation).snap();
28
+ * ```
29
+ *
30
+ * @category Components
31
+ */
32
+ export declare class Interpolation extends Component {
33
+ /**
34
+ * How render values are produced. `interpolate` blends between the last
35
+ * two fixed steps (smooth, adds up to one fixed step of visual latency).
36
+ * `extrapolate` projects the latest step forward using the rigid body
37
+ * velocity (no added latency, may briefly overshoot on impacts).
38
+ *
39
+ * `extrapolate` uses the world-space rigid body velocity against
40
+ * local-space snapshots, so it is intended for root-level actors.
41
+ */
42
+ mode: InterpolationMode;
43
+ /**
44
+ * Maximum distance in world units treated as continuous movement between
45
+ * two fixed steps. Larger jumps snap instead of gliding. `0` disables
46
+ * the automatic snap detection.
47
+ */
48
+ snapThreshold: number;
49
+ /** Whether smoothing is turned off. The renderer falls back to Transform. */
50
+ disabled: boolean;
51
+ /** @internal Local-space position X from the previous fixed step */
52
+ _prevX: number;
53
+ /** @internal Local-space position Y from the previous fixed step */
54
+ _prevY: number;
55
+ /** @internal Local-space rotation from the previous fixed step */
56
+ _prevRotation: number;
57
+ /** @internal Local-space position X from the latest fixed step */
58
+ _currX: number;
59
+ /** @internal Local-space position Y from the latest fixed step */
60
+ _currY: number;
61
+ /** @internal Local-space rotation from the latest fixed step */
62
+ _currRotation: number;
63
+ /** @internal Whether snapshots have been initialized since (re)enabling */
64
+ _initialized: boolean;
65
+ /** @internal Whether smoothing should be skipped at the next opportunity */
66
+ _snapRequested: boolean;
67
+ /** Render-facing local-space position X, written by Interpolator */
68
+ renderX: number;
69
+ /** Render-facing local-space position Y, written by Interpolator */
70
+ renderY: number;
71
+ /** Render-facing local-space rotation, written by Interpolator */
72
+ renderRotation: number;
73
+ constructor(config?: InterpolationConfig);
74
+ /**
75
+ * Whether the render-facing values hold a valid snapshot yet. `false`
76
+ * until the Interpolator takes its first snapshot after the component is
77
+ * (re)enabled.
78
+ */
79
+ get initialized(): boolean;
80
+ /**
81
+ * Requests an immediate jump to the Transform instead of
82
+ * smoothing towards it. Call right after teleporting the actor.
83
+ */
84
+ snap(): void;
85
+ }
@@ -0,0 +1,101 @@
1
+ import { Component } from '../../../engine/component';
2
+ /**
3
+ * Component that smooths rendering of actors moved during fixed updates.
4
+ *
5
+ * Interpolator keeps local-space snapshots of the actor's Transform around
6
+ * each fixed step and blends them into the render-facing
7
+ * `renderX`/`renderY`/`renderRotation` values every render frame using
8
+ * `Time.alpha`. The renderer prefers these values over the Transform.
9
+ *
10
+ * The Transform component always holds the authoritative simulation state.
11
+ * Interpolated values are only visible to the renderer and to consumers of
12
+ * InterpolatorAPI, so simulation code can never accidentally read a
13
+ * visually smoothed but physically incorrect position.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * actor.setComponent(new Interpolation({ mode: 'interpolate' }));
18
+ *
19
+ * // After teleporting the actor, skip smoothing for the jump:
20
+ * actor.getComponent(Transform).world.position.x = 500;
21
+ * actor.getComponent(Interpolation).snap();
22
+ * ```
23
+ *
24
+ * @category Components
25
+ */
26
+ export class Interpolation extends Component {
27
+ /**
28
+ * How render values are produced. `interpolate` blends between the last
29
+ * two fixed steps (smooth, adds up to one fixed step of visual latency).
30
+ * `extrapolate` projects the latest step forward using the rigid body
31
+ * velocity (no added latency, may briefly overshoot on impacts).
32
+ *
33
+ * `extrapolate` uses the world-space rigid body velocity against
34
+ * local-space snapshots, so it is intended for root-level actors.
35
+ */
36
+ mode;
37
+ /**
38
+ * Maximum distance in world units treated as continuous movement between
39
+ * two fixed steps. Larger jumps snap instead of gliding. `0` disables
40
+ * the automatic snap detection.
41
+ */
42
+ snapThreshold;
43
+ /** Whether smoothing is turned off. The renderer falls back to Transform. */
44
+ disabled;
45
+ /** @internal Local-space position X from the previous fixed step */
46
+ _prevX;
47
+ /** @internal Local-space position Y from the previous fixed step */
48
+ _prevY;
49
+ /** @internal Local-space rotation from the previous fixed step */
50
+ _prevRotation;
51
+ /** @internal Local-space position X from the latest fixed step */
52
+ _currX;
53
+ /** @internal Local-space position Y from the latest fixed step */
54
+ _currY;
55
+ /** @internal Local-space rotation from the latest fixed step */
56
+ _currRotation;
57
+ /** @internal Whether snapshots have been initialized since (re)enabling */
58
+ _initialized;
59
+ /** @internal Whether smoothing should be skipped at the next opportunity */
60
+ _snapRequested;
61
+ /** Render-facing local-space position X, written by Interpolator */
62
+ renderX;
63
+ /** Render-facing local-space position Y, written by Interpolator */
64
+ renderY;
65
+ /** Render-facing local-space rotation, written by Interpolator */
66
+ renderRotation;
67
+ constructor(config = {}) {
68
+ super();
69
+ const { mode = 'interpolate', snapThreshold = 0, disabled = false, } = config;
70
+ this.mode = mode;
71
+ this.snapThreshold = snapThreshold;
72
+ this.disabled = disabled;
73
+ this._prevX = 0;
74
+ this._prevY = 0;
75
+ this._prevRotation = 0;
76
+ this._currX = 0;
77
+ this._currY = 0;
78
+ this._currRotation = 0;
79
+ this._initialized = false;
80
+ this._snapRequested = false;
81
+ this.renderX = 0;
82
+ this.renderY = 0;
83
+ this.renderRotation = 0;
84
+ }
85
+ /**
86
+ * Whether the render-facing values hold a valid snapshot yet. `false`
87
+ * until the Interpolator takes its first snapshot after the component is
88
+ * (re)enabled.
89
+ */
90
+ get initialized() {
91
+ return this._initialized;
92
+ }
93
+ /**
94
+ * Requests an immediate jump to the Transform instead of
95
+ * smoothing towards it. Call right after teleporting the actor.
96
+ */
97
+ snap() {
98
+ this._snapRequested = true;
99
+ }
100
+ }
101
+ Interpolation.componentName = 'Interpolation';
@@ -31,6 +31,13 @@ interface PointImpulse {
31
31
  * impulses, gravity, collisions, and rotation. Static and kinematic bodies can
32
32
  * participate in collisions but are not moved by solver impulses.
33
33
  *
34
+ * Physics is simulated in world space.
35
+ *
36
+ * Rigid bodies may be parented only to actors with static transforms. A moving
37
+ * parent (for example, a dynamic or kinematic rigid body) and the physics
38
+ * simulation would both control the child's transform, so the result is
39
+ * undefined.
40
+ *
34
41
  * @example
35
42
  * ```typescript
36
43
  * // Create a dynamic rigid body
@@ -115,6 +122,7 @@ export declare class RigidBody extends Component {
115
122
  /**
116
123
  * Sets the mass used by dynamic bodies.
117
124
  *
125
+ * Mass is an authored, kilogram-like scalar.
118
126
  * Non-positive values make the body immovable by forces and impulses.
119
127
  */
120
128
  set mass(value: number);
@@ -7,6 +7,13 @@ import { Vector2 } from '../../../engine/math-lib';
7
7
  * impulses, gravity, collisions, and rotation. Static and kinematic bodies can
8
8
  * participate in collisions but are not moved by solver impulses.
9
9
  *
10
+ * Physics is simulated in world space.
11
+ *
12
+ * Rigid bodies may be parented only to actors with static transforms. A moving
13
+ * parent (for example, a dynamic or kinematic rigid body) and the physics
14
+ * simulation would both control the child's transform, so the result is
15
+ * undefined.
16
+ *
10
17
  * @example
11
18
  * ```typescript
12
19
  * // Create a dynamic rigid body
@@ -134,6 +141,7 @@ export class RigidBody extends Component {
134
141
  /**
135
142
  * Sets the mass used by dynamic bodies.
136
143
  *
144
+ * Mass is an authored, kilogram-like scalar.
137
145
  * Non-positive values make the body immovable by forces and impulses.
138
146
  */
139
147
  set mass(value) {
@@ -4,6 +4,8 @@ export { GameStatsMeter } from './game-stats-meter';
4
4
  export { KeyboardInputSystem } from './keyboard-input-system';
5
5
  export { KeyboardControlSystem } from './keyboard-control-system';
6
6
  export { CharacterController } from './character-controller';
7
+ export { Interpolator, InterpolatorAPI } from './interpolator';
8
+ export type { RenderTransform } from './interpolator';
7
9
  export { MouseControlSystem } from './mouse-control-system';
8
10
  export { MouseInputSystem } from './mouse-input-system';
9
11
  export { PhysicsSystem, PhysicsAPI } from './physics-system';
@@ -4,6 +4,7 @@ export { GameStatsMeter } from './game-stats-meter';
4
4
  export { KeyboardInputSystem } from './keyboard-input-system';
5
5
  export { KeyboardControlSystem } from './keyboard-control-system';
6
6
  export { CharacterController } from './character-controller';
7
+ export { Interpolator, InterpolatorAPI } from './interpolator';
7
8
  export { MouseControlSystem } from './mouse-control-system';
8
9
  export { MouseInputSystem } from './mouse-input-system';
9
10
  export { PhysicsSystem, PhysicsAPI } from './physics-system';
@@ -0,0 +1,40 @@
1
+ import type { Actor } from '../../../engine/actor';
2
+ import type { Time } from '../../../engine/time';
3
+ export interface RenderTransform {
4
+ x: number;
5
+ y: number;
6
+ rotation: number;
7
+ }
8
+ interface InterpolatorAPIOptions {
9
+ time: Time;
10
+ }
11
+ /**
12
+ * Public API for reading render-facing (visually smoothed) transforms.
13
+ *
14
+ * Retrieve it via `world.systemApi.get(InterpolatorAPI)`. Use it in
15
+ * systems and behaviors that position visuals against moving actors —
16
+ * e.g. a camera-follow script — instead of reading the authoritative
17
+ * Transform, which advances in fixed steps.
18
+ *
19
+ * Values are computed from the current `Time.alpha`, so this API is only
20
+ * meaningful during the update phase (not inside `fixedUpdate`).
21
+ *
22
+ * @category Systems
23
+ */
24
+ export declare class InterpolatorAPI {
25
+ private time;
26
+ constructor(options: InterpolatorAPIOptions);
27
+ /**
28
+ * Returns the world-space transform the actor is rendered with this
29
+ * frame. Falls back to the Transform for actors (and
30
+ * ancestors) without an active Interpolation component.
31
+ */
32
+ getRenderTransform(actor: Actor): RenderTransform;
33
+ /**
34
+ * Requests an immediate visual jump to the actor's
35
+ * Transform. Call right after teleporting the actor.
36
+ */
37
+ snap(actor: Actor): void;
38
+ private composeRenderWorldMatrix;
39
+ }
40
+ export {};
@@ -0,0 +1,70 @@
1
+ import { Matrix } from '../../../engine/math-lib';
2
+ import { Interpolation } from '../../components/interpolation';
3
+ import { Transform } from '../../components/transform';
4
+ import { computeRenderValues } from './utils';
5
+ /**
6
+ * Public API for reading render-facing (visually smoothed) transforms.
7
+ *
8
+ * Retrieve it via `world.systemApi.get(InterpolatorAPI)`. Use it in
9
+ * systems and behaviors that position visuals against moving actors —
10
+ * e.g. a camera-follow script — instead of reading the authoritative
11
+ * Transform, which advances in fixed steps.
12
+ *
13
+ * Values are computed from the current `Time.alpha`, so this API is only
14
+ * meaningful during the update phase (not inside `fixedUpdate`).
15
+ *
16
+ * @category Systems
17
+ */
18
+ export class InterpolatorAPI {
19
+ time;
20
+ constructor(options) {
21
+ this.time = options.time;
22
+ }
23
+ /**
24
+ * Returns the world-space transform the actor is rendered with this
25
+ * frame. Falls back to the Transform for actors (and
26
+ * ancestors) without an active Interpolation component.
27
+ */
28
+ getRenderTransform(actor) {
29
+ const matrix = this.composeRenderWorldMatrix(actor);
30
+ return {
31
+ x: matrix.tx,
32
+ y: matrix.ty,
33
+ rotation: Math.atan2(matrix.b, matrix.a),
34
+ };
35
+ }
36
+ /**
37
+ * Requests an immediate visual jump to the actor's
38
+ * Transform. Call right after teleporting the actor.
39
+ */
40
+ snap(actor) {
41
+ const interpolation = actor.getComponent(Interpolation);
42
+ interpolation?.snap();
43
+ }
44
+ composeRenderWorldMatrix(actor) {
45
+ const transform = actor.getComponent(Transform);
46
+ const interpolation = actor.getComponent(Interpolation);
47
+ const useRender = interpolation !== undefined &&
48
+ !interpolation.disabled &&
49
+ interpolation.initialized;
50
+ if (useRender) {
51
+ computeRenderValues(actor, interpolation, this.time.alpha, this.time.fixedDeltaTime);
52
+ }
53
+ const x = useRender ? interpolation.renderX : transform.local.position.x;
54
+ const y = useRender ? interpolation.renderY : transform.local.position.y;
55
+ const rotation = useRender
56
+ ? interpolation.renderRotation
57
+ : transform.local.rotation;
58
+ const { scale } = transform.local;
59
+ const cos = Math.cos(rotation);
60
+ const sin = Math.sin(rotation);
61
+ const localMatrix = new Matrix(cos * scale.x, sin * scale.x, -sin * scale.y, cos * scale.y, x, y);
62
+ const parentTransform = transform.getParentComponent();
63
+ if (!parentTransform?.actor) {
64
+ return localMatrix;
65
+ }
66
+ const result = new Matrix(1, 0, 0, 1, 0, 0);
67
+ Matrix.multiply(result, this.composeRenderWorldMatrix(parentTransform.actor), localMatrix);
68
+ return result;
69
+ }
70
+ }
@@ -0,0 +1,3 @@
1
+ export { Interpolator } from './system';
2
+ export { InterpolatorAPI } from './api';
3
+ export type { RenderTransform } from './api';
@@ -0,0 +1,2 @@
1
+ export { Interpolator } from './system';
2
+ export { InterpolatorAPI } from './api';
@@ -0,0 +1,31 @@
1
+ import { SceneSystem } from '../../../engine/system';
2
+ import type { SceneSystemOptions } from '../../../engine/system';
3
+ /**
4
+ * System that produces smooth render-facing transforms for actors moved
5
+ * during fixed updates.
6
+ *
7
+ * During `fixedUpdate` it snapshots the local Transform of every actor with
8
+ * an Interpolation component. During `update` it blends the last two
9
+ * snapshots using `Time.alpha` and writes the result into the component's
10
+ * `renderX`/`renderY`/`renderRotation`, which the renderer prefers over the
11
+ * Transform.
12
+ *
13
+ * Ordering: place this system AFTER every system that moves transforms in
14
+ * fixed updates (`PhysicsSystem`, `CharacterController`, behavior scripts
15
+ * that move actors in `fixedUpdate`) and BEFORE `Renderer` in the system
16
+ * configuration.
17
+ *
18
+ * @category Systems
19
+ */
20
+ export declare class Interpolator extends SceneSystem {
21
+ private actorQuery;
22
+ private time;
23
+ private world;
24
+ private api;
25
+ constructor(options: SceneSystemOptions);
26
+ onSceneEnter(): void;
27
+ onSceneExit(): void;
28
+ onSceneDestroy(): void;
29
+ fixedUpdate(): void;
30
+ update(): void;
31
+ }
@@ -0,0 +1,96 @@
1
+ import { ActorQuery } from '../../../engine/actor';
2
+ import { SceneSystem } from '../../../engine/system';
3
+ import { Interpolation } from '../../components/interpolation';
4
+ import { Transform } from '../../components/transform';
5
+ import { InterpolatorAPI } from './api';
6
+ import { snapToTransform, computeRenderValues } from './utils';
7
+ /**
8
+ * System that produces smooth render-facing transforms for actors moved
9
+ * during fixed updates.
10
+ *
11
+ * During `fixedUpdate` it snapshots the local Transform of every actor with
12
+ * an Interpolation component. During `update` it blends the last two
13
+ * snapshots using `Time.alpha` and writes the result into the component's
14
+ * `renderX`/`renderY`/`renderRotation`, which the renderer prefers over the
15
+ * Transform.
16
+ *
17
+ * Ordering: place this system AFTER every system that moves transforms in
18
+ * fixed updates (`PhysicsSystem`, `CharacterController`, behavior scripts
19
+ * that move actors in `fixedUpdate`) and BEFORE `Renderer` in the system
20
+ * configuration.
21
+ *
22
+ * @category Systems
23
+ */
24
+ export class Interpolator extends SceneSystem {
25
+ actorQuery;
26
+ time;
27
+ world;
28
+ api;
29
+ constructor(options) {
30
+ super();
31
+ this.time = options.time;
32
+ this.world = options.world;
33
+ this.actorQuery = new ActorQuery({
34
+ scene: options.scene,
35
+ filter: [Interpolation, Transform],
36
+ });
37
+ this.api = new InterpolatorAPI({ time: options.time });
38
+ }
39
+ onSceneEnter() {
40
+ this.world.systemApi.register(this.api);
41
+ }
42
+ onSceneExit() {
43
+ this.world.systemApi.unregister(InterpolatorAPI);
44
+ }
45
+ onSceneDestroy() {
46
+ this.actorQuery.destroy();
47
+ }
48
+ fixedUpdate() {
49
+ for (const actor of this.actorQuery.getActors()) {
50
+ const interpolation = actor.getComponent(Interpolation);
51
+ const { local } = actor.getComponent(Transform);
52
+ if (interpolation.disabled) {
53
+ interpolation._initialized = false;
54
+ continue;
55
+ }
56
+ if (!interpolation._initialized || interpolation._snapRequested) {
57
+ snapToTransform(interpolation, local);
58
+ continue;
59
+ }
60
+ interpolation._prevX = interpolation._currX;
61
+ interpolation._prevY = interpolation._currY;
62
+ interpolation._prevRotation = interpolation._currRotation;
63
+ interpolation._currX = local.position.x;
64
+ interpolation._currY = local.position.y;
65
+ interpolation._currRotation = local.rotation;
66
+ const threshold = interpolation.snapThreshold;
67
+ if (threshold > 0) {
68
+ const dx = interpolation._currX - interpolation._prevX;
69
+ const dy = interpolation._currY - interpolation._prevY;
70
+ if (dx * dx + dy * dy > threshold * threshold) {
71
+ interpolation._prevX = interpolation._currX;
72
+ interpolation._prevY = interpolation._currY;
73
+ interpolation._prevRotation = interpolation._currRotation;
74
+ }
75
+ }
76
+ }
77
+ }
78
+ update() {
79
+ const { alpha, fixedDeltaTime } = this.time;
80
+ for (const actor of this.actorQuery.getActors()) {
81
+ const interpolation = actor.getComponent(Interpolation);
82
+ if (interpolation.disabled) {
83
+ continue;
84
+ }
85
+ if (interpolation._snapRequested) {
86
+ snapToTransform(interpolation, actor.getComponent(Transform).local);
87
+ continue;
88
+ }
89
+ if (!interpolation._initialized) {
90
+ continue;
91
+ }
92
+ computeRenderValues(actor, interpolation, alpha, fixedDeltaTime);
93
+ }
94
+ }
95
+ }
96
+ Interpolator.systemName = 'Interpolator';
@@ -0,0 +1,13 @@
1
+ import { Actor } from '../../../../engine/actor';
2
+ import { Scene } from '../../../../engine/scene';
3
+ import { World } from '../../../../engine/world';
4
+ import { Time } from '../../../../engine/time';
5
+ import type { InterpolationConfig } from '../../../components/interpolation';
6
+ import { Interpolator } from '../system';
7
+ export declare const createScene: () => Scene;
8
+ export declare const createInterpolator: (scene: Scene) => {
9
+ system: Interpolator;
10
+ world: World;
11
+ time: Time;
12
+ };
13
+ export declare const createInterpolatedActor: (id: string, x: number, y: number, config?: InterpolationConfig) => Actor;
@@ -0,0 +1,45 @@
1
+ import { Actor, ActorCreator, ActorSpawner } from '../../../../engine/actor';
2
+ import { Scene } from '../../../../engine/scene';
3
+ import { TemplateCollection } from '../../../../engine/template';
4
+ import { World } from '../../../../engine/world';
5
+ import { Time } from '../../../../engine/time';
6
+ import { Interpolation } from '../../../components/interpolation';
7
+ import { Transform } from '../../../components/transform';
8
+ import { Interpolator } from '../system';
9
+ export const createScene = () => {
10
+ const templateCollection = new TemplateCollection();
11
+ const actorCreator = new ActorCreator([], templateCollection);
12
+ return new Scene({
13
+ id: 'scene',
14
+ name: 'scene',
15
+ actors: [],
16
+ actorCreator,
17
+ templateCollection,
18
+ });
19
+ };
20
+ export const createInterpolator = (scene) => {
21
+ const world = new World({ id: 'world', name: 'world' });
22
+ const templateCollection = new TemplateCollection();
23
+ const actorCreator = new ActorCreator([], templateCollection);
24
+ const time = new Time();
25
+ time.fixedDeltaTime = 0.1;
26
+ world.appendChild(scene);
27
+ const system = new Interpolator({
28
+ scene,
29
+ world,
30
+ actorSpawner: new ActorSpawner(actorCreator),
31
+ globalOptions: {},
32
+ templateCollection,
33
+ time,
34
+ });
35
+ system.onSceneEnter?.();
36
+ return { system, world, time };
37
+ };
38
+ export const createInterpolatedActor = (id, x, y, config = {}) => {
39
+ const actor = new Actor({ id, name: id });
40
+ const transform = actor.getComponent(Transform);
41
+ transform.local.position.x = x;
42
+ transform.local.position.y = y;
43
+ actor.setComponent(new Interpolation(config));
44
+ return actor;
45
+ };
@@ -0,0 +1,5 @@
1
+ import type { Actor } from '../../../engine/actor';
2
+ import type { Interpolation } from '../../components/interpolation';
3
+ import type { LocalTransform } from '../../components/transform/local-transform';
4
+ export declare const snapToTransform: (interpolation: Interpolation, local: LocalTransform) => void;
5
+ export declare const computeRenderValues: (actor: Actor, interpolation: Interpolation, alpha: number, fixedDeltaTime: number) => void;
@@ -0,0 +1,41 @@
1
+ import { MathOps } from '../../../engine/math-lib';
2
+ import { RigidBody } from '../../components/rigid-body';
3
+ export const snapToTransform = (interpolation, local) => {
4
+ interpolation._prevX = local.position.x;
5
+ interpolation._prevY = local.position.y;
6
+ interpolation._prevRotation = local.rotation;
7
+ interpolation._currX = local.position.x;
8
+ interpolation._currY = local.position.y;
9
+ interpolation._currRotation = local.rotation;
10
+ interpolation.renderX = local.position.x;
11
+ interpolation.renderY = local.position.y;
12
+ interpolation.renderRotation = local.rotation;
13
+ interpolation._initialized = true;
14
+ interpolation._snapRequested = false;
15
+ };
16
+ export const computeRenderValues = (actor, interpolation, alpha, fixedDeltaTime) => {
17
+ if (interpolation.mode === 'extrapolate') {
18
+ const rigidBody = actor.getComponent(RigidBody);
19
+ if (rigidBody !== undefined && !rigidBody.disabled) {
20
+ const timeAhead = alpha * fixedDeltaTime;
21
+ interpolation.renderX =
22
+ interpolation._currX + rigidBody.linearVelocity.x * timeAhead;
23
+ interpolation.renderY =
24
+ interpolation._currY + rigidBody.linearVelocity.y * timeAhead;
25
+ interpolation.renderRotation = rigidBody.lockRotation
26
+ ? interpolation._currRotation
27
+ : interpolation._currRotation + rigidBody.angularVelocity * timeAhead;
28
+ return;
29
+ }
30
+ }
31
+ interpolation.renderX =
32
+ interpolation._prevX +
33
+ (interpolation._currX - interpolation._prevX) * alpha;
34
+ interpolation.renderY =
35
+ interpolation._prevY +
36
+ (interpolation._currY - interpolation._prevY) * alpha;
37
+ interpolation.renderRotation =
38
+ interpolation._prevRotation +
39
+ MathOps.getAngleDelta(interpolation._prevRotation, interpolation._currRotation) *
40
+ alpha;
41
+ };