参考:

deepseek

Laya对象池源码

Cocos对象池源码

 

一 简介对象池

二 Cocos对象池

三 Egret对象池

四 Laya对象池

五 自定义对象池

 

一 简介对象池

对象池是预先创建并管理可重复使用的对象,适用于频繁创建和销毁同类对象的场景。

 

优点:

1. 避免频繁创建和销毁引起内存分配、垃圾回收等消耗。

2. 避免垃圾回收引起的帧率波动、卡顿。

3. 对象已初始化可直接使用。

 

场景:

1. 子弹、粒子效果、角色NPC敌人、特效等等。

2. 任何需要频繁创建和销毁的游戏对象。

 

由上面可知,对象池需要如下几个功能,但是cocos自带对象池并不满足需求。

 

对象池就是个列表保存实例,需要的时候拿出来用,用完再放回去。

 

二 Cocos对象池

cocos中的对象池NodePool.ts,并没有创建预制体Prefab的代码,只能存放和获取已有的node。

 

node-pool.ts:

* `NodePool` 是用于管理节点对象的对象缓存池。<br/>
 * 它可以帮助您提高游戏性能,适用于优化对象的反复创建和销毁<br/>
 * 以前 cocos2d-x 中的 pool 和新的节点事件注册系统不兼容,因此请使用 `NodePool` 来代替。
 *
 * 新的 NodePool 需要实例化之后才能使用,每种不同的节点对象池需要一个不同的对象池实例,这里的种类对应于游戏中的节点设计,一个 prefab 相当于一个种类的节点。<br/>
 * 在创建缓冲池时,可以传入一个包含 unuse, reuse 函数的组件类型用于节点的回收和复用逻辑。<br/>
 *
 * 一些常见的用例是:<br/>
 *      1.在游戏中的子弹(死亡很快,频繁创建,对其他对象无副作用)<br/>
 *      2.糖果粉碎传奇中的木块(频繁创建)。
 *      等等....
 */
export class NodePool {
    /**
     * @en The pool handler component, it could be the class name or the constructor.
     * @zh 缓冲池处理组件,用于节点的回收和复用逻辑,这个属性可以是组件类名或组件的构造函数。
     */
    public declare poolHandlerComp?: Constructor<IPoolHandlerComponent> | string;
    private _pool: Node[] = [];

    /**
     * @en
     * Constructor for creating a pool for a specific node template (usually a prefab).
     * You can pass a component (type or name) argument for handling event for reusing and recycling node.
     * @zh
     * 使用构造函数来创建一个节点专用的对象池,您可以传递一个组件类型或名称,用于处理节点回收和复用时的事件逻辑。
     * @param poolHandlerComp @en The constructor or the class name of the component to control the unuse/reuse logic. @zh 处理节点回收和复用事件逻辑的组件类型或名称。
     * @example
     * import { NodePool, Prefab } from 'cc';
     *  properties: {
     *      template: Prefab
     *     },
     *     onLoad () {
     *       // MyTemplateHandler is a component with 'unuse' and 'reuse' to handle events when node is reused or recycled.
     *       this.myPool = new NodePool('MyTemplateHandler');
     *     }
     *  }
     */
    constructor (poolHandlerComp?: Constructor<IPoolHandlerComponent> | string) {
        this.poolHandlerComp = poolHandlerComp;
    }

    /**
     * @en The current available size in the pool
     * @zh 获取当前缓冲池的可用对象数量
     */
    public size (): number {
        return this._pool.length;
    }

    /**
     * @en Destroy all cached nodes in the pool
     * @zh 销毁对象池中缓存的所有节点
     */
    public clear (): void {
        const count = this._pool.length;
        for (let i = 0; i < count; ++i) {
            this._pool[i].destroy();
        }
        this._pool.length = 0;
    }

    /**
     * @en Put a new Node into the pool.
     * It will automatically remove the node from its parent without cleanup.
     * It will also invoke unuse method of the poolHandlerComp if exist.
     * @zh 向缓冲池中存入一个不再需要的节点对象。
     * 这个函数会自动将目标节点从父节点上移除,但是不会进行 cleanup 操作。
     * 这个函数会调用 poolHandlerComp 的 unuse 函数,如果组件和函数都存在的话。
     * @example
     * import { instantiate } from 'cc';
     * const myNode = instantiate(this.template);
     * this.myPool.put(myNode);
     */
    public put (obj: Node): void {
        if (obj && this._pool.indexOf(obj) === -1) {
            // Remove from parent, but don't cleanup
            obj.removeFromParent();

            // Invoke pool handler
            // @ts-ignore
            const handler = this.poolHandlerComp ? obj.getComponent(this.poolHandlerComp) : null;
            if (handler && handler.unuse) {
                handler.unuse();
            }

            this._pool.push(obj);
        }
    }

    /**
     * @en Get a obj from pool, if no available object in pool, null will be returned.
     * This function will invoke the reuse function of poolHandlerComp if exist.
     * @zh 获取对象池中的对象,如果对象池没有可用对象,则返回空。
     * 这个函数会调用 poolHandlerComp 的 reuse 函数,如果组件和函数都存在的话。
     * @param args - 向 poolHandlerComp 中的 'reuse' 函数传递的参数
     * @example
     *   let newNode = this.myPool.get();
     */
    public get (...args: any[]): Node | null {
        const last = this._pool.length - 1;
        if (last < 0) {
            return null;
        } else {
            // Pop the last object in pool
            const obj = this._pool[last];
            this._pool.length = last;

            // Invoke pool handler
            // @ts-ignore
            const handler = this.poolHandlerComp ? obj.getComponent(this.poolHandlerComp) : null;
            if (handler && handler.reuse) {
                handler.reuse(arguments);
            }
            return obj;
        }
    }
}

  

 

三 Egret对象池

egret是创建对象是用new class,而不是和cocos一样用创建预制体。

Pool.ts:

/**
 * 对象池
 * @author chenkai 2019.10.25
 */
class Pool {
    private static poolMap = {};
 
    /**
     * 根据签名获取对象
     * @param sign 签名
     * @param clz 类名,对象池空,则根据类名创建新对象
     */
    public static getItemBySign(sign:string,clz:any){
        let pool = (this.poolMap[sign] || (this.poolMap[sign] = []));
        if (pool.length) {
            return pool.pop();
        }
        let obj: any = new clz();
        obj.poolKey = sign;
        return obj;
    }
     
    /**
     * 获取对象
     * @param clz 对象类名
     * @param args
     */
    public static getItemByClass(clz: any) {
        let clzName = clz.prototype["__class__"];
        return this.getItemBySign(clzName, clz);
    }
 
    /**
     * 根据签名回收对象
     * @param sign 签名
     * @param ins 对象实例
     */
    public static recoverBySign(sign:string, ins:any){
        this.poolMap[sign] && this.poolMap[sign].push(ins);
    }
 
    /**
     * 回收对象
     * @param ins 对象实例
     */
    public static recoverByIns(ins: any) {
        this.recoverBySign(ins.poolKey, ins);
    }
 
    /**
     * 根据签名清理对象
     * @param sign 签名
     */
    public static clearBySign(sign:string){
        let pool = this.poolMap[sign];
        if(pool){
            let len = pool.length;
            for(let i=0;i<len;i++){
                pool[i].destroy && pool[i].destroy();
            }
            pool.length = 0;
            delete this.poolMap[sign];
        }
    }
 
    /**
     * 清理对象。对象会执行destroy。
     * @param clz 对象类名
     */
    public static clearByClass(clz: any) {
        let clzName = clz.prototype["__class__"];
        this.clearBySign(clzName);
    }
 
     
 
    /**清理所有对象 */
    public static clearAll(){
        for(let key in this.poolMap){
           this.clearBySign(key);
        }
    }
 
}

  

 

四 Laya对象池

Laya的对象池里也没有创建实例的代码,只能获取和回收已有的实例。

laya.core.js:

    class Pool {
        static getPoolBySign(sign) {
            return Pool._poolDic[sign] || (Pool._poolDic[sign] = []);
        }
        static clearBySign(sign) {
            if (Pool._poolDic[sign])
                Pool._poolDic[sign].length = 0;
        }
        static recover(sign, item) {
            if (item[Pool.POOLSIGN])
                return;
            item[Pool.POOLSIGN] = true;
            Pool.getPoolBySign(sign).push(item);
        }
        static recoverByClass(instance) {
            if (instance) {
                var className = instance["__className"] || instance.constructor._$gid;
                if (className)
                    Pool.recover(className, instance);
            }
        }
        static _getClassSign(cla) {
            var className = cla["__className"] || cla["_$gid"];
            if (!className) {
                cla["_$gid"] = className = Pool._CLSID + "";
                Pool._CLSID++;
            }
            return className;
        }
        static createByClass(cls) {
            return Pool.getItemByClass(Pool._getClassSign(cls), cls);
        }
        static getItemByClass(sign, cls) {
            let rst;
            let pool = Pool.getPoolBySign(sign);
            if (pool.length)
                rst = pool.pop();
            else
                rst = new cls();
            rst[Pool.POOLSIGN] = false;
            return rst;
        }
        static getItemByCreateFun(sign, createFun, caller = null) {
            var pool = Pool.getPoolBySign(sign);
            var rst = pool.length ? pool.pop() : createFun.call(caller);
            rst[Pool.POOLSIGN] = false;
            return rst;
        }
        static getItem(sign) {
            var pool = Pool.getPoolBySign(sign);
            var rst = pool.length ? pool.pop() : null;
            if (rst) {
                rst[Pool.POOLSIGN] = false;
            }
            return rst;
        }
    }

  

五 自定义对象池

根据需求自定义了一个对象池类Pool.ts,通过传入Class来获取实例,内部已经写好预制体Prefab的创建。

预制体Prefab的路径和bundle写在配置中,通过App.view.getViewCfg()获取。

通过传入Class就能创建,在对象池调用地方也方便左键点类名进去查看,比起用Node方便很多。

 

Pool.ts:

/**
 * 预制体对象池
 * 1.根据类名创建新的类,并缓存到对象池中
 * 2.类名的获取问题
 * console.log(MainScene.name);                       //预览时'MainScene',打包web时't',由于代码混淆缘故
 * console.log(MainScene.prototype['__classname__']); //预览和打包web都是'MainScene'
 * console.log(js.getClassName(this));                //预览和打包都是'MainScene'
 */
@ccclass
export default class Pool {
    //单例
    private static instance: Pool;
    public static get ins(): Pool {
        if (Pool.instance == null) {
            Pool.instance = new Pool();
        }
        return Pool.instance;
    }

    /**对象池列表  {key:className, value:[BaseView, ...]}*/
    private poolObj = {};

    /**
     * 创建对象
     * @param clz 类名
     * @param num 数量
     * @returns true创建成功 false创建失败
     */
    public create(clz: typeof BaseView, num: number) {
        if (num <= 0) {
            return false;
        }

        //获取预制体路径
        const viewCfg: ViewCfg = App.View.getViewCfg(clz);
        if (!viewCfg) {
            console.error("[Pool] 配置不存在:", clz);
            return false;
        }

        const prefabUrl: string = viewCfg.prefabUrl;
        const bundleName: string = viewCfg.bundleName;
        const className: string = clz.prototype['__classname__'];
        const pool: BaseView[] = this.getPool(className);

        //获取预制体
        const prefab: Prefab = App.Res.get(prefabUrl, bundleName);
        if (!prefab) {
            console.warn("[PrefabPool] 预制体不存在:", prefabUrl);
            return false;
        }

        //创建预制体
        for (let i = 0; i < num; i++) {
            const node = instantiate(prefab);
            const baseView: BaseView = node.getComponent(clz);
            pool.push(baseView);
        }

        return true;
    }

    /**
     * 获取类
     * @param clz 类名
     * @returns 返回类
     */
    public get(clz: typeof BaseView): any {
        const className: string = clz.prototype['__classname__'];
        const pool: BaseView[] = this.getPool(className);
        if (pool.length == 0) {
            if (this.create(clz, 1) == false) {
                return null;
            }
        }

        //返回类
        return pool.pop();
    }

    /**
     * 回收对象
     * @param baseView
     */
    public put(baseView: BaseView) {
        if (!isValid(baseView)) {
            console.warn("[Pool] 无法回收,回收对象已经被销毁")
            return;
        }

        //获取池子
        const className: string = js.getClassName(baseView);
        const pool: BaseView[] = this.getPool(className);
        const node: Node = baseView.node;

        //存放对象
        if (pool.indexOf(baseView) == -1) {
            node.removeFromParent();
            pool.push(baseView);
        }
    }

    /**
     * 清理对象池
     * @param clz (必须继承自BaseView)
     */
    public clear(clz: typeof BaseView) {
        const className: string = clz.prototype['__classname__'];
        this.clearByClassName(className);
    }

    /**清理所有对象池 */
    public clearAll() {
        for (let key in this.poolObj) {
            this.clearByClassName(key);
        }
    }

    /**
     * 获取池子
     * @param className 类名
     * @returns 池子
     */
    private getPool(className: string): BaseView[] {
        if (this.poolObj[className] == null) {
            this.poolObj[className] = [];
        }
        return this.poolObj[className];
    }

    /**
     * 根据类名清理对象池 (销毁并从对象池移除)
     * @param className 类名
     */
    private clearByClassName(className: string) {
        let pool = this.poolObj[className];
        if (pool) {
            for (const baseView of pool) {
                const node: Node = baseView.node;
                if (isValid(node)) {
                    node.removeFromParent();
                    node.destroy();
                }
            }
            pool.length = 0;
            delete this.poolObj[className];
        }
    }

    /**
     * 对象池长度
     * @param clz 类名
     */
    public getSize(clz: typeof BaseView) {
        const className: string = clz.prototype['__classname__'];
        const pool = this.getPool(className);
        return pool.length;
    }

    /**打印对象池 */
    public print() {
        console.log(this.poolObj);
    }

    /**销毁 */
    public dispose() {
        this.clearAll();
        Pool.instance = null;
    }
}

  

 

使用如下:

//预先创建10个
Pool.ins.create(ComItem,10);

//获取
const item:ComItem = Pool.ins.get(ComItem);

//回收
Pool.ins.put(item);

//清理
Pool.ins.clear(ComItem);
Pool.ins.clearAll();

  

 

posted on 2025-07-11 14:21  gamedaybyday  阅读(44)  评论(0)    收藏  举报