Created
May 21, 2022 09:45
-
-
Save TimVosch/7175118bb37fc04b0d427dd299971e95 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
- Entities are simple IDs | |
- An entity has zero or more components | |
- Components are just data | |
- Systems are executed on components | |
*/ | |
export type Entity = number; | |
export type Component = Function; | |
export type ComponentInstance = Object; | |
export abstract class System { | |
public ecs!: ECS; | |
abstract requiredComponents: Component[]; | |
abstract update(entities: Set<Entity>, dt: number): void; | |
} | |
class ComponentContainer { | |
private components = new Map<Component, ComponentInstance>(); | |
public get<T extends ComponentInstance>(component: Component): T { | |
return this.components.get(component) as T; | |
} | |
public add<T extends ComponentInstance>(componentInstance: T): void { | |
this.components.set(componentInstance.constructor, componentInstance); | |
} | |
public remove(component: Component): void { | |
this.components.delete(component); | |
} | |
public has(...component: Component[]): boolean { | |
for (const c of component) { | |
if (!this.components.has(c)) { | |
return false; | |
} | |
} | |
return true; | |
} | |
} | |
export class ECS { | |
private entities = new Map<Entity, ComponentContainer>(); | |
private systems = new Map<System, Set<Entity>>(); | |
private ID = 0; | |
public createEntity(): Entity { | |
const entity = this.ID++; | |
this.entities.set(entity, new ComponentContainer()); | |
return entity; | |
} | |
public addComponent(entity: Entity, ...component: ComponentInstance[]): void { | |
const cc = this.entities.get(entity); | |
if (!cc) return; | |
for (const c of component) { | |
cc.add(c); | |
} | |
this.checkEntity(entity); | |
} | |
public removeComponent(entity: Entity, ...component: Component[]): void { | |
const cc = this.entities.get(entity); | |
if (!cc) return; | |
for (const c of component) { | |
cc.remove(c); | |
} | |
this.checkEntity(entity); | |
} | |
public getComponent(entity: Entity, component: Component): ComponentInstance | undefined { | |
return this.entities.get(entity)?.get(component); | |
} | |
public addSystem(system: System): void { | |
if (system.requiredComponents.length === 0) { | |
console.warn(`System ${system.constructor.name} has no required components`); | |
return; | |
} | |
system.ecs = this; | |
this.systems.set(system, new Set<Entity>()); | |
this.checkSystem(system); | |
} | |
public removeSystem(system: System): void { | |
this.systems.delete(system); | |
} | |
public update(): void { | |
for (const [system, entitySet] of this.systems) { | |
system.update(entitySet, 1.0); | |
} | |
} | |
private checkEntity(entity: Entity): void { | |
for (const system of this.systems.keys()) { | |
this.verifyEntitySystem(entity, system); | |
} | |
} | |
private checkSystem(system: System): void { | |
for (const entity of this.entities.keys()) { | |
this.verifyEntitySystem(entity, system); | |
} | |
} | |
private verifyEntitySystem(entity: Entity, system: System): void { | |
const cc = this.entities.get(entity); | |
const sys = this.systems.get(system); | |
if (!cc || !sys) return; | |
if (cc.has(...system.requiredComponents)) { | |
sys.add(entity); | |
} else { | |
sys.delete(entity); | |
} | |
} | |
} | |
export class ActiveEntity { | |
public constructor(private readonly id: Entity, private readonly ecs: ECS) {} | |
public get(component: Component): ComponentInstance | undefined { | |
return this.ecs.getComponent(this.id, component); | |
} | |
public add(...component: ComponentInstance[]): void { | |
this.ecs.addComponent(this.id, ...component); | |
} | |
public remove(...component: Component[]): void { | |
this.ecs.removeComponent(this.id, ...component); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment