From 1c28e10b860056d85cc07e5a834c4a54eac14563 Mon Sep 17 00:00:00 2001 From: Lizzy Hunt Date: Wed, 16 Aug 2023 15:41:35 -0600 Subject: [PATCH] refactor collision methods, rescaffold server --- engine/structures/QuadTree.ts | 10 ++++- engine/systems/Collision.ts | 76 +++++++++++++++++++++++++---------- server/src/server.ts | 66 +++++++++++++----------------- server/tsconfig.json | 2 - 4 files changed, 91 insertions(+), 63 deletions(-) diff --git a/engine/structures/QuadTree.ts b/engine/structures/QuadTree.ts index 49afdad..e6e29fa 100644 --- a/engine/structures/QuadTree.ts +++ b/engine/structures/QuadTree.ts @@ -1,6 +1,6 @@ import type { Coord2D, Dimension2D } from "../interfaces"; -interface BoxedEntry { +export interface BoxedEntry { id: string; dimension: Dimension2D; center: Coord2D; @@ -170,4 +170,12 @@ export class QuadTree { private hasChildren() { return this.children && this.children.size > 0; } + + public setTopLeft(topLeft: Coord2D) { + this.topLeft = topLeft; + } + + public setDimension(dimension: Dimension2D) { + this.dimension = dimension; + } } diff --git a/engine/systems/Collision.ts b/engine/systems/Collision.ts index 889f85e..e05aba0 100644 --- a/engine/systems/Collision.ts +++ b/engine/systems/Collision.ts @@ -10,8 +10,8 @@ import { import { Game } from "../Game"; import { PhysicsConstants } from "../config"; import { Entity } from "../entities"; -import type { Dimension2D, Velocity2D } from "../interfaces"; -import { QuadTree } from "../structures"; +import type { Coord2D, Dimension2D, Velocity2D } from "../interfaces"; +import { QuadTree, BoxedEntry } from "../structures"; export class Collision extends System { private static readonly COLLIDABLE_COMPONENT_NAMES = [ @@ -41,19 +41,26 @@ export class Collision extends System { const entitiesToAddToQuadtree: Entity[] = []; Collision.COLLIDABLE_COMPONENT_NAMES.map((componentName) => - game.componentEntities.get(componentName), - ).forEach( - (entityIds?: Set) => - entityIds?.forEach((id) => { - const entity = game.entities.get(id); - if (!entity || !entity.hasComponent(ComponentNames.BoundingBox)) { - return; - } - entitiesToAddToQuadtree.push(entity); - }), + game.forEachEntityWithComponent(componentName, (entity) => { + if (!entity.hasComponent(ComponentNames.BoundingBox)) { + return; + } + entitiesToAddToQuadtree.push(entity); + }), ); - entitiesToAddToQuadtree.forEach((entity) => { + this.insertEntitiesInQuadTreeAndUpdateBounds(entitiesToAddToQuadtree); + + this.findCollidingEntitiesAndCollide(entitiesToAddToQuadtree, game); + } + + private insertEntitiesInQuadTreeAndUpdateBounds(entities: Entity[]) { + const topLeft: Coord2D = { x: Infinity, y: Infinity }; + const bottomRight: Coord2D = { x: -Infinity, y: -Infinity }; + + const quadTreeInsertions: BoxedEntry[] = []; + + entities.forEach((entity) => { const boundingBox = entity.getComponent( ComponentNames.BoundingBox, ); @@ -63,18 +70,45 @@ export class Collision extends System { dimension = boundingBox.getOutscribedBoxDims(); } - this.quadTree.insert({ + const { center } = boundingBox; + const topLeftBoundingBox = { + x: center.x - dimension.width / 2, + y: center.y - dimension.height / 2, + }; + const bottomRightBoundingBox = { + x: center.x + dimension.width / 2, + y: center.y + dimension.height / 2, + }; + + topLeft.x = Math.min(topLeftBoundingBox.x, topLeft.x); + topLeft.y = Math.min(topLeftBoundingBox.y, topLeft.y); + bottomRight.x = Math.max(bottomRightBoundingBox.x, bottomRight.x); + bottomRight.y = Math.min(bottomRightBoundingBox.y, bottomRight.y); + + quadTreeInsertions.push({ id: entity.id, dimension, - center: boundingBox.center, + center, }); }); - // find colliding entities and perform collisions - const collidingEntities = this.getCollidingEntities( - entitiesToAddToQuadtree, - game, + // set bounds first + if (entities.length > 0) { + this.quadTree.setTopLeft(topLeft); + this.quadTree.setDimension({ + width: bottomRight.x - topLeft.x, + height: bottomRight.y - topLeft.y, + }); + } + + // then, begin insertions + quadTreeInsertions.forEach((boxedEntry: BoxedEntry) => + this.quadTree.insert(boxedEntry), ); + } + + private findCollidingEntitiesAndCollide(entities: Entity[], game: Game) { + const collidingEntities = this.getCollidingEntities(entities, game); collidingEntities.forEach(([entityAId, entityBId]) => { const [entityA, entityB] = [entityAId, entityBId].map((id) => @@ -139,8 +173,8 @@ export class Collision extends System { private getCollidingEntities( collidableEntities: Entity[], game: Game, - ): [number, number][] { - const collidingEntityIds: [number, number][] = []; + ): [string, string][] { + const collidingEntityIds: [string, string][] = []; for (const entity of collidableEntities) { const boundingBox = entity.getComponent( diff --git a/server/src/server.ts b/server/src/server.ts index 9a73f11..d169f7d 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -11,50 +11,38 @@ import { Miscellaneous } from "../../engine/config"; const TICK_RATE = 60 / 1000; -class Server { - private server: any; - private game: Game; +const game = new Game(); - constructor() { - this.game = new Game(); +[new Physics(), new Collision(), new WallBounds(Miscellaneous.WIDTH)].forEach( + (system) => game.addSystem(system), +); - [ - new Physics(), - new Collision({ - width: Miscellaneous.WIDTH, - height: Miscellaneous.HEIGHT, - }), - new WallBounds(Miscellaneous.WIDTH), - ].forEach((system) => this.game.addSystem(system)); +[new Floor(160), new Player()].forEach((entity) => game.addEntity(entity)); - [new Floor(160), new Player()].forEach((entity) => - this.game.addEntity(entity), - ); +game.start(); - this.game.start(); - setInterval(() => { - this.game.doGameLoop(performance.now()); - }, TICK_RATE); +setInterval(() => { + game.doGameLoop(performance.now()); +}, TICK_RATE); - this.server = Bun.serve({ - websocket: { - open(ws) { - ws.subscribe("the-group-chat"); - ws.publish("the-group-chat", msg); - }, - message(ws, message) { - // this is a group chat - // so the server re-broadcasts incoming message to everyone - ws.publish("the-group-chat", `${ws.data.username}: ${message}`); - }, - close(ws) { - const msg = `${ws.data.username} has left the chat`; - ws.unsubscribe("the-group-chat"); - ws.publish("the-group-chat", msg); - }, +const server = Bun.serve({ + port: 8080, + fetch(req, server) { + const sessionId = Math.floor(Math.random() * 1e10).toString(); + + server.upgrade(req, { + headers: { + "Set-Cookie": `SessionId=${sessionId}`, }, }); - } -} + }, + websocket: { + open(ws) {}, + message(ws, message) { + console.log(message); + }, + close(ws) {}, + }, +}); -new Server(); +console.log(`Listening on ${server.hostname}:${server.port}`); diff --git a/server/tsconfig.json b/server/tsconfig.json index 2567512..e39b364 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -13,8 +13,6 @@ "noEmit": true, "allowImportingTsExtensions": true, "moduleDetection": "force", - // if TS 4.x or earlier - "moduleResolution": "nodenext", "jsx": "react-jsx", // support JSX "allowJs": true, // allow importing `.js` from `.ts`