2023-08-25 18:48:17 -04:00
|
|
|
|
import { System, SystemNames } from '.';
|
2023-07-19 23:38:24 -04:00
|
|
|
|
import {
|
|
|
|
|
BoundingBox,
|
|
|
|
|
ComponentNames,
|
|
|
|
|
Forces,
|
|
|
|
|
Gravity,
|
|
|
|
|
Velocity,
|
|
|
|
|
Mass,
|
|
|
|
|
Jump,
|
2023-08-12 15:49:16 -04:00
|
|
|
|
Moment,
|
2023-08-25 18:48:17 -04:00
|
|
|
|
Control
|
|
|
|
|
} 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);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-20 23:47:32 -04:00
|
|
|
|
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;
|
2023-08-13 19:09:12 -04:00
|
|
|
|
const velocity = entity.getComponent<Velocity>(
|
2023-08-25 18:48:17 -04:00
|
|
|
|
ComponentNames.Velocity
|
2023-08-13 19:09:12 -04:00
|
|
|
|
).velocity;
|
2023-07-19 23:38:24 -04:00
|
|
|
|
const inertia = entity.getComponent<Moment>(
|
2023-08-25 18:48:17 -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-25 18:48:17 -04:00
|
|
|
|
fx: 0
|
2023-07-19 23:38:24 -04:00
|
|
|
|
},
|
2023-08-25 18:48:17 -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),
|
2023-08-25 18:48:17 -04:00
|
|
|
|
fy: accum.fCartesian.fy + (fCartesian?.fy ?? 0)
|
2023-07-19 23:38:24 -04:00
|
|
|
|
},
|
2023-08-25 18:48:17 -04:00
|
|
|
|
torque: accum.torque + (torque ?? 0)
|
2023-07-19 23:38:24 -04:00
|
|
|
|
}),
|
2023-08-25 18:48:17 -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,
|
2023-08-25 18:48:17 -04:00
|
|
|
|
sumOfForces.fCartesian.fx
|
2023-07-19 23:38:24 -04:00
|
|
|
|
].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) => {
|
2023-08-13 19:09:12 -04:00
|
|
|
|
const velocityComponent: Velocity = new Velocity();
|
2023-08-12 15:49:16 -04:00
|
|
|
|
const control = entity.getComponent<Control>(ComponentNames.Control);
|
|
|
|
|
|
2023-08-13 19:09:12 -04:00
|
|
|
|
velocityComponent.add(
|
2023-08-25 18:48:17 -04:00
|
|
|
|
entity.getComponent<Velocity>(ComponentNames.Velocity).velocity
|
2023-08-13 19:09:12 -04:00
|
|
|
|
);
|
2023-08-12 15:49:16 -04:00
|
|
|
|
if (control) {
|
2023-08-13 19:09:12 -04:00
|
|
|
|
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-25 18:48:17 -04:00
|
|
|
|
ComponentNames.BoundingBox
|
2023-07-19 23:38:24 -04:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// integrate velocity
|
2023-08-13 19:09:12 -04:00
|
|
|
|
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) {
|
2023-08-13 19:09:12 -04:00
|
|
|
|
control.controlVelocityComponent = new Velocity();
|
2023-08-12 15:49:16 -04:00
|
|
|
|
}
|
2023-07-19 23:38:24 -04:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|