jumpstorm/engine/systems/Physics.ts

108 lines
3.3 KiB
TypeScript
Raw Normal View History

2023-07-19 23:38:24 -04:00
import { System, SystemNames } from ".";
import {
BoundingBox,
ComponentNames,
Forces,
Gravity,
Velocity,
Mass,
Jump,
2023-08-12 15:49:16 -04:00
Moment,
Control,
2023-07-19 23:38:24 -04:00
} from "../components";
import { PhysicsConstants } from "../config";
import type { Force2D, Velocity2D } from "../interfaces";
import { Game } from "../Game";
2023-07-19 23:38:24 -04:00
export class Physics extends System {
constructor() {
super(SystemNames.Physics);
}
public update(dt: number, game: Game): void {
2023-08-12 15:49:16 -04:00
game.forEachEntityWithComponent(ComponentNames.Forces, (entity) => {
2023-07-19 23:38:24 -04:00
const mass = entity.getComponent<Mass>(ComponentNames.Mass).mass;
const forces = entity.getComponent<Forces>(ComponentNames.Forces).forces;
const velocity = entity.getComponent<Velocity>(
ComponentNames.Velocity,
).velocity;
2023-07-19 23:38:24 -04:00
const inertia = entity.getComponent<Moment>(
2023-08-12 15:49:16 -04:00
ComponentNames.Moment,
2023-07-19 23:38:24 -04:00
).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,
2023-08-12 15:49:16 -04:00
fx: 0,
2023-07-19 23:38:24 -04:00
},
2023-08-12 15:49:16 -04:00
torque: 0,
2023-07-19 23:38:24 -04:00
});
}
}
// 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),
}),
2023-08-12 15:49:16 -04:00
{ fCartesian: { fx: 0, fy: 0 }, torque: 0 },
2023-07-19 23:38:24 -04:00
);
// 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;
2023-08-12 15:49:16 -04:00
2023-07-19 23:38:24 -04:00
// 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;
}
});
2023-08-12 15:49:16 -04:00
game.forEachEntityWithComponent(ComponentNames.Velocity, (entity) => {
const velocityComponent: Velocity = new Velocity();
2023-08-12 15:49:16 -04:00
const control = entity.getComponent<Control>(ComponentNames.Control);
velocityComponent.add(
entity.getComponent<Velocity>(ComponentNames.Velocity).velocity,
);
2023-08-12 15:49:16 -04:00
if (control) {
velocityComponent.add(control.controlVelocityComponent.velocity);
2023-08-12 15:49:16 -04:00
}
2023-07-19 23:38:24 -04:00
const boundingBox = entity.getComponent<BoundingBox>(
2023-08-12 15:49:16 -04:00
ComponentNames.BoundingBox,
2023-07-19 23:38:24 -04:00
);
// integrate velocity
boundingBox.center.x += velocityComponent.velocity.dCartesian.dx * dt;
boundingBox.center.y += velocityComponent.velocity.dCartesian.dy * dt;
boundingBox.rotation += velocityComponent.velocity.dTheta * dt;
2023-07-19 23:38:24 -04:00
boundingBox.rotation =
(boundingBox.rotation < 0
? 360 + boundingBox.rotation
: boundingBox.rotation) % 360;
2023-08-12 15:49:16 -04:00
// clear the control velocity
if (control) {
control.controlVelocityComponent = new Velocity();
2023-08-12 15:49:16 -04:00
}
2023-07-19 23:38:24 -04:00
});
}
}