import { _decorator, Component, director, instantiate, Node, Prefab } from 'cc'; import IPoolable from './IPoolable'; export default class ObjectPool { private _inactive: Node[] = []; private _actives: Node[] = []; private _prefab: Prefab; private _expandable; private _poolHandlerComp: (new () => any) | string; public get listActive() { return [...this._actives]; } public get listInactive() { return [...this._inactive]; } public get all() { return [...this._actives, ...this._inactive]; } public get countInactive() { return this._inactive.length; } public get countActive() { return this._actives.length; } public get countAll() { return this.countInactive + this.countActive; } constructor( prefab?: Prefab, size?: number, expandable: boolean = true, poolHandlerComp?: (new () => any) | string, ) { if (!!!prefab) { console.error('prefab cant be null or undefine'); return; } this._prefab = prefab; this._expandable = expandable; this._poolHandlerComp = poolHandlerComp; for (let i = 0; i < size; ++i) { let obj = instantiate(this._prefab); // create node instance obj.removeFromParent(); this._inactive.push(obj); ObjectPool._poolLookUp.set(obj, this); } } //#region Static private static _poolLookUp: Map = new Map(); public static release(obj: Node): boolean { return ObjectPool._poolLookUp.get(obj)?.release(obj); } //#endregion public get(parent?: Node): Node; public get(classConstructor: new () => T): T; public get(classConstructor: new () => T, parent: Node): T; public get(a?: (new () => T) | Node, b?: Node): T | Node { if (!!!this._prefab) { console.error('prefab cant be null or undefine'); return; } let parent: Node; let classConstructor: new () => T; if (a instanceof Node) { parent = a || director.getScene(); } else { parent = b || director.getScene(); classConstructor = a; } let obj: Node = null; if (this._inactive.length > 0) { // Pop the last object in pool obj = this._inactive.pop(); } else if (this._expandable) { // if not enough node in the pool, we call cc.instantiate to create node obj = instantiate(this._prefab); ObjectPool._poolLookUp.set(obj, this); } else { obj = this._actives.shift(); obj.removeFromParent(); } obj.setParent(parent); this._actives.push(obj); // Invoke pool handler let handler: Component = null; if (typeof this._poolHandlerComp == 'string') { handler = this._poolHandlerComp ? obj.getComponent(this._poolHandlerComp) : null; } else { handler = this._poolHandlerComp ? obj.getComponent(this._poolHandlerComp) : null; } if (handler) { (handler as unknown as IPoolable)?.onGet(); } if (classConstructor) { return obj.getComponent(classConstructor); } return obj; } public release(obj: Node): boolean; public release(obj: T): boolean; public release(obj: T | Node): boolean { let node = obj instanceof Node ? obj : obj.node; const index = this._actives.indexOf(node); //check obj is belongs to pool if (index === -1) return false; this._actives.splice(index, 1); this._inactive.push(node); // Invoke pool handler let handler: Component = null; if (typeof this._poolHandlerComp == 'string') { handler = this._poolHandlerComp ? node.getComponent(this._poolHandlerComp) : null; } else { handler = this._poolHandlerComp ? node.getComponent(this._poolHandlerComp) : null; } if (handler) { (handler as unknown as IPoolable)?.onRelease(); } // Remove from parent, but don't cleanup node.removeFromParent(); return true; } public clear() { this.all.forEach((obj) => obj.destroy()); this._inactive = []; this._actives = []; ObjectPool._poolLookUp.clear(); } public releaseAll() { this.all.forEach((obj) => this.release(obj)); } }