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);
- });
- }
-}