import { Entity } from '.' ; import { EMPTY } from './Entity' ; type NonFunctionPropertyNames < T > = { [ K in keyof T ] : T [ K ] extends ( args : unknown ) => void ? never : K ; } [ keyof T ] ; export type NonFunctionProperties < T > = Pick < T , NonFunctionPropertyNames < T > > ; export type ComponentClassType < P > = new ( data ) => Component < P > & P ; export abstract class Component < P > { constructor ( data ? ) { } } see https see https export class ComponentManager < P > { private clazz : ComponentClassType < P > ; private components : Array < Component < P > & P > = [ ] ; private entities : Entity [ ] = [ ] ; /**
     * 不在 Entity 中维护拥有的 Component 列表，反之亦然
     */ private lookup : Record < Entity , number > = { } ; constructor ( clazz ) { this . clazz = clazz ; } public clear ( ) { this . components = [ ] ; this . entities = [ ] ; this . lookup = { } ; } public contains ( entity ) { return this . lookup [ entity ] > - 1 ; } public create ( entity , data ? ) { this . lookup [ entity ] = this . components . length ; const component = new this . clazz ( data || { } ) ; this . components . push ( component ) ; this . entities . push ( entity ) ; return component ; } public remove ( entity ) { const componentIndex = this . lookup [ entity ] ; if ( componentIndex > - 1 ) { if ( componentIndex < this . components . length - 1 ) { this . components [ componentIndex ] = this . components [ this . components . length - 1 ] ; this . entities [ componentIndex ] = this . entities [ this . entities . length - 1 ] ; this . lookup [ this . entities [ componentIndex ] ] = componentIndex ; } } this . components . pop ( ) ; this . entities . pop ( ) ; delete this . lookup [ entity ] ; } public removeKeepSorted ( entity ) { const componentIndex = this . lookup [ entity ] ; if ( componentIndex > - 1 ) { const entity2 = this . entities [ componentIndex ] ; if ( componentIndex < this . components . length - 1 ) { for ( let i = componentIndex + 1 ; i < this . components . length ; ++ i ) { this . components [ i - 1 ] = this . components [ i ] ; } for ( let i = componentIndex + 1 ; i < this . entities . length ; ++ i ) { this . entities [ i - 1 ] = this . entities [ i ] ; this . lookup [ this . entities [ i - 1 ] ] = i - 1 ; } } this . components . pop ( ) ; this . entities . pop ( ) ; delete this . lookup [ entity2 ] ; } } public moveItem ( srcIndex , destIndex ) { if ( srcIndex === destIndex ) { return ; } const srcComponent = this . components [ srcIndex ] ; const srcEntity = this . entities [ srcIndex ] ; const direction = srcIndex < destIndex ? 1 : - 1 ; for ( let i = srcIndex ; i !== destIndex ; i += direction ) { const next = i + direction ; this . components [ i ] = this . components [ next ] ; this . entities [ i ] = this . entities [ next ] ; this . lookup [ this . entities [ i ] ] = i ; } this . components [ destIndex ] = srcComponent ; this . entities [ destIndex ] = srcEntity ; this . lookup [ srcEntity ] = destIndex ; } public getEntity ( index ) { return this . entities [ index ] ; } /**
     * 由于缺少类似 C++ 的重载操作符，没法通过 [下标] 直接访问。因此只能增加该方法用于遍历。
     */ public getComponent ( index ) { return this . components [ index ] ; } public getComponentByEntity ( entity ) { const componentIndex = this . lookup [ entity ] ; if ( componentIndex > - 1 ) { return this . components [ componentIndex ] ; } return null ; } public getCount ( ) { return this . components . length ; } public getEntityByComponentIndex ( componentIdx ) { for ( const entity of Object . keys ( this . lookup ) ) { const entityInNum = Number ( entity ) ; if ( this . lookup [ entityInNum ] === componentIdx ) { return entityInNum ; } } return EMPTY ; } public find ( callback ) { for ( let i = 0 ; i < this . getCount ( ) ; i ++ ) { const component = this . getComponent ( i ) ; if ( callback ( component , i ) ) { return component ; } } return null ; } public findIndex ( callback ) { for ( let i = 0 ; i < this . getCount ( ) ; i ++ ) { const component = this . getComponent ( i ) ; if ( callback ( component , i ) ) { return i ; } } return - 1 ; } public forEach ( callback ) { for ( const entity of Object . keys ( this . lookup ) ) { const entityInNum = Number ( entity ) ; const componentIndex = this . lookup [ entityInNum ] ; callback ( entityInNum , this . getComponent ( componentIndex ) ) ; } } public async forEachAsync ( callback ) { for ( const entity of Object . keys ( this . lookup ) ) { const entityInNum = Number ( entity ) ; const componentIndex = this . lookup [ entityInNum ] ; await callback ( entityInNum , this . getComponent ( componentIndex ) ) ; } } public map ( callback ) { const result = [ ] ; for ( const entity of Object . keys ( this . lookup ) ) { const entityInNum = Number ( entity ) ; const componentIndex = this . lookup [ entityInNum ] ; result . push ( callback ( entityInNum , this . getComponent ( componentIndex ) ) ) ; } return result ; } }