UNPKG

entix-ecs

Version:
95 lines (89 loc) 4.53 kB
export type EntityId = number;// A entityId type export type ComponentClass<T> = new (...args: any[]) => T type Query = 'All' | 'Any' | 'None'; export class EntityManager {// A class to manage all the entities private entityCount: number = 0;//A counter for id's private entities: EntityId[] = [];//A array of entities private componentMap: Map<EntityId, Map<ComponentClass<any>, any>> = new Map();/* A nested map, contains EntityId with a another map containing components.*/ createEntity(): EntityId {// create a entity const id: EntityId = this.entityCount; this.entities.push(id); this.componentMap.set(id, new Map()); this.entityCount++; return id; }; addComponent<T>(id: EntityId, componentClass: ComponentClass<T>, componentInstance: T):void {/* adds a component to entity, doenst add a already added*/ if (!this.hasEntity(id)) throw new Error(`Entity ${id} does not exist!`); const components = this.componentMap.get(id); if (!components?.has(componentClass)) { components?.set(componentClass, componentInstance); } }; removeComponent<T>(id: EntityId, componentClass: ComponentClass<T>):void {//removes a component from entity if (!this.hasEntity(id)) throw new Error(`Entity ${id} does not exist!`); const components = this.componentMap.get(id); if (components?.has(componentClass)) { components.delete(componentClass); } }; getComponent<T>(id: EntityId, componentClass: ComponentClass<T>): T | undefined; getComponent<T>(id: EntityId, componentClass: ComponentClass<T>,strict:true): T; getComponent<T>(id: EntityId, componentClass: ComponentClass<T>,strict:false): T | undefined; getComponent<T>(id: EntityId, componentClass: ComponentClass<T>,strict?:boolean): T | undefined {// used to get a component if (!this.hasEntity(id)) throw new Error(`Entity ${id} does not exist!`); const components = this.componentMap.get(id); const requestedComponent = components?.get(componentClass) as T | undefined; if(strict && !requestedComponent){ throw new Error(`'${componentClass.name}'` + ' COMPONENT NOT FOUND ON ENTITY: ' + id); } return requestedComponent; }; getAllComponents(id: EntityId): any[] { if (!this.hasEntity(id)) throw new Error(`Entity ${id} does not exist!`); const components = this.componentMap.get(id); if (!components) return []; return Array.from(components.values()); }; hasComponent<T>(id:EntityId,componentClass:ComponentClass<T>):boolean { if (!this.hasEntity(id)) throw new Error(`Entity ${id} does not exist!`); return this.componentMap.get(id)?.has(componentClass) ?? false; }; removeEntity(id: EntityId) { if (!this.hasEntity(id)) throw new Error(`Entity ${id} does not exist!`); this.entities = this.entities.filter(e => e !== id); this.componentMap.delete(id); }; query<T extends Record<string,ComponentClass<any>>>( filterType: Query, componentsToMatch: T, callback: (id: EntityId, matched: {[K in keyof T]:InstanceType<T[K]>}) => void ) { const entitiesSnapshot = [...this.entities]; for (const entity of entitiesSnapshot) { if(!this.hasEntity(entity)) continue; const matched = {} as {[K in keyof T]: InstanceType<T[K]>}; const results: boolean[] = []; for (const key in componentsToMatch) { const compClass = componentsToMatch[key]; const instance = this.getComponent(entity, compClass); if (instance) matched[key] = instance; results.push(!!instance); } let shouldCall = false; if (filterType === 'All') shouldCall = results.every(Boolean); else if (filterType === 'Any') shouldCall = results.some(Boolean); else if (filterType === 'None') shouldCall = results.every(r => !r); else throw new Error(`Unknown filter type: ${filterType}`); if (shouldCall) callback(entity, matched); } }; getAllEntities():EntityId[]{ return this.entities; }; hasEntity(id:EntityId):boolean{ if(this.entities.includes(id)) return true; return false; }; };