Files
oops-framework/assets/script/core/libs/ECS.ts

1106 lines
35 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 重构原则:如无必要,勿增实体。
export module ecs {
export interface IComp {
canRecycle: boolean;
ent: Entity;
reset(): void;
}
export interface CompCtor<T> {
new(): T;
tid: number;
compName: string;
}
/**
* 组件里面只放数据可能在实际写代码的时候比较麻烦。如果是单纯对组件内的数据操作可以在组件里面写方法。
*/
export abstract class Comp implements IComp {
/**
* 组件的类型id-1表示未给该组件分配id
*/
static tid: number = -1;
static compName: string;
/**
* 拥有该组件的实体
*/
ent!: Entity;
/**
* 是否可回收组件对象,默认情况下都是可回收的。
* 如果该组件对象是由ecs系统外部创建的则不可回收需要用户自己手动进行回收。
*/
canRecycle: boolean = true;
/**
* 组件被回收时会调用这个接口。可以在这里重置数据,或者解除引用。
*
* **不要偷懒,除非你能确定并保证组件在复用时,里面的数据是先赋值然后再使用。**
*/
abstract reset(): void;
}
//#region 类型声明
type CompAddOrRemove = (entity: Entity) => void;
export type CompType<T> = CompCtor<T> | number;
//#endregion
//#region 注册组件
/**
* 组件缓存池
*/
let compPools: Map<number, IComp[]> = new Map();
/**
* 组件类型id
*/
let compTid = 0;
/**
* 组件构造函数
*/
let compCtors: (CompCtor<any> | number)[] = [];
/**
* 每个组件的添加和删除的动作都要派送到“关心”它们的group上。goup对当前拥有或者之前删除前拥有该组件的实体进行组件规则判断。判断该实体是否满足group
* 所期望的组件组合。
*/
let compAddOrRemove: Map<number, CompAddOrRemove[]> = new Map();
let tags: Map<number, string> = new Map();
/**
* 注册组件到ecs系统中
* @param compName 由于js打包会改变类名所以这里必须手动传入组件的名称。
* @param canNew 标识是否可以new对象。想继承自Cocos Creator的组件就不能去new需要写成@ecs.register('name', false)
*/
export function register<T>(compName: string, canNew: boolean = true) {
return function (ctor: CompCtor<T>) {
if (ctor.tid === -1) {
ctor.tid = compTid++;
ctor.compName = compName;
if (canNew) {
compCtors.push(ctor);
compPools.set(ctor.tid, []);
}
else {
compCtors.push(null);
}
compAddOrRemove.set(ctor.tid, []);
}
else {
throw new Error(`重复注册组件: ${compName}.`);
}
}
}
/**
* 添加tag
*
* eg.
* @registerTag()
* class Tag {
* static A: number;
* static B: number
* }
* @returns
*/
export function registerTag() {
return function (_class: any) {
let tid = compTid;
for (let k in _class) {
tid = compTid++;
_class[k] = tid;
compCtors.push(tid);
compPools.set(tid, []);
compAddOrRemove.set(tid, []);
tags.set(tid, k);
}
}
}
//#endregion
//#region context
/**
* 实体对象缓存池
*/
let entityPool: Entity[] = [];
/**
* 通过实体id查找实体对象
*/
let eid2Entity: Map<number, Entity> = new Map();
/**
* 缓存的group
*
* key是组件的筛选规则一个筛选规则对应一个group
*/
let groups: Map<number, Group> = new Map();
/**
* 实体自增id
*/
let eid = 1;
/**
* 创建实体
*/
export function createEntity<E extends Entity = Entity>(): E {
let entity = entityPool.pop();
if (!entity) {
entity = new Entity();
// @ts-ignore
entity.eid = eid++; // 实体id也是有限的资源
}
eid2Entity.set(entity.eid, entity);
return entity as E;
}
/**
* 创建组件对象
* @param ctor
*/
function createComp<T extends IComp>(ctor: CompCtor<T>): T {
if (!compCtors[ctor.tid]) {
throw Error(`没有找到该组件的构造函数,检查${ctor.compName}是否为不可构造的组件`);
}
let component = compPools.get(ctor.tid)!.pop() || new (compCtors[ctor.tid] as CompCtor<T>);
return component as T;
}
/**
* 指定一个组件创建实体,返回组件对象。
* @param ctor
*/
export function createEntityWithComp<T extends IComp>(ctor: CompCtor<T>): T {
let entity = createEntity();
return entity.add(ctor);
}
/**
* 指定多个组件创建实体,返回实体对象。
* @param ctors
*/
export function createEntityWithComps<E extends Entity = Entity>(...ctors: CompType<IComp>[]): E {
let entity = createEntity();
entity.addComponents(...ctors);
return entity as E;
}
/**
* 销毁实体。
*
* 缓存销毁的实体,下次新建实体时会优先从缓存中拿。
* @param entity
*/
function destroyEntity(entity: Entity) {
if (eid2Entity.has(entity.eid)) {
entityPool.push(entity);
eid2Entity.delete(entity.eid);
}
else {
console.warn('试图销毁不存在的实体!');
}
}
/**
* 创建group每个group只关心对应组件的添加和删除
* @param matcher 实体筛选器
*/
export function createGroup<E extends Entity = Entity>(matcher: IMatcher): Group<E> {
let group = groups.get(matcher.mid);
if (!group) {
group = new Group(matcher);
groups.set(matcher.mid, group);
let careComponentTypeIds = matcher.indices;
for (let i = 0; i < careComponentTypeIds.length; i++) {
compAddOrRemove.get(careComponentTypeIds[i])!.push(group.onComponentAddOrRemove.bind(group));
}
}
return group as unknown as Group<E>;
}
/**
* 动态查询实体
* @param matcher
* @returns
*/
export function query<E extends Entity = Entity>(matcher: IMatcher): E[] {
let group = groups.get(matcher.mid);
if (!group) {
group = createGroup(matcher);
eid2Entity.forEach(group.onComponentAddOrRemove, group);
}
return group.matchEntities as E[];
}
/**
* 清理所有的实体
*/
export function clear() {
eid2Entity.forEach((entity) => {
entity.destroy();
});
groups.forEach((group) => {
group.clear();
});
compAddOrRemove.forEach(callbackLst => {
callbackLst.length = 0;
});
eid2Entity.clear();
groups.clear();
}
/**
* 实体身上组件有增删操作,广播通知对应的观察者。
* @param entity 实体对象
* @param componentTypeId 组件类型id
*/
function broadcastCompAddOrRemove(entity: Entity, componentTypeId: number) {
let events = compAddOrRemove.get(componentTypeId);
for (let i = events!.length - 1; i >= 0; i--) {
events![i](entity);
}
// 判断是不是删了单例组件
if (tid2comp.has(componentTypeId)) {
tid2comp.delete(componentTypeId);
}
}
/**
* 根据实体id获得实体对象
* @param eid
*/
export function getEntityByEid<E extends Entity = Entity>(eid: number): E {
return eid2Entity.get(eid) as E;
}
/**
* 当前活动中的实体数量
*/
export function activeEntityCount() {
return eid2Entity.size;
}
//#endregion
/**
* 表示只关心这些组件的添加和删除动作。虽然实体可能有这些组件之外的组件,但是它们的添加和删除没有被关注,所以不会存在对关注之外的组件
* 进行添加操作引发Group重复添加实体。
* @param args
*/
export function allOf(...args: CompType<IComp>[]) {
return new Matcher().allOf(...args);
}
/**
* 组件间是或的关系,表示关注拥有任意一个这些组件的实体。
* @param args 组件索引
*/
export function anyOf(...args: CompType<IComp>[]) {
return new Matcher().anyOf(...args);
}
/**
* 表示关注只拥有这些组件的实体
*
* 注意:
* 不是特殊情况不建议使用onlyOf。因为onlyOf会监听所有组件的添加和删除事件。
* @param args 组件索引
*/
export function onlyOf(...args: CompType<IComp>[]) {
return new Matcher().onlyOf(...args);
}
/**
* 不包含指定的任意一个组件
*
* eg.
* ecs.excludeOf(A, B);表示不包含组件A或者组件B
* @param args
*/
export function excludeOf(...args: CompType<IComp>[]) {
return new Matcher().excludeOf(...args);
}
//#region 单例组件
let tid2comp: Map<number, IComp> = new Map();
/**
* 获取单例组件
* @param ctor 组件类
*/
export function getSingleton<T extends IComp>(ctor: CompCtor<T>) {
if (!tid2comp.has(ctor.tid)) {
let comp = createEntityWithComp(ctor) as T;
tid2comp.set(ctor.tid, comp);
}
return tid2comp.get(ctor.tid) as T;
}
/**
* 注册单例。主要用于那些不能手动创建对象的组件
* @param obj
*/
export function addSingleton(obj: IComp) {
let tid = (obj.constructor as CompCtor<IComp>).tid;
if (!tid2comp.has(tid)) {
tid2comp.set(tid, obj);
}
}
//#endregion
class Mask {
private mask: Uint32Array;
private size: number = 0;
constructor() {
let length = Math.ceil(compTid / 31);
this.mask = new Uint32Array(length);
this.size = length;
}
set(num: number) {
// https://stackoverflow.com/questions/34896909/is-it-correct-to-set-bit-31-in-javascript
// this.mask[((num / 32) >>> 0)] |= ((1 << (num % 32)) >>> 0);
this.mask[((num / 31) >>> 0)] |= (1 << (num % 31));
}
delete(num: number) {
this.mask[((num / 31) >>> 0)] &= ~(1 << (num % 31));
}
has(num: number) {
return !!(this.mask[((num / 31) >>> 0)] & (1 << (num % 31)));
}
or(other: Mask) {
for (let i = 0; i < this.size; i++) {
// &操作符最大也只能对2^30进行操作如果对2^31&2^31会得到负数。当然可以(2^31&2^31) >>> 0这样多了一步右移操作。
if (this.mask[i] & other.mask[i]) {
return true;
}
}
return false;
}
and(other: Mask) {
for (let i = 0; i < this.size; i++) {
if ((this.mask[i] & other.mask[i]) != this.mask[i]) {
return false;
}
}
return true;
}
clear() {
for (let i = 0; i < this.size; i++) {
this.mask[i] = 0;
}
}
}
export class Entity {
/**
* 实体唯一标识,不要手动修改。
*/
public eid: number = -1;
private mask = new Mask();
/**
* 当前实体身上附加的组件构造函数
*/
private compTid2Ctor: Map<number, CompType<IComp>> = new Map();
private compTid2Obj: Map<number, IComp> = new Map();
constructor() { }
/**
* 根据组件id动态创建组件并通知关心的系统。
*
* 如果实体存在了这个组件,那么会先删除之前的组件然后添加新的。
*
* 注意不要直接new Componentnew来的Component不会从Component的缓存池拿缓存的数据。
* @param componentTypeId 组件id
* @param isReAdd true-表示用户指定这个实体可能已经存在了该组件那么再次add组件的时候会先移除该组件然后再添加一遍。false-表示不重复添加组件。
*/
add<T extends IComp>(obj: T): Entity;
add(ctor: number, isReAdd?: boolean): Entity;
add<T extends IComp>(ctor: CompCtor<T>, isReAdd?: boolean): T;
add<T extends IComp>(ctor: CompType<T>, isReAdd?: boolean): T;
add<T extends IComp>(ctor: CompType<T> | T, isReAdd: boolean = false): T | Entity {
// console.log('typeof: ', typeof ctor);
if (typeof ctor === 'function') {
let compTid = ctor.tid;
if (ctor.tid === -1) {
throw Error('组件未注册!');
}
if (this.compTid2Ctor.has(compTid)) {// 判断是否有该组件,如果有则先移除
if (isReAdd) {
this.remove(ctor);
}
else {
console.log(`已经存在组件:${ctor.compName}`);
// @ts-ignore
return this[ctor.compName] as T;
}
}
this.mask.set(compTid);
let comp: T;
if (this.compTid2Obj.has(compTid)) {
comp = this.compTid2Obj.get(compTid) as T;
this.compTid2Obj.delete(compTid);
}
else {
// 创建组件对象
comp = createComp(ctor) as T;
}
// 将组件对象直接附加到实体对象身上,方便直接获取。
// @ts-ignore
this[ctor.compName] = comp;
this.compTid2Ctor.set(compTid, ctor);
comp.ent = this;
// 广播实体添加组件的消息
broadcastCompAddOrRemove(this, compTid);
return comp;
}
else if (typeof ctor === 'number') {
if (tags.has(ctor)) {
this.mask.set(ctor);
this.compTid2Ctor.set(ctor, ctor);
let tagName = tags.get(ctor)!;
// @ts-ignore
this[tagName] = ctor;
broadcastCompAddOrRemove(this, ctor);
}
else {
throw Error('不存在的tag');
}
return this;
}
else {
let tmpCtor = (ctor.constructor as CompCtor<T>);
let compTid = tmpCtor.tid;
// console.assert(compTid !== -1 || !compTid, '组件未注册!');
// console.assert(this.compTid2Ctor.has(compTid), '已存在该组件!');
if (compTid === -1 || compTid == null) {
throw Error('组件未注册!');
}
if (this.compTid2Ctor.has(compTid)) {
throw Error('已经存在该组件!');
}
this.mask.set(compTid);
this[tmpCtor.compName] = ctor;
this.compTid2Ctor.set(compTid, tmpCtor);
ctor.ent = this;
ctor.canRecycle = false;
broadcastCompAddOrRemove(this, compTid);
return this;
}
}
addComponents<T extends IComp>(...ctors: CompType<T>[]) {
for (let ctor of ctors) {
this.add(ctor);
}
return this;
}
get(ctor: number): number;
get<T extends IComp>(ctor: CompCtor<T>): T;
get<T extends IComp>(ctor: CompCtor<T> | number): T {
let compName: string;
if (typeof (ctor) === 'number') {
compName = tags.get(ctor)!;
}
else {
compName = ctor.compName;
}
// @ts-ignore
return this[compName];
}
has(ctor: CompType<IComp>): boolean {
if (typeof ctor == "number") {
return this.mask.has(ctor);
}
else {
return this.compTid2Ctor.has(ctor.tid);
}
}
/**
*
* @param ctor 组件构造函数或者组件Tag
* @param isRecycle 是否回收该组件对象。对于有些组件上有大量数据,当要描述移除组件但是不想清除组件上的数据是可以
* 设置该参数为false这样该组件对象会缓存在实体身上下次重新添加组件时会将该组件对象添加回来不会重新从组件缓存
* 池中拿一个组件来用。
*/
remove(ctor: CompType<IComp>, isRecycle: boolean = true) {
let componentTypeId = -1;
let compName = '';
let hasComp = false;
if (typeof ctor === "number") {
componentTypeId = ctor;
if (this.mask.has(ctor)) {
hasComp = true;
compName = tags.get(ctor)!;
}
}
else {
componentTypeId = ctor.tid;
compName = ctor.compName;
if (this.mask.has(componentTypeId)) {
hasComp = true;
let comp = this[ctor.compName] as IComp;
comp.ent = null;
if (isRecycle) {
comp.reset();
if (comp.canRecycle) {
compPools.get(componentTypeId).push(comp);
}
}
else {
this.compTid2Obj.set(componentTypeId, comp);
}
}
}
if (hasComp) {
this[compName] = null;
this.mask.delete(componentTypeId);
this.compTid2Ctor.delete(componentTypeId);
broadcastCompAddOrRemove(this, componentTypeId);
}
}
private _remove(comp: CompType<IComp>) {
this.remove(comp, false);
}
private _remove_release(comp: CompType<IComp>) {
this.remove(comp, true);
}
/**
* 销毁实体,实体会被回收到实体缓存池中。
* @param isClearData 是否清除组件上的数据
*/
destroy(isClearData: boolean = true) {
if (isClearData)
this.compTid2Ctor.forEach(this._remove_release, this);
else
this.compTid2Ctor.forEach(this._remove, this);
destroyEntity(this);
this.compTid2Obj.clear();
}
}
export class Group<E extends Entity = Entity> {
/**
* 实体筛选规则
*/
private matcher: IMatcher;
private _matchEntities: Map<number, E> = new Map();
private _entitiesCache: E[] | null = null;
/**
* 符合规则的实体
*/
public get matchEntities() {
if (this._entitiesCache === null) {
this._entitiesCache = Array.from(this._matchEntities.values());
}
return this._entitiesCache;
}
/**
* 当前group中实体的数量。
*
* 不要手动修改这个属性值。
*/
public count = 0; // 其实可以通过this._matchEntities.size获得实体数量但是需要封装get方法。为了减少一次方法的调用所以才直接创建一个count属性
/**
* 获取matchEntities中第一个实体
*/
get entity(): E {
return this.matchEntities[0];
}
private _enteredEntities: Map<number, E> | null = null;
private _removedEntities: Map<number, E> | null = null;
constructor(matcher: IMatcher) {
this.matcher = matcher;
}
public onComponentAddOrRemove(entity: E) {
if (this.matcher.isMatch(entity)) { // Group只关心指定组件在实体身上的添加和删除动作。
this._matchEntities.set(entity.eid, entity);
this._entitiesCache = null;
this.count++;
if (this._enteredEntities) {
this._enteredEntities.set(entity.eid, entity);
this._removedEntities!.delete(entity.eid);
}
}
else if (this._matchEntities.has(entity.eid)) { // 如果Group中有这个实体但是这个实体已经不满足匹配规则则从Group中移除该实体
this._matchEntities.delete(entity.eid);
this._entitiesCache = null;
this.count--;
if (this._enteredEntities) {
this._enteredEntities.delete(entity.eid);
this._removedEntities!.set(entity.eid, entity);
}
}
}
public watchEntityEnterAndRemove(enteredEntities: Map<number, E>, removedEntities: Map<number, E>) {
this._enteredEntities = enteredEntities;
this._removedEntities = removedEntities;
}
clear() {
this._matchEntities.clear();
this._entitiesCache = null;
this.count = 0;
this._enteredEntities?.clear();
this._removedEntities?.clear();
}
}
abstract class BaseOf {
protected mask = new Mask();
public indices: number[] = [];
constructor(...args: CompType<IComp>[]) {
let componentTypeId = -1;
let len = args.length;
for (let i = 0; i < len; i++) {
if (typeof (args[i]) === "number") {
componentTypeId = args[i] as number;
}
else {
componentTypeId = (args[i] as CompCtor<IComp>).tid;
}
if (componentTypeId == -1) {
throw Error('存在没有注册的组件!');
}
this.mask.set(componentTypeId);
if (this.indices.indexOf(componentTypeId) < 0) { // 去重
this.indices.push(componentTypeId);
}
}
if (len > 1) {
this.indices.sort((a, b) => { return a - b; }); // 对组件类型id进行排序这样关注相同组件的系统就能共用同一个group
}
}
public toString(): string {
return this.indices.join('-'); // 生成group的key
}
public abstract getKey(): string;
public abstract isMatch(entity: Entity): boolean;
}
/**
* 用于描述包含任意一个这些组件的实体
*/
class AnyOf extends BaseOf {
public isMatch(entity: Entity): boolean {
// @ts-ignore
return this.mask.or(entity.mask);
}
getKey(): string {
return 'anyOf:' + this.toString();
}
}
/**
* 用于描述包含了“这些”组件的实体,这个实体除了包含这些组件还可以包含其他组件
*/
class AllOf extends BaseOf {
public isMatch(entity: Entity): boolean {
// @ts-ignore
return this.mask.and(entity.mask);
}
getKey(): string {
return 'allOf:' + this.toString();
}
}
/**
* 不包含指定的任意一个组件
*/
class ExcludeOf extends BaseOf {
public getKey(): string {
return 'excludeOf:' + this.toString();
}
public isMatch(entity: Entity): boolean {
// @ts-ignore
return !this.mask.or(entity.mask);
}
}
export interface IMatcher {
mid: number;
indices: number[];
key: string;
isMatch(entity: Entity): boolean;
}
let macherId: number = 1;
/**
* 筛选规则间是“与”的关系
* 比如ecs.Macher.allOf(...).excludeOf(...)表达的是allOf && excludeOf即实体有“这些组件” 并且 “没有这些组件”
*/
class Matcher implements IMatcher {
protected rules: BaseOf[] = [];
protected _indices: number[] | null = null;
public isMatch!: (entity: Entity) => boolean;
public mid: number = -1;
private _key: string | null = null;
public get key(): string {
if (!this._key) {
let s = '';
for (let i = 0; i < this.rules.length; i++) {
s += this.rules[i].getKey()
if (i < this.rules.length - 1) {
s += ' && '
}
}
this._key = s;
}
return this._key;
}
constructor() {
this.mid = macherId++;
}
/**
* 匹配器关注的组件索引。在创建Group时Context根据组件id去给Group关联组件的添加和移除事件。
*/
public get indices() {
if (this._indices === null) {
this._indices = [];
this.rules.forEach((rule) => {
Array.prototype.push.apply(this._indices, rule.indices);
});
}
return this._indices;
}
/**
* 组件间是或的关系,表示关注拥有任意一个这些组件的实体。
* @param args 组件索引
*/
public anyOf(...args: CompType<IComp>[]): Matcher {
this.rules.push(new AnyOf(...args));
this.bindMatchMethod();
return this;
}
/**
* 组件间是与的关系,表示关注拥有所有这些组件的实体。
* @param args 组件索引
*/
public allOf(...args: CompType<IComp>[]): Matcher {
this.rules.push(new AllOf(...args));
this.bindMatchMethod();
return this;
}
/**
* 表示关注只拥有这些组件的实体
*
* 注意:
* 不是特殊情况不建议使用onlyOf。因为onlyOf会监听所有组件的添加和删除事件。
* @param args 组件索引
*/
public onlyOf(...args: CompType<IComp>[]): Matcher {
this.rules.push(new AllOf(...args));
let otherTids: CompType<IComp>[] = [];
for (let ctor of compCtors) {
if (args.indexOf(ctor) < 0) {
otherTids.push(ctor);
}
}
this.rules.push(new ExcludeOf(...otherTids));
this.bindMatchMethod();
return this;
}
/**
* 不包含指定的任意一个组件
* @param args
*/
public excludeOf(...args: CompType<IComp>[]) {
this.rules.push(new ExcludeOf(...args));
this.bindMatchMethod();
return this;
}
private bindMatchMethod() {
if (this.rules.length === 1) {
this.isMatch = this.isMatch1;
}
else if (this.rules.length === 2) {
this.isMatch = this.isMatch2;
}
else {
this.isMatch = this.isMatchMore;
}
}
private isMatch1(entity: Entity): boolean {
return this.rules[0].isMatch(entity);
}
private isMatch2(entity: Entity): boolean {
return this.rules[0].isMatch(entity) && this.rules[1].isMatch(entity);
}
private isMatchMore(entity: Entity): boolean {
for (let rule of this.rules) {
if (!rule.isMatch(entity)) {
return false;
}
}
return true;
}
public clone(): Matcher {
let newMatcher = new Matcher();
newMatcher.mid = macherId++;
this.rules.forEach(rule => newMatcher.rules.push(rule));
return newMatcher;
}
}
//#region System
/**
* 如果需要监听实体首次进入System的情况实现这个接口。
*
* entityEnter会在update方法之前执行实体进入后不会再次进入entityEnter方法中。
* 当实体从当前System移除下次再次符合条件进入System也会执行上述流程。
*/
export interface IEntityEnterSystem<E extends Entity = Entity> {
entityEnter(entities: E[]): void;
}
/**
* 如果需要监听实体从当前System移除需要实现这个接口。
*/
export interface IEntityRemoveSystem<E extends Entity = Entity> {
entityRemove(entities: E[]): void;
}
/**
* 第一次执行update
*/
export interface ISystemFirstUpdate<E extends Entity = Entity> {
firstUpdate(entities: E[]): void;
}
export abstract class ComblockSystem<E extends Entity = Entity> {
protected group: Group<E>;
protected dt: number = 0;
private enteredEntities: Map<number, E> | null = null;
private removedEntities: Map<number, E> | null = null;
private hasEntityEnter: boolean = false;
private hasEntityRemove: boolean = false;
private tmpExecute: ((dt: number) => void) | null = null;
private execute!: (dt: number) => void;
constructor() {
let hasOwnProperty = Object.hasOwnProperty;
let prototype = Object.getPrototypeOf(this);
let hasEntityEnter = hasOwnProperty.call(prototype, 'entityEnter');
let hasEntityRemove = hasOwnProperty.call(prototype, 'entityRemove');
let hasFirstUpdate = hasOwnProperty.call(prototype, 'firstUpdate');
this.hasEntityEnter = hasEntityEnter;
this.hasEntityRemove = hasEntityRemove;
if (hasEntityEnter || hasEntityRemove) {
this.enteredEntities = new Map<number, E>();
this.removedEntities = new Map<number, E>();
this.execute = this.execute1;
this.group = createGroup(this.filter());
this.group.watchEntityEnterAndRemove(this.enteredEntities, this.removedEntities);
}
else {
this.execute = this.execute0;
this.group = createGroup(this.filter());
}
if (hasFirstUpdate) {
this.tmpExecute = this.execute;
this.execute = this.updateOnce;
}
}
init(): void {
}
onDestroy(): void {
}
hasEntity(): boolean {
return this.group.count > 0;
}
private updateOnce(dt: number) {
if (this.group.count === 0) {
return;
}
this.dt = dt;
// 处理刚进来的实体
if (this.enteredEntities!.size > 0) {
(this as unknown as IEntityEnterSystem).entityEnter(Array.from(this.enteredEntities!.values()) as E[]);
this.enteredEntities!.clear();
}
(this as unknown as ISystemFirstUpdate).firstUpdate(this.group.matchEntities);
this.execute = this.tmpExecute!;
this.execute(dt);
this.tmpExecute = null;
}
/**
* 只执行update
* @param dt
* @returns
*/
private execute0(dt: number): void {
if (this.group.count === 0) {
return;
}
this.dt = dt;
this.update(this.group.matchEntities);
}
/**
* 先执行entityRemove再执行entityEnter最后执行update。
* @param dt
* @returns
*/
private execute1(dt: number): void {
if (this.removedEntities!.size > 0) {
if (this.hasEntityRemove) {
(this as unknown as IEntityRemoveSystem).entityRemove(Array.from(this.removedEntities!.values()) as E[]);
}
this.removedEntities!.clear();
}
if (this.group.count === 0) {
return;
}
this.dt = dt;
// 处理刚进来的实体
if (this.enteredEntities!.size > 0) {
if (this.hasEntityEnter) {
(this as unknown as IEntityEnterSystem).entityEnter(Array.from(this.enteredEntities!.values()) as E[]);
}
this.enteredEntities!.clear();
}
this.update(this.group.matchEntities as E[]);
}
/**
* 实体过滤规则
*
* 根据提供的组件过滤实体。
*/
abstract filter(): IMatcher;
// abstract update(entities: E[]): void;
update(entities: E[]) { }; // 避免不需要用update时写一些多余的代码
}
/**
* System的root对游戏中的System遍历从这里开始。
*
* 一个System组合中只能有一个RootSystem可以有多个并行的RootSystem。
*/
export class RootSystem {
private executeSystemFlows: ComblockSystem[] = [];
private systemCnt: number = 0;
add(system: System | ComblockSystem) {
if (system instanceof System) {
// 将嵌套的System都“摊平”放在根System中进行遍历减少execute的频繁进入退出。
Array.prototype.push.apply(this.executeSystemFlows, system.comblockSystems);
}
else {
this.executeSystemFlows.push(system as ComblockSystem);
}
this.systemCnt = this.executeSystemFlows.length;
return this;
}
init() {
this.executeSystemFlows.forEach(sys => sys.init());
}
execute(dt: number) {
for (let i = 0; i < this.systemCnt; i++) {
// @ts-ignore
this.executeSystemFlows[i].execute(dt);
}
}
clear() {
this.executeSystemFlows.forEach(sys => sys.onDestroy());
}
}
/**
* 系统组合器用于将多个相同功能模块的系统逻辑上放在一起。System也可以嵌套System。
*/
export class System {
private _comblockSystems: ComblockSystem[] = [];
get comblockSystems() {
return this._comblockSystems;
}
add(system: System | ComblockSystem) {
if (system instanceof System) {
Array.prototype.push.apply(this._comblockSystems, system._comblockSystems);
system._comblockSystems.length = 0;
}
else {
this._comblockSystems.push(system as ComblockSystem);
}
return this;
}
}
//#endregion
}