This commit is contained in:
Elizabeth Hunt 2024-03-01 16:45:58 -07:00
parent 5148ee2063
commit aa08a8943a
Signed by: simponic
GPG Key ID: 52B3774857EB24B1
10 changed files with 163 additions and 0 deletions

View File

@ -0,0 +1,7 @@
export abstract class Component {
public readonly name: string;
constructor(name: string) {
this.name = name;
}
}

View File

@ -0,0 +1,3 @@
export namespace ComponentNames {
export const Sprite = "Sprite";
}

View File

@ -0,0 +1,2 @@
export * from "./Component";
export * from "./ComponentNames";

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

View File

@ -0,0 +1,5 @@
export namespace EntityNames {
export const Player = "Player";
export const Wall = "Wall";
export const Ball = "Ball";
}

View File

@ -0,0 +1,2 @@
export * from "./Entity";
export * from "./EntityNames";

90
src/engine/index.ts Normal file
View 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);
});
}
}

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

View 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";
}

View File

@ -0,0 +1,2 @@
export * from "./SystemNames";
export * from "./System";