ecs init
This commit is contained in:
parent
5148ee2063
commit
aa08a8943a
7
src/engine/components/Component.ts
Normal file
7
src/engine/components/Component.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export abstract class Component {
|
||||||
|
public readonly name: string;
|
||||||
|
|
||||||
|
constructor(name: string) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
3
src/engine/components/ComponentNames.ts
Normal file
3
src/engine/components/ComponentNames.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export namespace ComponentNames {
|
||||||
|
export const Sprite = "Sprite";
|
||||||
|
}
|
2
src/engine/components/index.ts
Normal file
2
src/engine/components/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./Component";
|
||||||
|
export * from "./ComponentNames";
|
34
src/engine/entities/Entity.ts
Normal file
34
src/engine/entities/Entity.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { type Component } from "../components";
|
||||||
|
|
||||||
|
const randomId = () => (Math.random() * 1_000_000_000).toString();
|
||||||
|
|
||||||
|
export abstract class Entity {
|
||||||
|
public id: string;
|
||||||
|
public components: Map<string, Component>;
|
||||||
|
public name: string;
|
||||||
|
|
||||||
|
constructor(name: string, id: string = randomId()) {
|
||||||
|
this.name = name;
|
||||||
|
this.id = id;
|
||||||
|
this.components = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
public addComponent(component: Component) {
|
||||||
|
this.components.set(component.name, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getComponent<T extends Component>(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);
|
||||||
|
}
|
||||||
|
}
|
5
src/engine/entities/EntityNames.ts
Normal file
5
src/engine/entities/EntityNames.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export namespace EntityNames {
|
||||||
|
export const Player = "Player";
|
||||||
|
export const Wall = "Wall";
|
||||||
|
export const Ball = "Ball";
|
||||||
|
}
|
2
src/engine/entities/index.ts
Normal file
2
src/engine/entities/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./Entity";
|
||||||
|
export * from "./EntityNames";
|
90
src/engine/index.ts
Normal file
90
src/engine/index.ts
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import { Entity } from "./entities";
|
||||||
|
import { System } from "./systems";
|
||||||
|
|
||||||
|
export class Game {
|
||||||
|
private systemOrder: string[];
|
||||||
|
|
||||||
|
private running: boolean;
|
||||||
|
private lastTimeStamp: number;
|
||||||
|
|
||||||
|
public entities: Map<string, Entity>;
|
||||||
|
public systems: Map<string, System>;
|
||||||
|
public componentEntities: Map<string, Set<string>>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.lastTimeStamp = performance.now();
|
||||||
|
this.running = false;
|
||||||
|
this.systemOrder = [];
|
||||||
|
this.systems = new Map();
|
||||||
|
this.entities = new Map();
|
||||||
|
this.componentEntities = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
public start() {
|
||||||
|
this.lastTimeStamp = performance.now();
|
||||||
|
this.running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addEntity(entity: Entity) {
|
||||||
|
this.entities.set(entity.id, entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getEntity(id: string): Entity | undefined {
|
||||||
|
return this.entities.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeEntity(id: string) {
|
||||||
|
this.entities.delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public forEachEntityWithComponent(
|
||||||
|
componentName: string,
|
||||||
|
callback: (entity: Entity) => void,
|
||||||
|
) {
|
||||||
|
this.componentEntities.get(componentName)?.forEach((entityId) => {
|
||||||
|
const entity = this.getEntity(entityId);
|
||||||
|
if (!entity) return;
|
||||||
|
|
||||||
|
callback(entity);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public addSystem(system: System) {
|
||||||
|
if (!this.systemOrder.includes(system.name)) {
|
||||||
|
this.systemOrder.push(system.name);
|
||||||
|
}
|
||||||
|
this.systems.set(system.name, system);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSystem<T>(name: string): T {
|
||||||
|
return this.systems.get(name) as unknown as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
public doGameLoop(timeStamp: number) {
|
||||||
|
if (!this.running) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dt = timeStamp - this.lastTimeStamp;
|
||||||
|
this.lastTimeStamp = timeStamp;
|
||||||
|
|
||||||
|
// rebuild the Component -> { Entity } map
|
||||||
|
this.componentEntities.clear();
|
||||||
|
this.entities.forEach((entity) =>
|
||||||
|
entity.getComponents().forEach((component) => {
|
||||||
|
if (!this.componentEntities.has(component.name)) {
|
||||||
|
this.componentEntities.set(
|
||||||
|
component.name,
|
||||||
|
new Set<string>([entity.id]),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.componentEntities.get(component.name)?.add(entity.id);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.systemOrder.forEach((systemName) => {
|
||||||
|
this.systems.get(systemName)?.update(dt, this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
11
src/engine/systems/System.ts
Normal file
11
src/engine/systems/System.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { Game } from "..";
|
||||||
|
|
||||||
|
export abstract class System {
|
||||||
|
public readonly name: string;
|
||||||
|
|
||||||
|
constructor(name: string) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract update(dt: number, game: Game): void;
|
||||||
|
}
|
7
src/engine/systems/SystemNames.ts
Normal file
7
src/engine/systems/SystemNames.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export namespace SystemNames {
|
||||||
|
export const Render = "Render";
|
||||||
|
export const Physics = "Physics";
|
||||||
|
export const Input = "Input";
|
||||||
|
export const Collision = "Collision";
|
||||||
|
export const WallBounds = "WallBounds";
|
||||||
|
}
|
2
src/engine/systems/index.ts
Normal file
2
src/engine/systems/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./SystemNames";
|
||||||
|
export * from "./System";
|
Loading…
Reference in New Issue
Block a user