diff options
Diffstat (limited to 'client/lib/systems')
-rw-r--r-- | client/lib/systems/Collision.ts | 214 | ||||
-rw-r--r-- | client/lib/systems/FacingDirection.ts | 39 | ||||
-rw-r--r-- | client/lib/systems/Input.ts | 86 | ||||
-rw-r--r-- | client/lib/systems/Physics.ts | 94 | ||||
-rw-r--r-- | client/lib/systems/Render.ts | 41 | ||||
-rw-r--r-- | client/lib/systems/System.ts | 15 | ||||
-rw-r--r-- | client/lib/systems/WallBounds.ts | 35 | ||||
-rw-r--r-- | client/lib/systems/index.ts | 8 | ||||
-rw-r--r-- | client/lib/systems/names.ts | 8 |
9 files changed, 0 insertions, 540 deletions
diff --git a/client/lib/systems/Collision.ts b/client/lib/systems/Collision.ts deleted file mode 100644 index 16ad8c6..0000000 --- a/client/lib/systems/Collision.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { SystemNames, System } from "."; -import { - Mass, - BoundingBox, - ComponentNames, - Jump, - Velocity, - Moment, -} from "../components"; -import { PhysicsConstants } from "../config"; -import { Entity } from "../entities"; -import type { Dimension2D } from "../interfaces"; -import { QuadTree } from "../structures"; - -export class Collision extends System { - private static readonly COLLIDABLE_COMPONENTS = [ - ComponentNames.Collide, - ComponentNames.TopCollidable, - ]; - private static readonly QUADTREE_MAX_LEVELS = 10; - private static readonly QUADTREE_SPLIT_THRESHOLD = 10; - - private quadTree: QuadTree; - - constructor(screenDimensions: Dimension2D) { - super(SystemNames.Collision); - - this.quadTree = new QuadTree( - { x: 0, y: 0 }, - screenDimensions, - Collision.QUADTREE_MAX_LEVELS, - Collision.QUADTREE_SPLIT_THRESHOLD - ); - } - - public update( - dt: number, - entityMap: Map<number, Entity>, - entityComponents: Map<string, Set<number>> - ) { - this.quadTree.clear(); - - const entitiesToAddToQuadtree: Entity[] = []; - Collision.COLLIDABLE_COMPONENTS.map((componentName) => - entityComponents.get(componentName) - ).forEach((entityIds: Set<number>) => - entityIds.forEach((id) => { - const entity = entityMap.get(id); - if (!entity.hasComponent(ComponentNames.BoundingBox)) { - return; - } - entitiesToAddToQuadtree.push(entity); - }) - ); - - entitiesToAddToQuadtree.forEach((entity) => { - const boundingBox = entity.getComponent<BoundingBox>( - ComponentNames.BoundingBox - ); - - this.quadTree.insert( - entity.id, - boundingBox.dimension, - boundingBox.center - ); - }); - - const collidingEntities = this.getCollidingEntities( - entitiesToAddToQuadtree, - entityMap - ); - collidingEntities.forEach(([entityAId, entityBId]) => { - const [entityA, entityB] = [entityAId, entityBId].map((id) => - entityMap.get(id) - ); - this.performCollision(entityA, entityB); - }); - } - - private performCollision(entityA: Entity, entityB: Entity) { - const [entityABoundingBox, entityBBoundingBox] = [entityA, entityB].map( - (entity) => entity.getComponent<BoundingBox>(ComponentNames.BoundingBox) - ); - - let velocity: Velocity; - if (entityA.hasComponent(ComponentNames.Velocity)) { - velocity = entityA.getComponent<Velocity>(ComponentNames.Velocity); - } - - if ( - entityA.hasComponent(ComponentNames.Collide) && - entityB.hasComponent(ComponentNames.TopCollidable) && - entityABoundingBox.center.y <= entityBBoundingBox.center.y && - velocity && - velocity.dCartesian.dy >= 0 // don't apply floor logic when coming through the bottom - ) { - if (entityBBoundingBox.rotation != 0) { - throw new Error( - `entity with id ${entityB.id} has TopCollidable component and a non-zero rotation. that is not (yet) supported.` - ); - } - - // remove previous velocity in the y axis - velocity.dCartesian.dy = 0; - - // apply normal force - if (entityA.hasComponent(ComponentNames.Gravity)) { - const mass = entityA.getComponent<Mass>(ComponentNames.Mass).mass; - const F_n = -mass * PhysicsConstants.GRAVITY; - - entityA.getComponent<Forces>(ComponentNames.Forces).forces.push({ - fCartesian: { fy: F_n }, - }); - } - - // reset the entities' jump - if (entityA.hasComponent(ComponentNames.Jump)) { - entityA.getComponent<Jump>(ComponentNames.Jump).canJump = true; - } - - entityABoundingBox.center.y = - entityBBoundingBox.center.y - - entityBBoundingBox.dimension.height / 2 - - this.getDyToPushOutOfFloor(entityABoundingBox, entityBBoundingBox); - } - } - - private getCollidingEntities( - collidableEntities: Entity[], - entityMap: Map<number, Entity> - ): [number, number][] { - const collidingEntityIds: [number, number] = []; - - for (const entity of collidableEntities) { - const boundingBox = entity.getComponent<BoundingBox>( - ComponentNames.BoundingBox - ); - - this.quadTree - .getNeighborIds({ - id: entity.id, - dimension: boundingBox.dimension, - center: boundingBox.center, - }) - .filter((neighborId) => neighborId != entity.id) - .forEach((neighborId) => { - const neighborBoundingBox = entityMap - .get(neighborId) - .getComponent<BoundingBox>(ComponentNames.BoundingBox); - - if (boundingBox.isCollidingWith(neighborBoundingBox)) { - collidingEntityIds.push([entity.id, neighborId]); - } - }); - } - - return collidingEntityIds; - } - - private getDyToPushOutOfFloor( - entityBoundingBox: BoundingBox, - floorBoundingBox: BoundingBox - ): number { - // ramblings: https://excalidraw.com/#json=z-xD86Za4a3duZuV2Oky0,KaGe-5iHJu1Si8inEo4GLQ - const { - rotation, - center: { x, y }, - dimension: { width, height }, - } = entityBoundingBox; - - let rads = rotation * (Math.PI / 180); - if (rads >= Math.PI) { - rads -= Math.PI; // we have symmetry so we can skip two cases - } - - let boundedCollisionX = 0; // bounded x on the surface from width - let clippedX = 0; // x coordinate of the vertex below the surface - let outScribedRectangleHeight, dy, dx; - - if (rads <= Math.PI / 2) { - dx = (width * Math.cos(rads) - height * Math.sin(rads)) / 2; - outScribedRectangleHeight = - width * Math.sin(rads) + height * Math.cos(rads); - } else if (rads <= Math.PI) { - rads -= Math.PI / 2; - dx = (height * Math.cos(rads) - width * Math.sin(rads)) / 2; - outScribedRectangleHeight = - width * Math.cos(rads) + height * Math.sin(rads); - } - - if (x >= floorBoundingBox.center.x) { - clippedX = x + dx; - boundedCollisionX = Math.min( - floorBoundingBox.center.x + floorBoundingBox.dimension.width / 2, - clippedX - ); - return ( - outScribedRectangleHeight / 2 - - Math.max((clippedX - boundedCollisionX) * Math.tan(rads), 0) - ); - } - - clippedX = x - dx; - boundedCollisionX = Math.max( - floorBoundingBox.center.x - floorBoundingBox.dimension.width / 2, - clippedX - ); - - return ( - outScribedRectangleHeight / 2 - - Math.max((boundedCollisionX - clippedX) * Math.tan(rads), 0) - ); - } -} diff --git a/client/lib/systems/FacingDirection.ts b/client/lib/systems/FacingDirection.ts deleted file mode 100644 index fbb4c7c..0000000 --- a/client/lib/systems/FacingDirection.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { - ComponentNames, - Velocity, - FacingDirection as FacingDirectionComponent, -} from "../components"; -import type { Entity } from "../entities"; -import { System, SystemNames } from "./"; - -export class FacingDirection extends System { - constructor() { - super(SystemNames.FacingDirection); - } - - public update( - _dt: number, - entityMap: Map<number, Entity>, - componentEntities: Map<string, Set<number>> - ) { - componentEntities - .get(ComponentNames.FacingDirection) - ?.forEach((entityId) => { - const entity = entityMap.get(entityId); - if (!entity.hasComponent(ComponentNames.Velocity)) { - return; - } - - const velocity = entity.getComponent<Velocity>(ComponentNames.Velocity); - const facingDirection = entity.getComponent<FacingDirectionComponent>( - ComponentNames.FacingDirection - ); - - if (velocity.dCartesian.dx > 0) { - entity.addComponent(facingDirection.facingRightSprite); - } else if (velocity.dCartesian.dx < 0) { - entity.addComponent(facingDirection.facingLeftSprite); - } - }); - } -} diff --git a/client/lib/systems/Input.ts b/client/lib/systems/Input.ts deleted file mode 100644 index 92932dd..0000000 --- a/client/lib/systems/Input.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { - Jump, - Forces, - Acceleration, - ComponentNames, - Velocity, - Mass, -} from "../components"; -import { KeyConstants, PhysicsConstants } from "../config"; -import type { Entity } from "../entities"; -import { Action } from "../interfaces"; -import { System, SystemNames } from "./"; - -export class Input extends System { - private keys: Set<string>; - private actionTimeStamps: Map<Action, number>; - - constructor() { - super(SystemNames.Input); - - this.keys = new Set<number>(); - this.actionTimeStamps = new Map<Action, number>(); - } - - public keyPressed(key: string) { - this.keys.add(key); - } - - public keyReleased(key: string) { - this.keys.delete(key); - } - - private hasSomeKey(keys: string[]): boolean { - return keys.some((key) => this.keys.has(key)); - } - - public update( - dt: number, - entityMap: Map<number, Entity>, - componentEntities: Map<string, Set<number>> - ) { - componentEntities.get(ComponentNames.Control)?.forEach((entityId) => { - const entity = entityMap.get(entityId); - if (!entity.hasComponent(ComponentNames.Velocity)) { - return; - } - - const velocity = entity.getComponent<Velocity>(ComponentNames.Velocity); - - if (this.hasSomeKey(KeyConstants.ActionKeys.get(Action.MOVE_RIGHT))) { - velocity.dCartesian.dx = PhysicsConstants.PLAYER_MOVE_VEL; - } else if ( - this.hasSomeKey(KeyConstants.ActionKeys.get(Action.MOVE_LEFT)) - ) { - velocity.dCartesian.dx = -PhysicsConstants.PLAYER_MOVE_VEL; - } else { - velocity.dCartesian.dx = 0; - } - }); - - componentEntities.get(ComponentNames.Jump)?.forEach((entityId) => { - const entity = entityMap.get(entityId); - const jump = entity.getComponent<Jump>(ComponentNames.Jump); - const velocity = entity.getComponent<Velocity>(ComponentNames.Velocity); - - if (this.hasSomeKey(KeyConstants.ActionKeys.get(Action.JUMP))) { - if (jump.canJump) { - this.actionTimeStamps.set(Action.JUMP, performance.now()); - - velocity.dCartesian.dy = PhysicsConstants.PLAYER_JUMP_INITIAL_VEL; - jump.canJump = false; - } - - if ( - performance.now() - this.actionTimeStamps.get(Action.JUMP) < - PhysicsConstants.MAX_JUMP_TIME_MS - ) { - const mass = entity.getComponent<Mass>(ComponentNames.Mass).mass; - entity.getComponent<Forces>(ComponentNames.Forces)?.forces.push({ - fCartesian: { fy: mass * PhysicsConstants.PLAYER_JUMP_ACC }, - }); - } - } - }); - } -} diff --git a/client/lib/systems/Physics.ts b/client/lib/systems/Physics.ts deleted file mode 100644 index 319ae29..0000000 --- a/client/lib/systems/Physics.ts +++ /dev/null @@ -1,94 +0,0 @@ -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; - }); - } -} diff --git a/client/lib/systems/Render.ts b/client/lib/systems/Render.ts deleted file mode 100644 index 0c76b00..0000000 --- a/client/lib/systems/Render.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { System, SystemNames } from "."; -import { BoundingBox, ComponentNames, Sprite } from "../components"; -import type { Entity } from "../entities"; -import type { DrawArgs } from "../interfaces"; - -export class Render extends System { - private ctx: CanvasRenderingContext2D; - - constructor(ctx: CanvasRenderingContext2D) { - super(SystemNames.Render); - this.ctx = ctx; - } - - public update( - dt: number, - entityMap: Map<number, Entity>, - componentEntities: Map<string, Set<number>> - ) { - this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); - - componentEntities.get(ComponentNames.Sprite)?.forEach((entityId) => { - const entity = entityMap.get(entityId); - const sprite = entity.getComponent<Sprite>(ComponentNames.Sprite); - sprite.update(dt); - - let drawArgs: DrawArgs; - if (entity.hasComponent(ComponentNames.BoundingBox)) { - const boundingBox = entity.getComponent<BoundingBox>( - ComponentNames.BoundingBox - ); - - drawArgs = { - center: boundingBox.center, - dimension: boundingBox.dimension, - rotation: boundingBox.rotation, - }; - } - sprite.draw(this.ctx, drawArgs); - }); - } -} diff --git a/client/lib/systems/System.ts b/client/lib/systems/System.ts deleted file mode 100644 index 2accc97..0000000 --- a/client/lib/systems/System.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Entity } from "../entities"; - -export abstract class System { - public readonly name: string; - - constructor(name: string) { - this.name = name; - } - - abstract update( - dt: number, - entityMap: Map<number, Entity>, - componentEntities: Map<string, Set<number>> - ): void; -} diff --git a/client/lib/systems/WallBounds.ts b/client/lib/systems/WallBounds.ts deleted file mode 100644 index 3fd5dc4..0000000 --- a/client/lib/systems/WallBounds.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { System, SystemNames } from "."; -import { BoundingBox, ComponentNames } from "../components"; -import type { Entity } from "../entities"; - -export class WallBounds extends System { - private screenWidth: number; - - constructor(screenWidth: number) { - super(SystemNames.WallBounds); - - this.screenWidth = screenWidth; - } - - public update( - _dt: number, - entityMap: Map<number, Entity>, - componentEntities: Map<string, Set<number>> - ) { - componentEntities.get(ComponentNames.WallBounded)?.forEach((entityId) => { - const entity = entityMap.get(entityId); - if (!entity.hasComponent(ComponentNames.BoundingBox)) { - return; - } - - const boundingBox = entity.getComponent<BoundingBox>( - ComponentNames.BoundingBox - ); - - boundingBox.center.x = Math.min( - this.screenWidth - boundingBox.dimension.width / 2, - Math.max(boundingBox.dimension.width / 2, boundingBox.center.x) - ); - }); - } -} diff --git a/client/lib/systems/index.ts b/client/lib/systems/index.ts deleted file mode 100644 index 6cb6f35..0000000 --- a/client/lib/systems/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export * from "./names"; -export * from "./System"; -export * from "./Render"; -export * from "./Physics"; -export * from "./Input"; -export * from "./FacingDirection"; -export * from "./Collision"; -export * from "./WallBounds"; diff --git a/client/lib/systems/names.ts b/client/lib/systems/names.ts deleted file mode 100644 index 23f31fc..0000000 --- a/client/lib/systems/names.ts +++ /dev/null @@ -1,8 +0,0 @@ -export namespace SystemNames { - export const Render = "Render"; - export const Physics = "Physics"; - export const FacingDirection = "FacingDirection"; - export const Input = "Input"; - export const Collision = "Collision"; - export const WallBounds = "WallBounds"; -} |