Experimental canvas 2D engine for tile-based sidescroller/sandbox games, created strictly for educational purposes.
entity-component-system game-engine canvas-2d

✨ Implement new LoggingSystem and add demo usage of stores

Jo 5904307b 397800d3

+81 -47
+1 -5
packages/core/src/core/EntityComponentSystem.ts
··· 1 1 export type Component = { [key: string]: any }; 2 2 3 3 export class Entity { 4 - public id: string; 4 + public id: string = crypto.randomUUID(); 5 5 public components = new Map<string, Component>(); 6 - 7 - constructor(id: string) { 8 - this.id = id; 9 - } 10 6 11 7 addComponent(component: Component): void { 12 8 const componentName = component.constructor.name;
+31
packages/demo/src/LoggingSystem.ts
··· 1 + import { Cosmic, Entity, System } from "@cosmic/core"; 2 + import { Name } from "@cosmic/kit/components"; 3 + 4 + export class LoggingSystem extends System { 5 + private worldStore: Map<string, any>; 6 + 7 + constructor(engine: Cosmic) { 8 + super(); 9 + 10 + this.worldStore = engine.getStore("cosmic.world"); 11 + } 12 + 13 + public readonly requiredComponents = new Set([ 14 + Name.name, 15 + ]); 16 + 17 + public update(entities: Entity[], _deltaTime: number) { 18 + for (const entity of entities) { 19 + console.log(` 20 + ==================== Entity ==================== 21 + ID: ${entity.id} 22 + Name: '${entity.getComponent(Name)?.name}' 23 + Description: '${entity.getComponent(Name)?.description}' 24 + 25 + =============== World Properties =============== 26 + Gravitational Strength: ${this.worldStore.get("cosmic.world.gravity.strength") ?? 0} 27 + Cell Size: ${this.worldStore.get("cosmic.world.grid.cellSize") ?? 0} 28 + `); 29 + } 30 + } 31 + }
-1
packages/demo/src/constants.ts
··· 1 - export const TILE_SIZE = 32;
+21 -8
packages/demo/src/index.ts
··· 1 1 import { Cosmic, CosmicMode, Entity } from '@cosmic/core'; 2 - import { Transform, Collider, WorldProperties, Renderable, Physics, PlayerControlled } from '@cosmic/kit/components'; 2 + import { Transform, Collider, Renderable, Physics, PlayerControlled, Movable, Name } from '@cosmic/kit/components'; 3 3 import { RenderingSystem, PhysicsSystem, CollisionSystem, PlayerControlSystem } from '@cosmic/kit/systems'; 4 - 5 - import { TILE_SIZE } from './constants'; 4 + import { LoggingSystem } from './LoggingSystem'; 6 5 6 + //====================================== 7 + // Initialize engine and systems 8 + //====================================== 7 9 const engine = Cosmic.getInstance(CosmicMode.DEVELOPMENT); 8 10 9 11 engine.addSystem(new PlayerControlSystem(engine)); 10 12 engine.addSystem(new PhysicsSystem(engine)); 11 13 engine.addSystem(new CollisionSystem()); 12 14 engine.addSystem(new RenderingSystem(engine)); 15 + engine.addSystem(new LoggingSystem(engine)); 13 16 14 17 engine.start(); 15 18 16 - const worldProperties = new WorldProperties(0); 19 + //====================================== 20 + // Initialize crucial stores 21 + //====================================== 22 + const worldStore = engine.getStore("cosmic.world"); 23 + worldStore.set("cosmic.world.gravity.strength", 8); 24 + worldStore.set("cosmic.world.grid.cellSize", 32); 17 25 26 + //====================================== 27 + // EXPERIMENTATION AREA 28 + //====================================== 18 29 const createTestEntity = ( 19 30 options: { 20 31 initialX: number; ··· 23 34 inputs: string[]; 24 35 } 25 36 ) => { 26 - const entity = new Entity("player"); 37 + const worldGridCellSize = worldStore.get("cosmic.world.grid.cellSize"); 27 38 28 - entity.addComponent(new Transform(options.initialX, options.initialY, TILE_SIZE * 2, TILE_SIZE * 2)); 39 + const entity = new Entity(); 40 + entity.addComponent(new Transform(options.initialX, options.initialY, worldGridCellSize * 2, worldGridCellSize * 2)); 29 41 entity.addComponent(new PlayerControlled(500, ...options.inputs)); 30 42 entity.addComponent(new Renderable()); 31 - entity.addComponent(new Physics(worldProperties.gravity)); 43 + entity.addComponent(new Movable()); 44 + entity.addComponent(new Physics()); 45 + entity.addComponent(new Name("player", "This is a player controlled entity.")); 32 46 entity.addComponent(new Collider(options.isStatic)); 33 - entity.addComponent(worldProperties); 34 47 35 48 return entity; 36 49 }
+4 -3
packages/kit/src/components.ts
··· 1 + export * from "./components/PlayerControlled"; 2 + export * from "./components/Renderable"; 1 3 export * from "./components/Transform"; 2 4 export * from "./components/Collider"; 3 5 export * from "./components/Physics"; 4 - export * from "./components/Renderable"; 5 - export * from "./components/PlayerControlled"; 6 - export * from "./components/WorldProperties"; 6 + export * from "./components/Movable"; 7 + export * from "./components/Name";
+5
packages/kit/src/components/Movable.ts
··· 1 + export class Movable { 2 + constructor( 3 + public speed: number = 500, 4 + ) { } 5 + }
+6
packages/kit/src/components/Name.ts
··· 1 + export class Name { 2 + constructor( 3 + public name: string, 4 + public description: string, 5 + ) {} 6 + }
+3 -12
packages/kit/src/components/Transform.ts
··· 1 - const TILE_SIZE = 32; 2 - 3 1 export class Transform { 4 2 constructor( 5 3 public x: number = 0, 6 4 public y: number = 0, 7 - public width: number = TILE_SIZE, 8 - public height: number = TILE_SIZE, 5 + public width: number = 32, 6 + public height: number = 32, 9 7 8 + // Todo: Consider moving this to Collider.ts 10 9 public isCollidingWithEntity: boolean = false, 11 10 public isCollidingWithCanvasBoundaries: boolean = false, 12 11 ) { } 13 - 14 - get tileX() { 15 - return Math.floor(this.x / TILE_SIZE); 16 - } 17 - 18 - get tileY() { 19 - return Math.floor(this.y / TILE_SIZE); 20 - } 21 12 }
-5
packages/kit/src/components/WorldProperties.ts
··· 1 - export class WorldProperties { 2 - constructor( 3 - public gravity: number = 8 4 - ) { } 5 - }
+2 -2
packages/kit/src/systems.ts
··· 1 + export * from "./systems/PlayerControlSystem"; 2 + export * from "./systems/CollisionSystem"; 1 3 export * from "./systems/RenderingSystem"; 2 4 export * from "./systems/PhysicsSystem"; 3 - export * from "./systems/CollisionSystem"; 4 - export * from "./systems/PlayerControlSystem";
+8 -9
packages/kit/src/systems/PhysicsSystem.ts
··· 1 1 import { Entity, System, type Cosmic } from "@cosmic/core"; 2 - import { Transform, WorldProperties, Physics } from "@cosmic/kit/components"; 2 + import { Transform, Physics } from "@cosmic/kit/components"; 3 3 4 4 export class PhysicsSystem extends System { 5 - public requiredComponents = new Set([Transform.name, Physics.name, WorldProperties.name]); 6 - public context: CanvasRenderingContext2D; 5 + public requiredComponents = new Set([Transform.name, Physics.name]); 6 + private worldStore: Map<string, any>; 7 7 8 8 constructor(engine: Cosmic) { 9 9 super(); 10 - this.context = engine.getContext(); 10 + this.worldStore = engine.getStore("cosmic.world"); 11 11 } 12 12 13 13 update(entities: Entity[], deltaTime: number) { 14 - entities.forEach(entity => { 15 - console.log(`[PhysicsSystem] Manipulating entity ${entity.id}`); 14 + const worldGravityStrength = this.worldStore.get("cosmic.world.gravity.strength") ?? 0; 16 15 16 + entities.forEach(entity => { 17 17 const transform = entity.getComponent(Transform)!; 18 - const physics = entity.getComponent(Physics)!; 19 - const worldProperties = entity.getComponent(WorldProperties)!; 18 + const physics = entity.getComponent(Physics)!; 20 19 21 - transform.y += (worldProperties.gravity * physics.weight) * deltaTime; 20 + transform.y += (worldGravityStrength * physics.weight) * deltaTime; 22 21 }); 23 22 } 24 23 }
-2
packages/kit/src/systems/RenderingSystem.ts
··· 14 14 this.context.clearRect(0, 0, 9999, 9999); 15 15 16 16 entities.forEach(entity => { 17 - console.log(`[RenderingSystem] Working on entity ${entity.id}`); 18 - 19 17 const transform = entity.getComponent(Transform)!; 20 18 21 19 this.context.strokeStyle = "#bf1d70";