From 0d0cec0320be7c198c8a6ae97603fa98d816d14d Mon Sep 17 00:00:00 2001 From: Jo Date: Wed, 4 Feb 2026 01:40:36 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Implement=20updated=20Cosmic=20clas?= =?UTF-8?q?s=20and=20Loop=20concept?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 10 ++ .oxfmtrc.jsonc | 3 +- README.md | 39 ++++++ bun.lock | 10 -- package.json | 34 ++--- packages/core/package.json | 32 ++--- packages/core/src/core/Cosmic.ts | 124 +++++++++++++---- packages/core/src/core/Loop.ts | 23 ++++ packages/core/src/index.ts | 1 + packages/demo/index.html | 40 ++++-- packages/demo/package.json | 27 ++-- packages/demo/src/LoggingSystem.ts | 29 ---- packages/demo/src/index.ts | 119 ++-------------- packages/demo/styles/base.css | 74 ---------- packages/kit/package.json | 18 --- packages/kit/src/components.ts | 7 - packages/kit/src/components/Collider.ts | 3 - packages/kit/src/components/Movable.ts | 3 - packages/kit/src/components/Name.ts | 6 - packages/kit/src/components/Physics.ts | 3 - .../kit/src/components/PlayerControlled.ts | 9 -- packages/kit/src/components/Renderable.ts | 1 - packages/kit/src/components/Transform.ts | 12 -- packages/kit/src/systems.ts | 4 - packages/kit/src/systems/CollisionSystem.ts | 129 ------------------ packages/kit/src/systems/PhysicsSystem.ts | 23 ---- .../kit/src/systems/PlayerControlSystem.ts | 28 ---- packages/kit/src/systems/RenderingSystem.ts | 26 ---- 28 files changed, 255 insertions(+), 582 deletions(-) create mode 100644 .editorconfig create mode 100644 README.md create mode 100644 packages/core/src/core/Loop.ts delete mode 100644 packages/demo/src/LoggingSystem.ts delete mode 100644 packages/demo/styles/base.css delete mode 100644 packages/kit/package.json delete mode 100644 packages/kit/src/components.ts delete mode 100644 packages/kit/src/components/Collider.ts delete mode 100644 packages/kit/src/components/Movable.ts delete mode 100644 packages/kit/src/components/Name.ts delete mode 100644 packages/kit/src/components/Physics.ts delete mode 100644 packages/kit/src/components/PlayerControlled.ts delete mode 100644 packages/kit/src/components/Renderable.ts delete mode 100644 packages/kit/src/components/Transform.ts delete mode 100644 packages/kit/src/systems.ts delete mode 100644 packages/kit/src/systems/CollisionSystem.ts delete mode 100644 packages/kit/src/systems/PhysicsSystem.ts delete mode 100644 packages/kit/src/systems/PlayerControlSystem.ts delete mode 100644 packages/kit/src/systems/RenderingSystem.ts diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f5764e4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + +[*.{js,ts,html}] +indent_style = space +indent_size = 4 diff --git a/.oxfmtrc.jsonc b/.oxfmtrc.jsonc index 2ab3a6e..f38a424 100644 --- a/.oxfmtrc.jsonc +++ b/.oxfmtrc.jsonc @@ -1,3 +1,4 @@ { - "$schema": "./node_modules/oxfmt/configuration_schema.json", + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "tabWidth": 4, } diff --git a/README.md b/README.md new file mode 100644 index 0000000..51894c9 --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ +# Cosmic +The experimental pixi.js based engine for tile-based 2D games, created strictly for educational purposes. + +## Requirements +To run the demo project and experiment with the engine, the following tools are required. + +- Bun v1.3+ + +## Try it out! +You can try out the ever evolving demo project at any time, by running the following commands. + +```sh +bun install +``` + +```sh +bun dev +``` + +## Architecture +The following will explain the data-flow and general architecture of the engine, including the main class, the ECS and more! + +### The `Cosmic` class + +### The `Loop` class +Loops are singular, scoped and enqueue-able looping directives. + +For example, there might be a "rendering" loop, which draws the state of various entities onto the screen, on each frame, using drawing APIs. + +These are enqueued separately, rather than having one hard-coded main loop, to allow varying execution speeds, like a rendering pipeline running on every frame, and a physics pipeline running at a calculated interval of 60 FPS. + +### The `Layer` class + +### The `EntityComponentSystem` class + +### The `Store` class +Stores are utilized as a means of shared state throughout the engine. + +They are defined as static DTO-like (data transfer object) classes that extend the `Store` class. diff --git a/bun.lock b/bun.lock index 1dfc429..b77bb15 100644 --- a/bun.lock +++ b/bun.lock @@ -16,25 +16,15 @@ "version": "0.1.0", "dependencies": { "@cosmic/core": "workspace:*", - "@cosmic/kit": "workspace:*", "vite": "^7.2.4", }, }, - "packages/kit": { - "name": "@cosmic/kit", - "version": "0.1.0", - "devDependencies": { - "@cosmic/core": "workspace:*", - }, - }, }, "packages": { "@cosmic/core": ["@cosmic/core@workspace:packages/core"], "@cosmic/demo": ["@cosmic/demo@workspace:packages/demo"], - "@cosmic/kit": ["@cosmic/kit@workspace:packages/kit"], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], diff --git a/package.json b/package.json index 9e5e3d2..60deaf5 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,20 @@ { - "private": true, - "workspaces": [ - "packages/*" - ], - "contributors": [ - { - "name": "Johannes Reckers", - "email": "contact@thevoid.cafe", - "url": "https://thevoid.cafe" + "private": true, + "contributors": [ + { + "name": "Johannes Reckers", + "email": "contact@thevoid.cafe", + "url": "https://thevoid.cafe" + } + ], + "workspaces": [ + "packages/*" + ], + "scripts": { + "dev": "cd packages/demo && bun dev", + "format": "oxfmt" + }, + "devDependencies": { + "oxfmt": "^0.16.0" } - ], - "scripts": { - "dev": "cd packages/demo && bun dev", - "format": "oxfmt" - }, - "devDependencies": { - "oxfmt": "^0.16.0" - } } diff --git a/packages/core/package.json b/packages/core/package.json index db40b9c..fe3189d 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,19 +1,19 @@ { - "name": "@cosmic/core", - "version": "0.1.0", - "description": "Experimental Pixi.js based engine for tile-based 2D games, created strictly for educational purposes.", - "module": "src/index.ts", - "contributors": [ - { - "name": "Johannes Reckers", - "email": "contact@thevoid.cafe", - "url": "https://thevoid.cafe" + "name": "@cosmic/core", + "version": "0.1.0", + "description": "Experimental Pixi.js based engine for tile-based 2D games, created strictly for educational purposes.", + "contributors": [ + { + "name": "Johannes Reckers", + "email": "contact@thevoid.cafe", + "url": "https://thevoid.cafe" + } + ], + "module": "src/index.ts", + "exports": { + ".": { + "import": "./src/index.ts", + "require": "./src/index.cjs" + } } - ], - "exports": { - ".": { - "import": "./src/index.ts", - "require": "./src/index.cjs" - } - } } diff --git a/packages/core/src/core/Cosmic.ts b/packages/core/src/core/Cosmic.ts index 68a577e..0ddc940 100644 --- a/packages/core/src/core/Cosmic.ts +++ b/packages/core/src/core/Cosmic.ts @@ -1,42 +1,108 @@ -import { System, Entity, InputManager } from "@cosmic/core"; +import { Loop } from "@cosmic/core"; /** * Enum representing the different modes in which the Cosmic engine can operate. */ export enum CosmicMode { - /** Enables debugging features and logs additional information. */ - DEVELOPMENT, + /** Enables debugging features and logs additional information. */ + DEVELOPMENT, - /** Enables production mode, optimized for high performance. */ - PRODUCTION, + /** Enables production mode, optimized for high performance. */ + PRODUCTION, +} + +/** + * Enum representing the different states in which the Cosmic engine can operate. + */ +export enum CosmicState { + /** Indicates the main engine pulse to be active. */ + ACTIVE, + + /** Indicates the main engine pulse is not currently active. */ + INACTIVE, } /** * The Cosmic engine, a powerful tool for tile-based 2D games. + * Accessed through the `Cosmic.instance` property. + * + * @example + * const engine = Cosmic.instance; + * + * engine.enqueue(new Loop("rendering", (dt) => { + * console.log(`Delta time: ${dt}`); + * })); */ export class Cosmic { - /** - * The engine's current, and only, instance. - */ - public _instance?: Cosmic; - - /** - * Returns a reference to the current engine instance. - */ - get instance() { - if (!this._instance) { - this._instance = new Cosmic(CosmicMode.PRODUCTION); - } - - return this._instance; - } - - /** - * The mode in which the engine is operating. - */ - public mode: CosmicMode; - - public constructor(mode: CosmicMode) { - this.mode = mode; - } + protected static _instance?: Cosmic; + public static get instance() { + if (!this._instance) { + this._instance = new Cosmic(CosmicMode.PRODUCTION); + } + + return this._instance; + } + + public constructor( + /** The mode in which the engine is operating. */ + public mode: CosmicMode, + + /** Declares whether the engine is currently running. */ + protected state: CosmicState = CosmicState.INACTIVE, + ) { + requestAnimationFrame(this.pulse.bind(this)); + } + + /** The last recorded frame timestamp. */ + protected lastTimestamp: number = performance.now(); + + /** The current calculated deltatime in seconds */ + protected deltaTime: number = 0; + + /** Stores the engine's enqueued external loops */ + protected loops: Loop[] = []; + + /** + * Appends a `Loop` to the engine's main pulse. + * + * @param {Loop} loop - The loop to enqueue. + */ + public enqueue(loop: Loop) { + if (!loop.name) throw new Error("[Cosmic::enqueue] The provided loop is missing the `name` property"); + + if (!loop.pulse) throw new Error("[Cosmic::enqueue] The provided loop is missing a `pulse` method"); + + this.loops.push(loop); + } + + /** Initializes the main engine pulse. */ + public start() { + if (this.state === CosmicState.ACTIVE) return; + + this.state = CosmicState.ACTIVE; + } + + /** Stops the main engine pulse. */ + public stop() { + if (this.state === CosmicState.INACTIVE) return; + + this.state = CosmicState.INACTIVE; + } + + /** The main engine pulse, hooked to requestAnimationFrame. Executes all enqueued loops once per frame, and calculates new deltaTime in seconds. */ + protected pulse(timestamp: number) { + this.deltaTime = (timestamp - this.lastTimestamp) / 1000; + this.lastTimestamp = timestamp; + + for (const loop of this.loops) { + if (this.mode === CosmicMode.DEVELOPMENT) { + console.log(`Running enqueued loop: ${loop.name}`); + } + + loop.pulse(this.deltaTime); + } + + // Move on to the next frame + requestAnimationFrame(this.pulse.bind(this)); + } } diff --git a/packages/core/src/core/Loop.ts b/packages/core/src/core/Loop.ts new file mode 100644 index 0000000..67a7b20 --- /dev/null +++ b/packages/core/src/core/Loop.ts @@ -0,0 +1,23 @@ +/** + * Defines a singular scoped loop, which may be enqueued in the engine's main loop. + * + * @param {string} name - The unique name for this loop, e.g. "rendering" or "entity-component-system". + * + * @example + * const renderingLoop = new Loop("rendering", (dt: number) => { + * console.log(`Delta time: ${dt}`); + * }); + */ +export class Loop { + public constructor( + /** The unique name for this loop, e.g. "rendering" or "entity-component-system". */ + public name: string, + + /** + * The loop's pulse, called by the engine on requestAnimationFrame. + * + * @param {number} dt - The delta time value passed on from the main engine loop. + */ + public pulse: (dt: number) => void + ) { } +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index e9cfe43..4e1c182 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1 +1,2 @@ export * from "./core/Cosmic"; +export * from "./core/Loop"; diff --git a/packages/demo/index.html b/packages/demo/index.html index 79e815b..71159e8 100644 --- a/packages/demo/index.html +++ b/packages/demo/index.html @@ -1,14 +1,30 @@ - + - - - - - Cosmic Demo - - - - - - + + + + + Cosmic Demo + + + + + + + diff --git a/packages/demo/package.json b/packages/demo/package.json index 137d6cb..ee2c4fe 100644 --- a/packages/demo/package.json +++ b/packages/demo/package.json @@ -1,16 +1,15 @@ { - "name": "@cosmic/demo", - "version": "0.1.0", - "description": "Simple demo of the cosmic engine.", - "module": "src/index.ts", - "scripts": { - "dev": "vite", - "build": "vite build", - "preview": "vite preview" - }, - "dependencies": { - "vite": "^7.2.4", - "@cosmic/core": "workspace:*", - "@cosmic/kit": "workspace:*" - } + "name": "@cosmic/demo", + "version": "0.1.0", + "description": "Simple demo of the cosmic engine.", + "module": "src/index.ts", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@cosmic/core": "workspace:*", + "vite": "^7.2.4" + } } diff --git a/packages/demo/src/LoggingSystem.ts b/packages/demo/src/LoggingSystem.ts deleted file mode 100644 index f3fd6f7..0000000 --- a/packages/demo/src/LoggingSystem.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Cosmic, Entity, System } from "@cosmic/core"; -import { Name } from "@cosmic/kit/components"; - -export class LoggingSystem extends System { - private worldStore: Map; - - constructor(engine: Cosmic) { - super(); - - this.worldStore = engine.getStore("cosmic.world"); - } - - public readonly requiredComponents = new Set([Name.name]); - - public update(entities: Entity[], _deltaTime: number) { - for (const entity of entities) { - console.log(` - ==================== Entity ==================== - ID: ${entity.id} - Name: '${entity.getComponent(Name)?.name}' - Description: '${entity.getComponent(Name)?.description}' - - =============== World Properties =============== - Gravitational Strength: ${this.worldStore.get("cosmic.world.gravity.strength") ?? 0} - Cell Size: ${this.worldStore.get("cosmic.world.grid.cellSize") ?? 0} - `); - } - } -} diff --git a/packages/demo/src/index.ts b/packages/demo/src/index.ts index 2d22375..a38eb57 100644 --- a/packages/demo/src/index.ts +++ b/packages/demo/src/index.ts @@ -1,112 +1,15 @@ -import { Cosmic, CosmicMode, Entity } from "@cosmic/core"; -import { - Transform, - Collider, - Renderable, - Physics, - PlayerControlled, - Movable, - Name, -} from "@cosmic/kit/components"; -import { - RenderingSystem, - PhysicsSystem, - CollisionSystem, - PlayerControlSystem, -} from "@cosmic/kit/systems"; -import { LoggingSystem } from "./LoggingSystem"; +import { Cosmic, Loop } from "@cosmic/core"; -//====================================== -// Initialize engine and systems -//====================================== -const engine = Cosmic.getInstance(CosmicMode.DEVELOPMENT); +console.log("Hiii world :3"); -engine.addSystem(new PlayerControlSystem(engine)); -engine.addSystem(new PhysicsSystem(engine)); -engine.addSystem(new CollisionSystem()); -engine.addSystem(new RenderingSystem(engine)); -engine.addSystem(new LoggingSystem(engine)); +const cosmicDeltaWrapper = document.createElement("p"); +const cosmicDeltaDisplay = document.createElement("span"); -engine.start(); +cosmicDeltaWrapper.innerText = "The current delta time is... " +cosmicDeltaWrapper.appendChild(cosmicDeltaDisplay); +document.body.appendChild(cosmicDeltaWrapper); -//====================================== -// Initialize crucial stores -//====================================== -const worldStore = engine.getStore("cosmic.world"); -worldStore.set("cosmic.world.gravity.strength", 2); -worldStore.set("cosmic.world.grid.cellSize", 32); - -//====================================== -// EXPERIMENTATION AREA -//====================================== -const createTestEntity = (options: { - initialX: number; - initialY: number; - isStatic: boolean; - inputs: string[]; -}) => { - const worldGridCellSize = worldStore.get("cosmic.world.grid.cellSize"); - - const entity = new Entity(); - entity.addComponent( - new Transform(options.initialX, options.initialY, worldGridCellSize * 2, worldGridCellSize * 2), - ); - entity.addComponent(new PlayerControlled(500, ...options.inputs)); - entity.addComponent(new Renderable()); - entity.addComponent(new Movable()); - entity.addComponent(new Physics()); - entity.addComponent(new Name("player", "This is a player controlled entity.")); - entity.addComponent(new Collider(options.isStatic)); - - return entity; -}; - -engine.addEntity( - createTestEntity({ - initialX: 0, - initialY: 0, - isStatic: false, - inputs: ["w", "s", "a", "d"], - }), -); -engine.addEntity( - createTestEntity({ - initialX: 30, - initialY: 200, - isStatic: false, - inputs: ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"], - }), -); - -// Create a static object entity, e.g. a world tile. -const createObjectEntity = (options: { initialX: number; initialY: number }) => { - const worldGridCellSize = worldStore.get("cosmic.world.grid.cellSize"); - - const entity = new Entity(); - entity.addComponent( - new Transform(options.initialX, options.initialY, worldGridCellSize, worldGridCellSize), - ); - entity.addComponent(new Renderable()); - entity.addComponent(new Movable()); - entity.addComponent(new Physics(0)); - entity.addComponent(new Name("object", "This is a static object entity.")); - entity.addComponent(new Collider(true)); - - return entity; -}; - -engine.addEntity( - createObjectEntity({ - initialX: 0, - initialY: 900, - }), -); - -for (let i = 0; i < 70; i++) { - engine.addEntity( - createObjectEntity({ - initialX: i * worldStore.get("cosmic.world.grid.cellSize"), - initialY: 900, - }), - ); -} +const engine = Cosmic.instance; +engine.enqueue(new Loop("rendering", (dt) => { + cosmicDeltaDisplay.innerText = dt.toString(); +})); diff --git a/packages/demo/styles/base.css b/packages/demo/styles/base.css deleted file mode 100644 index b2fd254..0000000 --- a/packages/demo/styles/base.css +++ /dev/null @@ -1,74 +0,0 @@ -* { - margin: 0; - padding: 0; - position: relative; - box-sizing: border-box; -} - -:root { - --color-bg0: #10131B; - --color-bg1: #13161e; - --color-bg2: #242e34; - --color-bg-gradient: linear-gradient(to bottom, var(--color-bg1), var(--color-primary)); - - --color-text: #DFE5E8; - - --color-primary: #70b9ae; - --color-secondary: #bf1d70; - --color-success: var(--color-primary); - --color-danger: var(--color-secondary); - - --color-border: rgba(200, 220, 229, 0.15); - - --color-shadow: rgba(0, 0, 0, 0.3); - - --color-overlay-glow: var(--color-primary); -} - -* { - padding: 0; - margin: 0; - box-sizing: border-box; - position: relative; -} - -body { - width: 100vw; - height: 100vh; - - overflow: hidden; - - background: var(--color-bg0); - - &::after { - content: ""; - - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - - opacity: 0.05; - background-image: linear-gradient(rgba(0, 255, 65, 0.5) 1px, - transparent 1px), - linear-gradient(90deg, rgba(0, 255, 65, 0.5) 1px, transparent 1px); - background-size: 32px 32px; - - pointer-events: none; - z-index: 1; - } -} - -.cosmic-layer { - position: absolute; - top: 0; - left: 0; - - width: 100%; - height: 100%; -} - -.cosmic-gui { - z-index: 1; -} diff --git a/packages/kit/package.json b/packages/kit/package.json deleted file mode 100644 index 5861d61..0000000 --- a/packages/kit/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "@cosmic/kit", - "version": "0.1.0", - "description": "Collection of components, systems and helpers to aid developers of games using the Cosmic engine.", - "exports": { - "./components": { - "import": "./src/components.ts", - "require": "./src/components.cjs" - }, - "./systems": { - "import": "./src/systems.ts", - "require": "./src/systems.cjs" - } - }, - "devDependencies": { - "@cosmic/core": "workspace:*" - } -} diff --git a/packages/kit/src/components.ts b/packages/kit/src/components.ts deleted file mode 100644 index 836d709..0000000 --- a/packages/kit/src/components.ts +++ /dev/null @@ -1,7 +0,0 @@ -export * from "./components/PlayerControlled"; -export * from "./components/Renderable"; -export * from "./components/Transform"; -export * from "./components/Collider"; -export * from "./components/Physics"; -export * from "./components/Movable"; -export * from "./components/Name"; diff --git a/packages/kit/src/components/Collider.ts b/packages/kit/src/components/Collider.ts deleted file mode 100644 index 269b316..0000000 --- a/packages/kit/src/components/Collider.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class Collider { - constructor(public isStatic: boolean = false) {} -} diff --git a/packages/kit/src/components/Movable.ts b/packages/kit/src/components/Movable.ts deleted file mode 100644 index ecc420f..0000000 --- a/packages/kit/src/components/Movable.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class Movable { - constructor(public speed: number = 500) {} -} diff --git a/packages/kit/src/components/Name.ts b/packages/kit/src/components/Name.ts deleted file mode 100644 index 4e9899e..0000000 --- a/packages/kit/src/components/Name.ts +++ /dev/null @@ -1,6 +0,0 @@ -export class Name { - constructor( - public name: string, - public description: string, - ) {} -} diff --git a/packages/kit/src/components/Physics.ts b/packages/kit/src/components/Physics.ts deleted file mode 100644 index 3d6cdfa..0000000 --- a/packages/kit/src/components/Physics.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class Physics { - constructor(public weight: number = 120) {} -} diff --git a/packages/kit/src/components/PlayerControlled.ts b/packages/kit/src/components/PlayerControlled.ts deleted file mode 100644 index 0a0d60a..0000000 --- a/packages/kit/src/components/PlayerControlled.ts +++ /dev/null @@ -1,9 +0,0 @@ -export class PlayerControlled { - constructor( - public speed = 500, - public inputUp = "w", - public inputDown = "s", - public inputLeft = "a", - public inputRight = "d", - ) {} -} diff --git a/packages/kit/src/components/Renderable.ts b/packages/kit/src/components/Renderable.ts deleted file mode 100644 index db71ecd..0000000 --- a/packages/kit/src/components/Renderable.ts +++ /dev/null @@ -1 +0,0 @@ -export class Renderable {} diff --git a/packages/kit/src/components/Transform.ts b/packages/kit/src/components/Transform.ts deleted file mode 100644 index 12caecd..0000000 --- a/packages/kit/src/components/Transform.ts +++ /dev/null @@ -1,12 +0,0 @@ -export class Transform { - constructor( - public x: number = 0, - public y: number = 0, - public width: number = 32, - public height: number = 32, - - // Todo: Consider moving this to Collider.ts - public isCollidingWithEntity: boolean = false, - public isCollidingWithCanvasBoundaries: boolean = false, - ) {} -} diff --git a/packages/kit/src/systems.ts b/packages/kit/src/systems.ts deleted file mode 100644 index 798143d..0000000 --- a/packages/kit/src/systems.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from "./systems/PlayerControlSystem"; -export * from "./systems/CollisionSystem"; -export * from "./systems/RenderingSystem"; -export * from "./systems/PhysicsSystem"; diff --git a/packages/kit/src/systems/CollisionSystem.ts b/packages/kit/src/systems/CollisionSystem.ts deleted file mode 100644 index d8af6a7..0000000 --- a/packages/kit/src/systems/CollisionSystem.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { Entity, System } from "@cosmic/core"; -import { Transform, Collider } from "@cosmic/kit/components"; - -export class CollisionSystem extends System { - public readonly requiredComponents = new Set([Transform.name, Collider.name]); - - public update(entities: Entity[], _deltaTime: number) { - // Reset collision flag for all first - for (const entity of entities) { - const transform = entity.getComponent(Transform)!; - transform.isCollidingWithEntity = false; - } - - // Check each pair once - for (let i = 0; i < entities.length; i++) { - const entityA = entities[i]; - const aTransform = entityA.getComponent(Transform)!; - const aCollider = entityA.getComponent(Collider)!; - - for (let j = i + 1; j < entities.length; j++) { - const entityB = entities[j]; - const bTransform = entityB.getComponent(Transform)!; - const bCollider = entityB.getComponent(Collider)!; - - if (this.areColliding(aTransform, bTransform)) { - aTransform.isCollidingWithEntity = true; - bTransform.isCollidingWithEntity = true; - - this.resolveOverlap(aTransform, bTransform, aCollider, bCollider); - } - } - } - } - - protected areColliding(a: Transform, b: Transform): boolean { - // AABB vs AABB - return ( - a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y - ); - } - - protected resolveOverlap(a: Transform, b: Transform, colA: Collider, colB: Collider) { - // Centers - const centerAx = a.x + a.width / 2; - const centerAy = a.y + a.height / 2; - const centerBx = b.x + b.width / 2; - const centerBy = b.y + b.height / 2; - - const dx = centerAx - centerBx; - const dy = centerAy - centerBy; - - const combinedHalfWidths = (a.width + b.width) / 2; - const combinedHalfHeights = (a.height + b.height) / 2; - - const overlapX = combinedHalfWidths - Math.abs(dx); - const overlapY = combinedHalfHeights - Math.abs(dy); - - if (overlapX <= 0 || overlapY <= 0) return; // just in case - - const aStatic = colA.isStatic; - const bStatic = colB.isStatic; - - // Decide which axis to resolve along (least penetration) - if (overlapX < overlapY) { - // Horizontal - if (dx > 0) { - // A is to the right of B - this.pushApartX(a, b, overlapX, +1, aStatic, bStatic); - } else { - // A is to the left of B - this.pushApartX(a, b, overlapX, -1, aStatic, bStatic); - } - } else { - // Vertical - if (dy > 0) { - // A is below B - this.pushApartY(a, b, overlapY, +1, aStatic, bStatic); - } else { - // A is above B - this.pushApartY(a, b, overlapY, -1, aStatic, bStatic); - } - } - } - - private pushApartX( - a: Transform, - b: Transform, - overlap: number, - direction: 1 | -1, // +1: A right of B, -1: A left of B - aStatic: boolean, - bStatic: boolean, - ) { - if (aStatic && bStatic) return; - - if (aStatic && !bStatic) { - // Only move B - b.x -= direction * overlap; - } else if (!aStatic && bStatic) { - // Only move A - a.x += direction * overlap; - } else { - // Both dynamic → split - const half = overlap / 2; - a.x += direction * half; - b.x -= direction * half; - } - } - - private pushApartY( - a: Transform, - b: Transform, - overlap: number, - direction: 1 | -1, // +1: A below B, -1: A above B - aStatic: boolean, - bStatic: boolean, - ) { - if (aStatic && bStatic) return; - - if (aStatic && !bStatic) { - b.y -= direction * overlap; - } else if (!aStatic && bStatic) { - a.y += direction * overlap; - } else { - const half = overlap / 2; - a.y += direction * half; - b.y -= direction * half; - } - } -} diff --git a/packages/kit/src/systems/PhysicsSystem.ts b/packages/kit/src/systems/PhysicsSystem.ts deleted file mode 100644 index 52ed80a..0000000 --- a/packages/kit/src/systems/PhysicsSystem.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Entity, System, type Cosmic } from "@cosmic/core"; -import { Transform, Physics } from "@cosmic/kit/components"; - -export class PhysicsSystem extends System { - public requiredComponents = new Set([Transform.name, Physics.name]); - private worldStore: Map; - - constructor(engine: Cosmic) { - super(); - this.worldStore = engine.getStore("cosmic.world"); - } - - update(entities: Entity[], deltaTime: number) { - const worldGravityStrength = this.worldStore.get("cosmic.world.gravity.strength") ?? 0; - - entities.forEach((entity) => { - const transform = entity.getComponent(Transform)!; - const physics = entity.getComponent(Physics)!; - - transform.y += worldGravityStrength * physics.weight * deltaTime; - }); - } -} diff --git a/packages/kit/src/systems/PlayerControlSystem.ts b/packages/kit/src/systems/PlayerControlSystem.ts deleted file mode 100644 index 9d36d96..0000000 --- a/packages/kit/src/systems/PlayerControlSystem.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Cosmic, System, Entity } from "@cosmic/core"; -import { Transform, PlayerControlled } from "@cosmic/kit/components"; - -export class PlayerControlSystem extends System { - public requiredComponents = new Set([Transform.name, PlayerControlled.name]); - - constructor(private engine: Cosmic) { - super(); - } - - update(entities: Entity[], deltaTime: number): void { - const input = this.engine.input; - - entities.forEach((entity) => { - const transform = entity.getComponent(Transform)!; - const playerControlled = entity.getComponent(PlayerControlled)!; - - if (input.isKeyDown(playerControlled.inputUp)) - transform.y -= playerControlled.speed * deltaTime; - if (input.isKeyDown(playerControlled.inputDown)) - transform.y += playerControlled.speed * deltaTime; - if (input.isKeyDown(playerControlled.inputLeft)) - transform.x -= playerControlled.speed * deltaTime; - if (input.isKeyDown(playerControlled.inputRight)) - transform.x += playerControlled.speed * deltaTime; - }); - } -} diff --git a/packages/kit/src/systems/RenderingSystem.ts b/packages/kit/src/systems/RenderingSystem.ts deleted file mode 100644 index ee21ce9..0000000 --- a/packages/kit/src/systems/RenderingSystem.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Entity, System, type Cosmic } from "@cosmic/core"; -import { Transform, Renderable } from "@cosmic/kit/components"; - -export class RenderingSystem extends System { - public requiredComponents = new Set([Transform.name, Renderable.name]); - private context: CanvasRenderingContext2D; - - constructor(engine: Cosmic) { - super(); - this.context = engine.getContext(); - } - - public update(entities: Entity[]): void { - this.context.clearRect(0, 0, 9999, 9999); - - entities.forEach((entity) => { - const transform = entity.getComponent(Transform)!; - - this.context.strokeStyle = "#bf1d70"; - if (transform.isCollidingWithEntity) this.context.strokeStyle = "#d6a5b0"; - if (transform.isCollidingWithCanvasBoundaries) this.context.strokeStyle = "purple"; - - this.context.strokeRect(transform.x, transform.y, transform.width, transform.height); - }); - } -} -- 2.51.2