summaryrefslogtreecommitdiff
path: root/client/lib/systems/Physics.ts
blob: 319ae29f7545bf518de793127e51ca5884fa8c87 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import { System, SystemNames } from ".";
import {
  Acceleration,
  BoundingBox,
  ComponentNames,
  Forces,
  Gravity,
  Velocity,
  Mass,
  Jump,
} from "../components";
import { PhysicsConstants } from "../config";
import type { Entity } from "../entities";
import type { Force2D } from "../interfaces";

export class Physics extends System {
  constructor() {
    super(SystemNames.Physics);
  }

  public update(
    dt: number,
    entityMap: Map<number, Entity>,
    componentEntities: Map<string, Set<number>>
  ): void {
    componentEntities.get(ComponentNames.Forces)?.forEach((entityId) => {
      const entity = entityMap.get(entityId);

      const mass = entity.getComponent<Mass>(ComponentNames.Mass).mass;
      const forces = entity.getComponent<Forces>(ComponentNames.Forces).forces;
      const velocity = entity.getComponent<Velocity>(ComponentNames.Velocity);
      const inertia = entity.getComponent<Moment>(
        ComponentNames.Moment
      ).inertia;

      // F_g = mg, applied only until terminal velocity is reached
      if (entity.hasComponent(ComponentNames.Gravity)) {
        const gravity = entity.getComponent<Gravity>(ComponentNames.Gravity);
        if (velocity.dCartesian.dy <= gravity.terminalVelocity) {
          forces.push({
            fCartesian: {
              fy: mass * PhysicsConstants.GRAVITY,
            },
          });
        }
      }

      // ma = Σ(F), Iα = Σ(T)
      const sumOfForces = forces.reduce(
        (accum: Force2D, { fCartesian, torque }: Force2D) => ({
          fCartesian: {
            fx: accum.fCartesian.fx + (fCartesian?.fx ?? 0),
            fy: accum.fCartesian.fy + (fCartesian?.fy ?? 0),
          },
          torque: accum.torque + (torque ?? 0),
        }),
        { fCartesian: { fx: 0, fy: 0 }, torque: 0 }
      );

      // integrate accelerations
      const [ddy, ddx] = [
        sumOfForces.fCartesian.fy,
        sumOfForces.fCartesian.fx,
      ].map((x) => x / mass);
      velocity.dCartesian.dx += ddx * dt;
      velocity.dCartesian.dy += ddy * dt;
      velocity.dTheta += (sumOfForces.torque * dt) / inertia;
      // clear the forces
      entity.getComponent<Forces>(ComponentNames.Forces).forces = [];

      // maybe we fell off the floor
      if (ddy > 0 && entity.hasComponent(ComponentNames.Jump)) {
        entity.getComponent<Jump>(ComponentNames.Jump).canJump = false;
      }
    });

    componentEntities.get(ComponentNames.Velocity)?.forEach((entityId) => {
      const entity = entityMap.get(entityId);
      const velocity = entity.getComponent<Velocity>(ComponentNames.Velocity);
      const boundingBox = entity.getComponent<BoundingBox>(
        ComponentNames.BoundingBox
      );

      // integrate velocity
      boundingBox.center.x += velocity.dCartesian.dx * dt;
      boundingBox.center.y += velocity.dCartesian.dy * dt;
      boundingBox.rotation += velocity.dTheta * dt;
      boundingBox.rotation =
        (boundingBox.rotation < 0
          ? 360 + boundingBox.rotation
          : boundingBox.rotation) % 360;
    });
  }
}