From 72c6c7de12e9833f52bf2d0718d70f044f8ab57e Mon Sep 17 00:00:00 2001 From: Elizabeth Hunt Date: Thu, 20 Jul 2023 20:47:32 -0700 Subject: a bit of refactoring; importing engine into bun for server --- .gitignore | 169 +++++++++++++++++++++ client/lib/Game.ts | 70 --------- client/lib/JumpStorm.ts | 54 ------- client/lib/components/BoundingBox.ts | 97 ------------ client/lib/components/Collide.ts | 7 - client/lib/components/Component.ts | 7 - client/lib/components/Control.ts | 7 - client/lib/components/FacingDirection.ts | 13 -- client/lib/components/Forces.ts | 17 --- client/lib/components/Gravity.ts | 13 -- client/lib/components/Jump.ts | 10 -- client/lib/components/Mass.ts | 10 -- client/lib/components/Moment.ts | 10 -- client/lib/components/Sprite.ts | 92 ------------ client/lib/components/TopCollidable.ts | 7 - client/lib/components/Velocity.ts | 15 -- client/lib/components/WallBounded.ts | 7 - client/lib/components/index.ts | 15 -- client/lib/components/names.ts | 15 -- client/lib/config/assets.ts | 40 ----- client/lib/config/constants.ts | 34 ----- client/lib/config/index.ts | 3 - client/lib/config/sprites.ts | 49 ------ client/lib/entities/Entity.ts | 33 ---- client/lib/entities/Floor.ts | 31 ---- client/lib/entities/Player.ts | 68 --------- client/lib/entities/index.ts | 3 - client/lib/interfaces/Action.ts | 5 - client/lib/interfaces/Direction.ts | 6 - client/lib/interfaces/Draw.ts | 9 -- client/lib/interfaces/LeaderBoardEntry.ts | 5 - client/lib/interfaces/Vec2.ts | 22 --- client/lib/interfaces/index.ts | 5 - client/lib/structures/QuadTree.ts | 154 ------------------- client/lib/structures/index.ts | 1 - client/lib/systems/Collision.ts | 214 -------------------------- client/lib/systems/FacingDirection.ts | 39 ----- client/lib/systems/Input.ts | 86 ----------- client/lib/systems/Physics.ts | 94 ------------ client/lib/systems/Render.ts | 41 ----- client/lib/systems/System.ts | 15 -- client/lib/systems/WallBounds.ts | 35 ----- client/lib/systems/index.ts | 8 - client/lib/systems/names.ts | 8 - client/lib/utils/dotProduct.ts | 4 - client/lib/utils/index.ts | 3 - client/lib/utils/normalizeVector.ts | 8 - client/lib/utils/rotateVector.ts | 15 -- client/package-lock.json | 8 + client/package.json | 3 + client/src/JumpStorm.ts | 54 +++++++ client/src/components/GameCanvas.svelte | 9 +- client/src/components/LeaderBoard.svelte | 2 +- client/src/components/LeaderBoardCard.svelte | 2 +- client/tsconfig.json | 3 + client/vite.config.ts | 12 +- engine/Game.ts | 76 ++++++++++ engine/components/BoundingBox.ts | 97 ++++++++++++ engine/components/Collide.ts | 7 + engine/components/Component.ts | 7 + engine/components/Control.ts | 7 + engine/components/FacingDirection.ts | 13 ++ engine/components/Forces.ts | 17 +++ engine/components/Gravity.ts | 13 ++ engine/components/Jump.ts | 10 ++ engine/components/Mass.ts | 10 ++ engine/components/Moment.ts | 10 ++ engine/components/Sprite.ts | 92 ++++++++++++ engine/components/TopCollidable.ts | 7 + engine/components/Velocity.ts | 15 ++ engine/components/WallBounded.ts | 7 + engine/components/index.ts | 15 ++ engine/components/names.ts | 15 ++ engine/config/assets.ts | 40 +++++ engine/config/constants.ts | 34 +++++ engine/config/index.ts | 3 + engine/config/sprites.ts | 49 ++++++ engine/entities/Entity.ts | 33 ++++ engine/entities/Floor.ts | 31 ++++ engine/entities/Player.ts | 68 +++++++++ engine/entities/index.ts | 3 + engine/interfaces/Action.ts | 5 + engine/interfaces/Direction.ts | 6 + engine/interfaces/Draw.ts | 9 ++ engine/interfaces/LeaderBoardEntry.ts | 5 + engine/interfaces/Vec2.ts | 22 +++ engine/interfaces/index.ts | 5 + engine/structures/QuadTree.ts | 154 +++++++++++++++++++ engine/structures/index.ts | 1 + engine/systems/Collision.ts | 215 +++++++++++++++++++++++++++ engine/systems/FacingDirection.ts | 36 +++++ engine/systems/Input.ts | 83 +++++++++++ engine/systems/Physics.ts | 91 ++++++++++++ engine/systems/Render.ts | 55 +++++++ engine/systems/System.ts | 12 ++ engine/systems/WallBounds.ts | 36 +++++ engine/systems/index.ts | 8 + engine/systems/names.ts | 8 + engine/utils/clamp.ts | 2 + engine/utils/dotProduct.ts | 4 + engine/utils/index.ts | 4 + engine/utils/normalizeVector.ts | 8 + engine/utils/rotateVector.ts | 15 ++ server/README.md | 15 ++ server/bun.lockb | Bin 0 -> 1650 bytes server/index.ts | 0 server/package.json | 13 ++ server/src/index.ts | 3 + server/tsconfig.json | 21 +++ 109 files changed, 1766 insertions(+), 1515 deletions(-) create mode 100644 .gitignore delete mode 100644 client/lib/Game.ts delete mode 100644 client/lib/JumpStorm.ts delete mode 100644 client/lib/components/BoundingBox.ts delete mode 100644 client/lib/components/Collide.ts delete mode 100644 client/lib/components/Component.ts delete mode 100644 client/lib/components/Control.ts delete mode 100644 client/lib/components/FacingDirection.ts delete mode 100644 client/lib/components/Forces.ts delete mode 100644 client/lib/components/Gravity.ts delete mode 100644 client/lib/components/Jump.ts delete mode 100644 client/lib/components/Mass.ts delete mode 100644 client/lib/components/Moment.ts delete mode 100644 client/lib/components/Sprite.ts delete mode 100644 client/lib/components/TopCollidable.ts delete mode 100644 client/lib/components/Velocity.ts delete mode 100644 client/lib/components/WallBounded.ts delete mode 100644 client/lib/components/index.ts delete mode 100644 client/lib/components/names.ts delete mode 100644 client/lib/config/assets.ts delete mode 100644 client/lib/config/constants.ts delete mode 100644 client/lib/config/index.ts delete mode 100644 client/lib/config/sprites.ts delete mode 100644 client/lib/entities/Entity.ts delete mode 100644 client/lib/entities/Floor.ts delete mode 100644 client/lib/entities/Player.ts delete mode 100644 client/lib/entities/index.ts delete mode 100644 client/lib/interfaces/Action.ts delete mode 100644 client/lib/interfaces/Direction.ts delete mode 100644 client/lib/interfaces/Draw.ts delete mode 100644 client/lib/interfaces/LeaderBoardEntry.ts delete mode 100644 client/lib/interfaces/Vec2.ts delete mode 100644 client/lib/interfaces/index.ts delete mode 100644 client/lib/structures/QuadTree.ts delete mode 100644 client/lib/structures/index.ts delete mode 100644 client/lib/systems/Collision.ts delete mode 100644 client/lib/systems/FacingDirection.ts delete mode 100644 client/lib/systems/Input.ts delete mode 100644 client/lib/systems/Physics.ts delete mode 100644 client/lib/systems/Render.ts delete mode 100644 client/lib/systems/System.ts delete mode 100644 client/lib/systems/WallBounds.ts delete mode 100644 client/lib/systems/index.ts delete mode 100644 client/lib/systems/names.ts delete mode 100644 client/lib/utils/dotProduct.ts delete mode 100644 client/lib/utils/index.ts delete mode 100644 client/lib/utils/normalizeVector.ts delete mode 100644 client/lib/utils/rotateVector.ts create mode 100644 client/src/JumpStorm.ts create mode 100644 engine/Game.ts create mode 100644 engine/components/BoundingBox.ts create mode 100644 engine/components/Collide.ts create mode 100644 engine/components/Component.ts create mode 100644 engine/components/Control.ts create mode 100644 engine/components/FacingDirection.ts create mode 100644 engine/components/Forces.ts create mode 100644 engine/components/Gravity.ts create mode 100644 engine/components/Jump.ts create mode 100644 engine/components/Mass.ts create mode 100644 engine/components/Moment.ts create mode 100644 engine/components/Sprite.ts create mode 100644 engine/components/TopCollidable.ts create mode 100644 engine/components/Velocity.ts create mode 100644 engine/components/WallBounded.ts create mode 100644 engine/components/index.ts create mode 100644 engine/components/names.ts create mode 100644 engine/config/assets.ts create mode 100644 engine/config/constants.ts create mode 100644 engine/config/index.ts create mode 100644 engine/config/sprites.ts create mode 100644 engine/entities/Entity.ts create mode 100644 engine/entities/Floor.ts create mode 100644 engine/entities/Player.ts create mode 100644 engine/entities/index.ts create mode 100644 engine/interfaces/Action.ts create mode 100644 engine/interfaces/Direction.ts create mode 100644 engine/interfaces/Draw.ts create mode 100644 engine/interfaces/LeaderBoardEntry.ts create mode 100644 engine/interfaces/Vec2.ts create mode 100644 engine/interfaces/index.ts create mode 100644 engine/structures/QuadTree.ts create mode 100644 engine/structures/index.ts create mode 100644 engine/systems/Collision.ts create mode 100644 engine/systems/FacingDirection.ts create mode 100644 engine/systems/Input.ts create mode 100644 engine/systems/Physics.ts create mode 100644 engine/systems/Render.ts create mode 100644 engine/systems/System.ts create mode 100644 engine/systems/WallBounds.ts create mode 100644 engine/systems/index.ts create mode 100644 engine/systems/names.ts create mode 100644 engine/utils/clamp.ts create mode 100644 engine/utils/dotProduct.ts create mode 100644 engine/utils/index.ts create mode 100644 engine/utils/normalizeVector.ts create mode 100644 engine/utils/rotateVector.ts create mode 100644 server/README.md create mode 100755 server/bun.lockb create mode 100644 server/index.ts create mode 100644 server/package.json create mode 100644 server/src/index.ts create mode 100644 server/tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f81d56e --- /dev/null +++ b/.gitignore @@ -0,0 +1,169 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +\*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +\*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +\*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +\*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.cache +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +.cache/ + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp +.cache + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.\* diff --git a/client/lib/Game.ts b/client/lib/Game.ts deleted file mode 100644 index d6ffb47..0000000 --- a/client/lib/Game.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Entity } from "./entities"; -import { System } from "./systems"; - -export class Game { - private entities: Map; - private systems: Map; - private systemOrder: string[]; - - private running: boolean; - private lastTimeStamp: number; - - constructor() { - this.running = false; - this.systemOrder = []; - this.systems = new Map(); - this.entities = new Map(); - } - - public start() { - this.lastTimeStamp = performance.now(); - this.running = true; - } - - public addEntity(entity: Entity) { - this.entities.set(entity.id, entity); - } - - public getEntity(id: number): Entity { - return this.entities.get(id); - } - - public removeEntity(id: number) { - this.entities.delete(id); - } - - public addSystem(system: System) { - if (!this.systemOrder.includes(system.name)) { - this.systemOrder.push(system.name); - } - this.systems.set(system.name, system); - } - - public getSystem(name: string): System { - return this.systems.get(name); - } - - public doGameLoop = (timeStamp: number) => { - if (!this.running) { - return; - } - - const dt = timeStamp - this.lastTimeStamp; - this.lastTimeStamp = timeStamp; - - const componentEntities = new Map>(); - this.entities.forEach((entity) => - entity.getComponents().forEach((component) => { - if (!componentEntities.has(component.name)) { - componentEntities.set(component.name, new Set([entity.id])); - return; - } - componentEntities.get(component.name).add(entity.id); - }) - ); - - this.systemOrder.forEach((systemName) => { - this.systems.get(systemName).update(dt, this.entities, componentEntities); - }); - }; -} diff --git a/client/lib/JumpStorm.ts b/client/lib/JumpStorm.ts deleted file mode 100644 index c76d9bc..0000000 --- a/client/lib/JumpStorm.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Floor, Player } from "./entities"; -import { Game } from "./Game"; -import { - WallBounds, - FacingDirection, - Render, - Physics, - Input, - Collision, -} from "./systems"; - -export class JumpStorm { - private game: Game; - - constructor(ctx: CanvasRenderingContext2D) { - this.game = new Game(); - - [ - this.createInputSystem(), - new FacingDirection(), - new Physics(), - new Collision(), - new WallBounds(ctx.canvas.width), - new Render(ctx), - ].forEach((system) => this.game.addSystem(system)); - - [new Floor(160), new Player()].forEach((entity) => - this.game.addEntity(entity) - ); - } - - public play() { - this.game.start(); - - const loop = (timestamp: number) => { - this.game.doGameLoop(timestamp); - requestAnimationFrame(loop); // tail call recursion! /s - }; - requestAnimationFrame(loop); - } - - private createInputSystem(): Input { - const inputSystem = new Input(); - - window.addEventListener("keydown", (e) => { - if (!e.repeat) { - inputSystem.keyPressed(e.key); - } - }); - window.addEventListener("keyup", (e) => inputSystem.keyReleased(e.key)); - - return inputSystem; - } -} diff --git a/client/lib/components/BoundingBox.ts b/client/lib/components/BoundingBox.ts deleted file mode 100644 index 2b1d648..0000000 --- a/client/lib/components/BoundingBox.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { Component, ComponentNames } from "."; -import type { Coord2D, Dimension2D } from "../interfaces"; -import { dotProduct, rotateVector, normalizeVector } from "../utils"; - -export class BoundingBox extends Component { - public center: Coord2D; - public dimension: Dimension2D; - public rotation: number; - - constructor(center: Coord2D, dimension: Dimension2D, rotation?: number) { - super(ComponentNames.BoundingBox); - - this.center = center; - this.dimension = dimension; - this.rotation = rotation ?? 0; - } - - public isCollidingWith(box: BoundingBox): boolean { - const boxes = [this.getVertices(), box.getVertices()]; - for (const poly of boxes) { - for (let i = 0; i < poly.length; ++i) { - const [A, B] = [poly[i], poly[(i + 1) % poly.length]]; - const normal: Coord2D = { x: B.y - A.y, y: A.x - B.x }; - - const [[minThis, maxThis], [minBox, maxBox]] = boxes.map((box) => - box.reduce( - ([min, max], vertex) => { - const projection = dotProduct(normal, vertex); - return [Math.min(min, projection), Math.max(max, projection)]; - }, - [Infinity, -Infinity] - ) - ); - - if (maxThis < minBox || maxBox < minThis) return false; - } - } - - return true; - } - - public getVertices(): Coord2D[] { - return [ - { x: -this.dimension.width / 2, y: -this.dimension.height / 2 }, - { x: -this.dimension.width / 2, y: this.dimension.height / 2 }, - { x: this.dimension.width / 2, y: this.dimension.height / 2 }, - { x: this.dimension.width / 2, y: -this.dimension.height / 2 }, - ] - .map((vertex) => rotateVector(vertex, this.rotation)) - .map((vertex) => { - return { - x: vertex.x + this.center.x, - y: vertex.y + this.center.y, - }; - }); - } - - private getAxes() { - const corners: Coord2D[] = this.getVerticesRelativeToCenter(); - const axes: Coord2D[] = []; - - for (let i = 0; i < corners.length; ++i) { - const [cornerA, cornerB] = [ - corners[i], - corners[(i + 1) % corners.length], - ].map((corner) => rotateVector(corner, this.rotation)); - - axes.push( - normalizeVector({ - x: cornerB.y - cornerA.y, - y: -(cornerB.x - cornerA.x), - }) - ); - } - - return axes; - } - - private project(axis: Coord2D): [number, number] { - const corners = this.getCornersRelativeToCenter(); - let [min, max] = [Infinity, -Infinity]; - - for (const corner of corners) { - const rotated = rotateVector(corner, this.rotation); - const translated = { - x: rotated.x + this.center.x, - y: rotated.y + this.center.y, - }; - const projection = dotProduct(translated, axis); - - min = Math.min(projection, min); - max = Math.max(projection, max); - } - - return [min, max]; - } -} diff --git a/client/lib/components/Collide.ts b/client/lib/components/Collide.ts deleted file mode 100644 index 889ecf8..0000000 --- a/client/lib/components/Collide.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Component, ComponentNames } from "."; - -export class Collide extends Component { - constructor() { - super(ComponentNames.Collide); - } -} diff --git a/client/lib/components/Component.ts b/client/lib/components/Component.ts deleted file mode 100644 index 7331982..0000000 --- a/client/lib/components/Component.ts +++ /dev/null @@ -1,7 +0,0 @@ -export abstract class Component { - public readonly name: string; - - constructor(name: string) { - this.name = name; - } -} diff --git a/client/lib/components/Control.ts b/client/lib/components/Control.ts deleted file mode 100644 index 094ef1c..0000000 --- a/client/lib/components/Control.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Component, ComponentNames } from "."; - -export class Control extends Component { - constructor() { - super(ComponentNames.Control); - } -} diff --git a/client/lib/components/FacingDirection.ts b/client/lib/components/FacingDirection.ts deleted file mode 100644 index 1c701a3..0000000 --- a/client/lib/components/FacingDirection.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Component, ComponentNames, Sprite } from "."; - -export class FacingDirection extends Component { - public readonly facingLeftSprite: Sprite; - public readonly facingRightSprite: Sprite; - - constructor(facingLeftSprite: Sprite, facingRightSprite: Sprite) { - super(ComponentNames.FacingDirection); - - this.facingLeftSprite = facingLeftSprite; - this.facingRightSprite = facingRightSprite; - } -} diff --git a/client/lib/components/Forces.ts b/client/lib/components/Forces.ts deleted file mode 100644 index bf540a1..0000000 --- a/client/lib/components/Forces.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { Accel2D, Force2D } from "../interfaces"; -import { Component } from "./Component"; -import { ComponentNames } from "."; - -/** - * A list of forces and torque, (in newtons, and newton-meters respectively) - * to apply on one Physics system update (after which, they are cleared). - */ -export class Forces extends Component { - public forces: Force2D[]; - - constructor(forces?: Force2D[]) { - super(ComponentNames.Forces); - - this.forces = forces ?? []; - } -} diff --git a/client/lib/components/Gravity.ts b/client/lib/components/Gravity.ts deleted file mode 100644 index 89fcb67..0000000 --- a/client/lib/components/Gravity.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ComponentNames, Component } from "."; - -export class Gravity extends Component { - private static DEFAULT_TERMINAL_VELOCITY = 5; - - public terminalVelocity: number; - - constructor(terminalVelocity?: number) { - super(ComponentNames.Gravity); - this.terminalVelocity = - terminalVelocity ?? Gravity.DEFAULT_TERMINAL_VELOCITY; - } -} diff --git a/client/lib/components/Jump.ts b/client/lib/components/Jump.ts deleted file mode 100644 index 0b40767..0000000 --- a/client/lib/components/Jump.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Component, ComponentNames } from "."; - -export class Jump extends Component { - public canJump: boolean; - - constructor() { - super(ComponentNames.Jump); - this.canJump = false; - } -} diff --git a/client/lib/components/Mass.ts b/client/lib/components/Mass.ts deleted file mode 100644 index daa2d71..0000000 --- a/client/lib/components/Mass.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Component, ComponentNames } from "."; - -export class Mass extends Component { - public mass: number; - - constructor(mass: number) { - super(ComponentNames.Mass); - this.mass = mass; - } -} diff --git a/client/lib/components/Moment.ts b/client/lib/components/Moment.ts deleted file mode 100644 index 3d0dd2f..0000000 --- a/client/lib/components/Moment.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Component, ComponentNames } from "."; - -export class Moment extends Component { - public inertia: number; - - constructor(inertia: number) { - super(ComponentNames.Moment); - this.inertia = inertia; - } -} diff --git a/client/lib/components/Sprite.ts b/client/lib/components/Sprite.ts deleted file mode 100644 index 90e1389..0000000 --- a/client/lib/components/Sprite.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { Component, ComponentNames } from "."; -import type { Dimension2D, DrawArgs, Coord2D } from "../interfaces"; - -export class Sprite extends Component { - private sheet: HTMLImageElement; - - private spriteImgPos: Coord2D; - private spriteImgDimensions: Dimension2D; - - private msPerFrame: number; - private msSinceLastFrame: number; - private currentFrame: number; - private numFrames: number; - - constructor( - sheet: HTMLImageElement, - spriteImgPos: Coord2D, - spriteImgDimensions: Dimension2D, - msPerFrame: number, - numFrames: number - ) { - super(ComponentNames.Sprite); - - this.sheet = sheet; - this.spriteImgPos = spriteImgPos; - this.spriteImgDimensions = spriteImgDimensions; - this.msPerFrame = msPerFrame; - this.numFrames = numFrames; - - this.msSinceLastFrame = 0; - this.currentFrame = 0; - } - - public update(dt: number) { - this.msSinceLastFrame += dt; - if (this.msSinceLastFrame >= this.msPerFrame) { - this.currentFrame = (this.currentFrame + 1) % this.numFrames; - this.msSinceLastFrame = 0; - } - } - - public draw(ctx: CanvasRenderingContext2D, drawArgs: DrawArgs) { - const { center, rotation, tint, opacity } = drawArgs; - - ctx.save(); - ctx.translate(center.x, center.y); - if (rotation != 0) { - ctx.rotate(rotation * (Math.PI / 180)); - } - ctx.translate(-center.x, -center.y); - - if (opacity) { - ctx.globalAlpha = opacity; - } - - ctx.drawImage( - this.sheet, - ...this.getSpriteArgs(), - ...this.getDrawArgs(drawArgs) - ); - - if (tint) { - ctx.globalAlpha = 0.5; - ctx.globalCompositeOperation = "source-atop"; - ctx.fillStyle = tint; - ctx.fillRect(...this.getDrawArgs(drawArgs)); - } - - ctx.restore(); - } - - private getSpriteArgs(): [sx: number, sy: number, sw: number, sh: number] { - return [ - this.spriteImgPos.x + this.currentFrame * this.spriteImgDimensions.width, - this.spriteImgPos.y, - this.spriteImgDimensions.width, - this.spriteImgDimensions.height, - ]; - } - - private getDrawArgs({ - center, - dimension, - }: DrawArgs): [dx: number, dy: number, dw: number, dh: number] { - return [ - center.x - dimension.width / 2, - center.y - dimension.height / 2, - dimension.width, - dimension.height, - ]; - } -} diff --git a/client/lib/components/TopCollidable.ts b/client/lib/components/TopCollidable.ts deleted file mode 100644 index 7fb147d..0000000 --- a/client/lib/components/TopCollidable.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Component, ComponentNames } from "."; - -export class TopCollidable extends Component { - constructor() { - super(ComponentNames.TopCollidable); - } -} diff --git a/client/lib/components/Velocity.ts b/client/lib/components/Velocity.ts deleted file mode 100644 index 119427d..0000000 --- a/client/lib/components/Velocity.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { Velocity2D } from "../interfaces"; -import { Component } from "./Component"; -import { ComponentNames } from "."; - -export class Velocity extends Component { - public dCartesian: Velocity2D; - public dTheta: number; - - constructor(dCartesian: Velocity2D, dTheta: number) { - super(ComponentNames.Velocity); - - this.dCartesian = dCartesian; - this.dTheta = dTheta; - } -} diff --git a/client/lib/components/WallBounded.ts b/client/lib/components/WallBounded.ts deleted file mode 100644 index 5f787e1..0000000 --- a/client/lib/components/WallBounded.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Component, ComponentNames } from "."; - -export class WallBounded extends Component { - constructor() { - super(ComponentNames.WallBounded); - } -} diff --git a/client/lib/components/index.ts b/client/lib/components/index.ts deleted file mode 100644 index 67f1259..0000000 --- a/client/lib/components/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -export * from "./Component"; -export * from "./BoundingBox"; -export * from "./Velocity"; -export * from "./Forces"; -export * from "./Sprite"; -export * from "./FacingDirection"; -export * from "./Jump"; -export * from "./TopCollidable"; -export * from "./Collide"; -export * from "./Control"; -export * from "./WallBounded"; -export * from "./Gravity"; -export * from "./Mass"; -export * from "./Moment"; -export * from "./names"; diff --git a/client/lib/components/names.ts b/client/lib/components/names.ts deleted file mode 100644 index e2ee3d3..0000000 --- a/client/lib/components/names.ts +++ /dev/null @@ -1,15 +0,0 @@ -export namespace ComponentNames { - export const Sprite = "Sprite"; - export const BoundingBox = "BoundingBox"; - export const Velocity = "Velocity"; - export const FacingDirection = "FacingDirection"; - export const Control = "Control"; - export const Jump = "Jump"; - export const TopCollidable = "TopCollidable"; - export const Collide = "Collide"; - export const WallBounded = "WallBounded"; - export const Gravity = "Gravity"; - export const Forces = "Forces"; - export const Mass = "Mass"; - export const Moment = "Moment"; -} diff --git a/client/lib/config/assets.ts b/client/lib/config/assets.ts deleted file mode 100644 index 51a5303..0000000 --- a/client/lib/config/assets.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { SpriteSpec } from "./sprites"; -import { SPRITE_SPECS } from "./sprites"; - -export const IMAGES = new Map(); - -export const loadSpritesIntoImageElements = ( - spriteSpecs: Partial[] -): Promise[] => { - const spritePromises: Promise[] = []; - - for (const spriteSpec of spriteSpecs) { - if (spriteSpec.sheet) { - const img = new Image(); - img.src = spriteSpec.sheet; - IMAGES.set(spriteSpec.sheet, img); - - spritePromises.push( - new Promise((resolve) => { - img.onload = () => resolve(); - }) - ); - } - - if (spriteSpec.states) { - spritePromises.push( - ...loadSpritesIntoImageElements(Object.values(spriteSpec.states)) - ); - } - } - - return spritePromises; -}; - -export const loadAssets = () => - Promise.all([ - ...loadSpritesIntoImageElements( - Array.from(SPRITE_SPECS.keys()).map((key) => SPRITE_SPECS.get(key)) - ), - // TODO: Sound - ]); diff --git a/client/lib/config/constants.ts b/client/lib/config/constants.ts deleted file mode 100644 index 27c8160..0000000 --- a/client/lib/config/constants.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Action } from "../interfaces"; - -export namespace KeyConstants { - export const KeyActions: Record = { - a: Action.MOVE_LEFT, - ArrowLeft: Action.MOVE_LEFT, - d: Action.MOVE_RIGHT, - ArrowRight: Action.MOVE_RIGHT, - w: Action.JUMP, - ArrowUp: Action.JUMP, - }; - - export const ActionKeys: Map = Object.keys( - KeyActions - ).reduce((acc: Map, key) => { - const action = KeyActions[key]; - - if (acc.has(action)) { - acc.get(action).push(key); - return acc; - } - - acc.set(action, [key]); - return acc; - }, new Map()); -} - -export namespace PhysicsConstants { - export const MAX_JUMP_TIME_MS = 150; - export const GRAVITY = 0.0075; - export const PLAYER_MOVE_VEL = 1; - export const PLAYER_JUMP_ACC = -0.01; - export const PLAYER_JUMP_INITIAL_VEL = -0.9; -} diff --git a/client/lib/config/index.ts b/client/lib/config/index.ts deleted file mode 100644 index 7a1052a..0000000 --- a/client/lib/config/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./constants"; -export * from "./assets.ts"; -export * from "./sprites.ts"; diff --git a/client/lib/config/sprites.ts b/client/lib/config/sprites.ts deleted file mode 100644 index 18bec73..0000000 --- a/client/lib/config/sprites.ts +++ /dev/null @@ -1,49 +0,0 @@ -export enum Sprites { - FLOOR, - TRAMPOLINE, - COFFEE, -} - -export interface SpriteSpec { - sheet: string; - width: number; - height: number; - frames: number; - msPerFrame: number; - states?: Record>; -} - -export const SPRITE_SPECS: Map> = new Map< - Sprites, - SpriteSpec ->(); - -const floorSpriteSpec = { - height: 40, - frames: 3, - msPerFrame: 125, - states: {}, -}; -floorSpriteSpec.states = [40, 80, 120, 160].reduce((acc, cur) => { - acc[cur] = { - width: cur, - sheet: `/assets/floor_tile_${cur}.png`, - }; - return acc; -}, {}); -SPRITE_SPECS.set(Sprites.FLOOR, floorSpriteSpec); - -SPRITE_SPECS.set(Sprites.COFFEE, { - msPerFrame: 100, - width: 60, - height: 45, - frames: 3, - states: { - LEFT: { - sheet: "/assets/coffee_left.png", - }, - RIGHT: { - sheet: "/assets/coffee_right.png", - }, - }, -}); diff --git a/client/lib/entities/Entity.ts b/client/lib/entities/Entity.ts deleted file mode 100644 index e57ccde..0000000 --- a/client/lib/entities/Entity.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { Component } from "../components"; -import { ComponentNotFoundError } from "../exceptions"; - -export abstract class Entity { - private static ID = 0; - - public readonly id: number; - public readonly components: Map; - - constructor() { - this.id = Entity.ID++; - this.components = new Map(); - } - - public addComponent(component: Component) { - this.components.set(component.name, component); - } - - public getComponent(name: string): T { - if (!this.hasComponent(name)) { - throw new Error("Entity does not have component " + name); - } - return this.components.get(name) as T; - } - - public getComponents(): Component[] { - return Array.from(this.components.values()); - } - - public hasComponent(name: string): boolean { - return this.components.has(name); - } -} diff --git a/client/lib/entities/Floor.ts b/client/lib/entities/Floor.ts deleted file mode 100644 index d51badc..0000000 --- a/client/lib/entities/Floor.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { IMAGES, SPRITE_SPECS, Sprites, type SpriteSpec } from "../config"; -import { BoundingBox, Sprite } from "../components"; -import { TopCollidable } from "../components/TopCollidable"; -import { Entity } from "../entities"; - -export class Floor extends Entity { - private static spriteSpec: SpriteSpec = SPRITE_SPECS.get(Sprites.FLOOR); - - constructor(width: number) { - super(); - - this.addComponent( - new Sprite( - IMAGES.get(Floor.spriteSpec.states[width].sheet), - { x: 0, y: 0 }, - { width, height: Floor.spriteSpec.height }, - Floor.spriteSpec.msPerFrame, - Floor.spriteSpec.frames - ) - ); - - this.addComponent( - new BoundingBox( - { x: 300, y: 300 }, - { width, height: Floor.spriteSpec.height } - ) - ); - - this.addComponent(new TopCollidable()); - } -} diff --git a/client/lib/entities/Player.ts b/client/lib/entities/Player.ts deleted file mode 100644 index 0ba5a41..0000000 --- a/client/lib/entities/Player.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Entity } from "."; -import { IMAGES, SPRITE_SPECS, Sprites, type SpriteSpec } from "../config"; -import { - Jump, - FacingDirection, - BoundingBox, - Sprite, - Velocity, - Gravity, - WallBounded, - Forces, - Collide, - Control, - Mass, - Moment, -} from "../components"; -import { PhysicsConstants } from "../config"; -import { Direction } from "../interfaces"; - -export class Player extends Entity { - private static MASS: number = 10; - private static MOI: number = 1000; - - private static spriteSpec: SpriteSpec = SPRITE_SPECS.get(Sprites.COFFEE); - - constructor() { - super(); - - this.addComponent( - new BoundingBox( - { x: 300, y: 100 }, - { width: Player.spriteSpec.width, height: Player.spriteSpec.height }, - 0 - ) - ); - - this.addComponent(new Velocity({ dx: 0, dy: 0 }, 0)); - - this.addComponent(new Mass(Player.MASS)); - this.addComponent(new Moment(Player.MOI)); - this.addComponent(new Forces()); - this.addComponent(new Gravity()); - - this.addComponent(new Jump()); - this.addComponent(new Control()); - - this.addComponent(new Collide()); - this.addComponent(new WallBounded()); - - this.addFacingDirectionComponents(); - } - - private addFacingDirectionComponents() { - const [leftSprite, rightSprite] = [Direction.LEFT, Direction.RIGHT].map( - (direction) => - new Sprite( - IMAGES.get(Player.spriteSpec.states[direction].sheet), - { x: 0, y: 0 }, - { width: Player.spriteSpec.width, height: Player.spriteSpec.height }, - Player.spriteSpec.msPerFrame, - Player.spriteSpec.frames - ) - ); - - this.addComponent(new FacingDirection(leftSprite, rightSprite)); - this.addComponent(leftSprite); // face Left by default - } -} diff --git a/client/lib/entities/index.ts b/client/lib/entities/index.ts deleted file mode 100644 index a921512..0000000 --- a/client/lib/entities/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./Entity"; -export * from "./Floor"; -export * from "./Player"; diff --git a/client/lib/interfaces/Action.ts b/client/lib/interfaces/Action.ts deleted file mode 100644 index 61c89e1..0000000 --- a/client/lib/interfaces/Action.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum Action { - MOVE_LEFT, - MOVE_RIGHT, - JUMP, -} diff --git a/client/lib/interfaces/Direction.ts b/client/lib/interfaces/Direction.ts deleted file mode 100644 index 0bc6ef3..0000000 --- a/client/lib/interfaces/Direction.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum Direction { - UP = "UP", - DOWN = "DOWN", - LEFT = "LEFT", - RIGHT = "RIGHT", -} diff --git a/client/lib/interfaces/Draw.ts b/client/lib/interfaces/Draw.ts deleted file mode 100644 index 6561a01..0000000 --- a/client/lib/interfaces/Draw.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { Coord2D, Dimension2D } from "./"; - -export interface DrawArgs { - center: Coord2D; - dimension: Dimension2D; - tint?: string; - opacity?: number; - rotation?: number; -} diff --git a/client/lib/interfaces/LeaderBoardEntry.ts b/client/lib/interfaces/LeaderBoardEntry.ts deleted file mode 100644 index 1b1e7b3..0000000 --- a/client/lib/interfaces/LeaderBoardEntry.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface LeaderBoardEntry { - name: string; - score: number; - avatar: string; -} diff --git a/client/lib/interfaces/Vec2.ts b/client/lib/interfaces/Vec2.ts deleted file mode 100644 index b2bae37..0000000 --- a/client/lib/interfaces/Vec2.ts +++ /dev/null @@ -1,22 +0,0 @@ -export interface Coord2D { - x: number; - y: number; -} - -export interface Dimension2D { - width: number; - height: number; -} - -export interface Velocity2D { - dx: number; - dy: number; -} - -export interface Force2D { - fCartesian: { - fx: number; - fy: number; - }; - torque: number; -} diff --git a/client/lib/interfaces/index.ts b/client/lib/interfaces/index.ts deleted file mode 100644 index 0398abd..0000000 --- a/client/lib/interfaces/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from "./LeaderBoardEntry"; -export * from "./Vec2"; -export * from "./Draw"; -export * from "./Direction"; -export * from "./Action"; diff --git a/client/lib/structures/QuadTree.ts b/client/lib/structures/QuadTree.ts deleted file mode 100644 index 7913e59..0000000 --- a/client/lib/structures/QuadTree.ts +++ /dev/null @@ -1,154 +0,0 @@ -import type { Coord2D, Dimension2D } from "../interfaces"; -import { ComponentNames, BoundingBox } from "../components"; -import { Entity } from "../entities"; - -interface BoxedEntry { - id: number; - dimension: Dimension2D; - center: Coord2D; -} - -enum Quadrant { - I, - II, - III, - IV, -} - -export class QuadTree { - private maxLevels: number; - private splitThreshold: number; - private level: number; - private topLeft: Coord2D; - private dimension: Dimension2D; - - private children: Map; - private objects: BoxedEntry[]; - - constructor( - topLeft: Coord2D, - dimension: Dimension2D, - maxLevels: number, - splitThreshold: number, - level?: number - ) { - this.children = []; - this.objects = []; - - this.maxLevels = maxLevels; - this.splitThreshold = splitThreshold; - this.level = level ?? 0; - } - - public insert(id: number, dimension: Dimension2D, center: Coord2D): void { - if (this.hasChildren()) { - this.getIndices(boundingBox).forEach((i) => - this.children[i].insert(id, dimension, center) - ); - return; - } - - this.objects.push({ id, dimension, center }); - - if ( - this.objects.length > this.splitThreshold && - this.level < this.maxLevels - ) { - if (!this.hasChildren()) { - this.performSplit(); - } - this.realignObjects(); - } - } - - public clear(): void { - this.objects = []; - if (this.hasChildren()) { - this.children.forEach((child) => child.clear()); - this.children.clear(); - } - } - - public getNeighborIds(boxedEntry: BoxedEntry): number[] { - const neighbors: number[] = this.objects.map(({ id }) => id); - - if (this.hasChildren()) { - this.getQuadrants(boxedEntry).forEach((quadrant) => { - this.children - .get(quadrant) - .getNeighborIds(boxedEntry) - .forEach((id) => neighbors.push(id)); - }); - } - - return neighbors; - } - - private performSplit(): void { - const halfWidth = this.dimension.width / 2; - const halfHeight = this.dimension.height / 2; - - [ - [Quadrant.I, { x: this.topLeft.x + halfWidth, y: this.topLeft.y }], - [Quadrant.II, { ...this.topLeft }], - [Quadrant.III, { x: this.topLeft.x, y: this.topLeft.y + halfHeight }], - [ - Quadrant.IV, - { x: this.topLeft.x + halfWidth, y: this.topLeft.y + halfHeight }, - ], - ].forEach(([quadrant, pos]) => { - this.children.set( - quadrant, - new QuadTree( - pos, - { width: halfWidth, height: halfHeight }, - this.maxLevels, - this.splitThreshold, - this.level + 1 - ) - ); - }); - } - - private getQuandrants(boxedEntry: BoxedEntry): Quadrant[] { - const treeCenter: Coord2D = { - x: this.topLeft.x + this.dimension.width / 2, - y: this.topLeft.y + this.dimension.height / 2, - }; - - return [ - [Quadrant.I, (x, y) => x >= treeCenter.x && y < treeCenter.y], - [Quadrant.II, (x, y) => x < treeCenter.x && y < treeCenter.y], - [Quadrant.III, (x, y) => x < treeCenter.x && y >= treeCenter.y], - [Quadrant.IV, (x, y) => x >= treeCenter.x && y >= treeCenter.y], - ] - .filter( - ([_quadrant, condition]) => - condition( - boxedEntry.center.x + boxedEntry.dimension.width / 2, - boxedEntry.center.y + boxedEntry.dimension.height / 2 - ) || - condition( - boxedEntry.center.x - boxedEntry.dimension.width / 2, - boxedEntry.center.y - boxedEntry.dimension.height / 2 - ) - ) - .map(([quadrant]) => quadrant); - } - - private realignObjects(): void { - this.objects.forEach((boxedEntry) => { - this.getQuadrants(boxedEntry).forEach((direction) => { - this.children - .get(direction) - .insert(boxedEntry.id, boxedEntry.dimension, boxedEntry.center); - }); - }); - - this.objects = []; - } - - private hasChildren() { - return this.children && this.children.length > 0; - } -} diff --git a/client/lib/structures/index.ts b/client/lib/structures/index.ts deleted file mode 100644 index 605a82a..0000000 --- a/client/lib/structures/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./QuadTree"; 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, - entityComponents: Map> - ) { - this.quadTree.clear(); - - const entitiesToAddToQuadtree: Entity[] = []; - Collision.COLLIDABLE_COMPONENTS.map((componentName) => - entityComponents.get(componentName) - ).forEach((entityIds: Set) => - entityIds.forEach((id) => { - const entity = entityMap.get(id); - if (!entity.hasComponent(ComponentNames.BoundingBox)) { - return; - } - entitiesToAddToQuadtree.push(entity); - }) - ); - - entitiesToAddToQuadtree.forEach((entity) => { - const boundingBox = entity.getComponent( - 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(ComponentNames.BoundingBox) - ); - - let velocity: Velocity; - if (entityA.hasComponent(ComponentNames.Velocity)) { - velocity = entityA.getComponent(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(ComponentNames.Mass).mass; - const F_n = -mass * PhysicsConstants.GRAVITY; - - entityA.getComponent(ComponentNames.Forces).forces.push({ - fCartesian: { fy: F_n }, - }); - } - - // reset the entities' jump - if (entityA.hasComponent(ComponentNames.Jump)) { - entityA.getComponent(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, number][] { - const collidingEntityIds: [number, number] = []; - - for (const entity of collidableEntities) { - const boundingBox = entity.getComponent( - 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(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, - componentEntities: Map> - ) { - componentEntities - .get(ComponentNames.FacingDirection) - ?.forEach((entityId) => { - const entity = entityMap.get(entityId); - if (!entity.hasComponent(ComponentNames.Velocity)) { - return; - } - - const velocity = entity.getComponent(ComponentNames.Velocity); - const facingDirection = entity.getComponent( - 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; - private actionTimeStamps: Map; - - constructor() { - super(SystemNames.Input); - - this.keys = new Set(); - this.actionTimeStamps = new Map(); - } - - 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, - componentEntities: Map> - ) { - componentEntities.get(ComponentNames.Control)?.forEach((entityId) => { - const entity = entityMap.get(entityId); - if (!entity.hasComponent(ComponentNames.Velocity)) { - return; - } - - const velocity = entity.getComponent(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(ComponentNames.Jump); - const velocity = entity.getComponent(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(ComponentNames.Mass).mass; - entity.getComponent(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, - componentEntities: Map> - ): void { - componentEntities.get(ComponentNames.Forces)?.forEach((entityId) => { - const entity = entityMap.get(entityId); - - const mass = entity.getComponent(ComponentNames.Mass).mass; - const forces = entity.getComponent(ComponentNames.Forces).forces; - const velocity = entity.getComponent(ComponentNames.Velocity); - const inertia = entity.getComponent( - ComponentNames.Moment - ).inertia; - - // F_g = mg, applied only until terminal velocity is reached - if (entity.hasComponent(ComponentNames.Gravity)) { - const gravity = entity.getComponent(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(ComponentNames.Forces).forces = []; - - // maybe we fell off the floor - if (ddy > 0 && entity.hasComponent(ComponentNames.Jump)) { - entity.getComponent(ComponentNames.Jump).canJump = false; - } - }); - - componentEntities.get(ComponentNames.Velocity)?.forEach((entityId) => { - const entity = entityMap.get(entityId); - const velocity = entity.getComponent(ComponentNames.Velocity); - const boundingBox = entity.getComponent( - 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, - componentEntities: Map> - ) { - 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(ComponentNames.Sprite); - sprite.update(dt); - - let drawArgs: DrawArgs; - if (entity.hasComponent(ComponentNames.BoundingBox)) { - const boundingBox = entity.getComponent( - 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, - componentEntities: Map> - ): 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, - componentEntities: Map> - ) { - componentEntities.get(ComponentNames.WallBounded)?.forEach((entityId) => { - const entity = entityMap.get(entityId); - if (!entity.hasComponent(ComponentNames.BoundingBox)) { - return; - } - - const boundingBox = entity.getComponent( - 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"; -} diff --git a/client/lib/utils/dotProduct.ts b/client/lib/utils/dotProduct.ts deleted file mode 100644 index 59f8857..0000000 --- a/client/lib/utils/dotProduct.ts +++ /dev/null @@ -1,4 +0,0 @@ -import type { Coord2D } from "../interfaces"; - -export const dotProduct = (vector1: Coord2D, vector2: Coord2D): number => - vector1.x * vector2.x + vector1.y * vector2.y; diff --git a/client/lib/utils/index.ts b/client/lib/utils/index.ts deleted file mode 100644 index 1f8e2f0..0000000 --- a/client/lib/utils/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./rotateVector"; -export * from "./normalizeVector"; -export * from "./dotProduct"; diff --git a/client/lib/utils/normalizeVector.ts b/client/lib/utils/normalizeVector.ts deleted file mode 100644 index e6dfd7f..0000000 --- a/client/lib/utils/normalizeVector.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { Coord2D } from "../interfaces"; - -export const normalizeVector = (vector: Coord2D): Coord2D => { - const { x, y } = vector; - const length = Math.sqrt(x * x + y * y); - - return { x: x / length, y: y / length }; -}; diff --git a/client/lib/utils/rotateVector.ts b/client/lib/utils/rotateVector.ts deleted file mode 100644 index 82bb54d..0000000 --- a/client/lib/utils/rotateVector.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { Coord2D } from "../interfaces"; - -/** - * ([[cos(θ), -sin(θ),]) ([x,) - * ([sin(θ), cos(θ)] ]) ( y]) - */ -export const rotateVector = (vector: Coord2D, theta: number): Coord2D => { - const rads = (theta * Math.PI) / 180; - const [cos, sin] = [Math.cos(rads), Math.sin(rads)]; - - return { - x: vector.x * cos - vector.y * sin, - y: vector.x * sin + vector.y * cos, - }; -}; diff --git a/client/package-lock.json b/client/package-lock.json index 641f566..bf627d9 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -7,6 +7,9 @@ "": { "name": "client", "version": "0.0.0", + "dependencies": { + "module-alias": "^2.2.3" + }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^2.0.4", "@tsconfig/svelte": "^4.0.1", @@ -1585,6 +1588,11 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/module-alias": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.3.tgz", + "integrity": "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==" + }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", diff --git a/client/package.json b/client/package.json index 1ddd2b8..43c1b66 100644 --- a/client/package.json +++ b/client/package.json @@ -20,5 +20,8 @@ "tslib": "^2.5.0", "typescript": "^5.0.2", "vite": "^4.3.9" + }, + "dependencies": { + "module-alias": "^2.2.3" } } diff --git a/client/src/JumpStorm.ts b/client/src/JumpStorm.ts new file mode 100644 index 0000000..45ea163 --- /dev/null +++ b/client/src/JumpStorm.ts @@ -0,0 +1,54 @@ +import { Floor, Player } from "@engine/entities"; +import { Game } from "@engine/Game"; +import { + WallBounds, + FacingDirection, + Render, + Physics, + Input, + Collision, +} from "@engine/systems"; + +export class JumpStorm { + private game: Game; + + constructor(ctx: CanvasRenderingContext2D) { + this.game = new Game(); + + [ + this.createInputSystem(), + new FacingDirection(), + new Physics(), + new Collision(), + new WallBounds(ctx.canvas.width), + new Render(ctx), + ].forEach((system) => this.game.addSystem(system)); + + [new Floor(160), new Player()].forEach((entity) => + this.game.addEntity(entity) + ); + } + + public play() { + this.game.start(); + + const loop = (timestamp: number) => { + this.game.doGameLoop(timestamp); + requestAnimationFrame(loop); // tail call recursion! /s + }; + requestAnimationFrame(loop); + } + + private createInputSystem(): Input { + const inputSystem = new Input(); + + window.addEventListener("keydown", (e) => { + if (!e.repeat) { + inputSystem.keyPressed(e.key); + } + }); + window.addEventListener("keyup", (e) => inputSystem.keyReleased(e.key)); + + return inputSystem; + } +} diff --git a/client/src/components/GameCanvas.svelte b/client/src/components/GameCanvas.svelte index 766a08a..d7abecf 100644 --- a/client/src/components/GameCanvas.svelte +++ b/client/src/components/GameCanvas.svelte @@ -1,11 +1,8 @@