/** @format */ import {ECSFilter} from './ECSFilter' import {ECSComConstructor, GetComConstructor as GetComConstructor, GetComConstructorType} from './ECSComponent' import {ECSSystem} from './ECSSystem' import {ComPoolIndex, ComType, EntityIndex} from './Const' import {ECSComponentPool} from './ECSComponentPool' export class ECSWorld { private _systems: ECSSystem[] = [] // world内所有的system private _entityToComponents: number[][] = [] // entity component 二维表 private _entitiesToDelete: Set = new Set() // 统一删除entity private _componentPools: ECSComponentPool[] = [] // component pools private _filters = new Map() // filter public curFrame: number = 0 /** 获取ComponentPool */ public getComponentPool(typeOrFunc: ComType | {prototype: T}): ECSComponentPool { let type = typeof typeOrFunc == 'number' ? typeOrFunc : GetComConstructorType(typeOrFunc) if (!this._componentPools[type]) { this._componentPools[type] = new ECSComponentPool(GetComConstructor(type)) } return this._componentPools[type] as any } /** 添加system */ public addSystem(system: ECSSystem) { this._systems.push(system) system.onAdd(this) for (let i = 0; i < this._entityToComponents.length; i++) { system.onEntityEnter(this, i) } } public getSystem(args: new () => T): T | undefined { return this._systems.find(system => system.constructor == args) as T | undefined } /** 移除system */ public removeSystem(system: ECSSystem) { system.onRemove(this) for (let i = 0; i < this._entityToComponents.length; i++) { system.onEntityLeave(this, i) } for (let i = this._systems.length - 1; i >= 0; i--) { if (this._systems[i] == system) { this._systems.splice(i, 1) } } } /** 创建实体 */ public createEntity(): number { let index = this._entityToComponents.length this._entityToComponents[index] = new Array(Object.keys(ComType).length / 2).fill(-1) for (let system of this._systems) { system.onEntityEnter(this, index) } return index } /** 移除实体 */ public removeEntity(entityIndex: EntityIndex): boolean { if (entityIndex <= 0) return false if (!this._entityToComponents[entityIndex]) { console.warn(`[ECSWorld] removeEntity entity is removed`) return false } this._filters.forEach((fillter, key) => { fillter.isContains(entityIndex) && fillter.onEntityLeave(entityIndex) }) for (let system of this._systems) { system.onEntityLeave(this, entityIndex) } this._entitiesToDelete.add(entityIndex) return true } public getComponentPoolIdx(entityIndex: EntityIndex, com: {prototype: T} | ComType): ComPoolIndex { let entity = this._entityToComponents[entityIndex] if (!entity) return -1 let type = typeof com == 'number' ? com : GetComConstructorType(com) return entity[type] } public getComponent(entityIndex: EntityIndex, com: {prototype: T} | ComType) { let comPoolIdx = this.getComponentPoolIdx(entityIndex, com) if (comPoolIdx == -1) return null return this.getComponentPool(com).get(comPoolIdx) } public addComponent(entityIndex: EntityIndex, com: {prototype: T}, dirty = true) { let entity = this._entityToComponents[entityIndex] if (!entity) return null let type = GetComConstructorType(com) let comPoolIdx = entity[type] if (comPoolIdx == -1) { comPoolIdx = this.getComponentPool(com).alloc() } entity[type] = comPoolIdx dirty && this.setEntityDirty(entityIndex) return this.getComponentPool(com).get(comPoolIdx) } public removeComponent(entityIndex: EntityIndex, com: ECSComConstructor, dirty = true) { let entity = this._entityToComponents[entityIndex] if (!entity) return true let type = GetComConstructorType(com) let comPoolIdx = entity[type] if (comPoolIdx == -1) return true entity[type] = -1 this.getComponentPool(com).free(comPoolIdx) dirty && this.setEntityDirty(entityIndex) return true } public removeAllComponents(entityIndex: EntityIndex, dirty = true) { let entity = this._entityToComponents[entityIndex] if (!entity) return null for (let i = 0; i < entity.length; i++) { if (entity[i] == -1) continue this.getComponentPool(i).free(entity[i]) entity[i] = -1 } dirty && this.setEntityDirty(entityIndex) } public getSingletonComponent(com: {prototype: T}): T { let component = this.getComponent(0, com) if (!component) { component = this.addComponent(0, com) } return component } public setEntityDirty(entityIndex: EntityIndex): void { this._filters.forEach((fillter, key) => { let accept = !this._entitiesToDelete.has(entityIndex) && fillter.isAccept(entityIndex) if (accept != fillter.isContains(entityIndex)) { accept ? fillter.onEntityEnter(entityIndex) : fillter.onEntityLeave(entityIndex) } }) } public getFilter(filterKey: string): ECSFilter { if (this._filters.has(filterKey)) { return this._filters.get(filterKey) } let [acceptStr, rejectStr] = filterKey.split('-') let accept = acceptStr && acceptStr.length > 0 ? acceptStr.split(',').map(Number) : null let reject = rejectStr && rejectStr.length > 0 ? rejectStr.split(',').map(Number) : null let filter = new ECSFilter(this, accept, reject) this._filters.set(filterKey, filter) // 将当期的entity放入filter for (let i = 1; i < this._entityToComponents.length; i++) { if (filter.isAccept(i)) { filter.onEntityEnter(i) } } return filter } public update(dt: number) { for (let system of this._systems) { system.onUpdate(this, dt) } if (this._entitiesToDelete.size > 0) { this._realRemoveEntity() } this.curFrame += 1 } public clear(isRemoveSystem: boolean = true) { if (isRemoveSystem) this._systems = [] for (let i = 0; i < this._entityToComponents.length; i++) { this.putNode(i) this.removeEntity(i) } //不能清楚0号实体 this._entityToComponents.length = 1 if (this._entitiesToDelete.size > 0) { this._realRemoveEntity() } this.curFrame = 0 } private _realRemoveEntity() { this._entitiesToDelete.forEach(value => { this.removeAllComponents(value) }) this._entitiesToDelete.clear() } public putNode(entityIndex: EntityIndex) {} }