分享一个分帧创建的对象池管理器

官方提供的NodePool不太好用 想要使用get获取对象 必须要预先创建。但有的时候预先并不清楚需要创建多少个,创建多了少了都不太好,而且在用NodePool的地方都要自己封装一个创建预制体的方法

所以这里封装了一个Pool 使用的时候不需要关心数量 如果池中有?->直接用 没有?->创建
并且被创建的对象绝对不会有多余或不够的情况
比如第一次发射100发子弹 创建100预制体 此时池中就有100可用的缓存
回收之后 第二次如果发射150个 则会自动创建超出的50个

import { Component, Prefab, Node, director, Constructor, instantiate } from "cc";
type PoolTotalCallback<T> = (item: T | Node, data: any, index: number) => void
type PoolPushCallback<T> = (item: T | Node, index: number) => void
/** 对象池管理类 */
export class Pool<T extends Component = Component> {
    private prefab: Prefab | Node
    private parent: Node
    private component: Constructor<T>
    private gener!: Generator
    private _caches: any[]
    private _uses: any[]
    get caches() { return Object.assign([], this._caches) }
    get uses() { return Object.assign([], this._uses) }
    constructor(prefab: Prefab | Node, parent: Node, component?: Constructor<T>) {
        this.prefab = prefab
        this.parent = parent
        this.component = component
        this._caches = []
        this._uses = []
    }
    /**
     * 从对象池中创建一组对象
     */
    public total(arrayOrNumber: any[] | number, done: PoolTotalCallback<T>, duration: number = 10): Promise<void> {
        return new Promise(async (resolve, reject) => {
            if (arrayOrNumber == undefined || arrayOrNumber == null || typeof arrayOrNumber == "number" && isNaN(arrayOrNumber)) return resolve()
            this.gener?.return("")
            let length = arrayOrNumber instanceof Array ? arrayOrNumber.length : arrayOrNumber
            let data = arrayOrNumber instanceof Array ? arrayOrNumber : null
            this.gener = this.getGenerator(length, this.task.bind(this), data, done)
            await this.exeGenerator(this.gener, duration)
            this.surplus(length)
            resolve()
        })
    }
    /**
     * 添加一个对象
     */
    push(done: PoolPushCallback<T>): void {
        let node = this.getNode()
        done(node, this._uses.length - 1)
    }
    /**
     * 弹出一个对象
     * @param node 要弹出的对象
     */
    pop(node: Node | T): boolean {
        if (!node) return
        let curre = node instanceof Node ? node : node.node
        let index = this._uses.findIndex(item => {
            if (item instanceof Node) return item.uuid == curre.uuid
            return item.node.uuid == curre.uuid
        })
        if (index == -1) return false
        let obj = this._uses.splice(index, 1)[0]
        obj instanceof Node ? obj.removeFromParent() : obj.node.removeFromParent()
        this._caches.push(obj)
        return true
    }
    /**
     * 回收所有对象到对象池
     */
    recycle(): void {
        try {
            while (this._uses.length > 0) {
                let obj = this._uses.pop()
                obj instanceof Node ? obj.removeFromParent() : obj.node.removeFromParent()
                this._caches.push(obj)
            }
        } catch (error) {
            console.error(error)
        }
    }
    /** 
     * 清理对象池
     */
    clear() {
        let list = [...this._uses, ...this._caches]
        for (let i = 0; i < list.length; i++) {
            const obj = list[i];
            obj instanceof Node ? obj.destroy() : obj.node.destroy()
        }
        this._caches = []
        this._uses = []
    }
    private task(index: number, array: any[] | null, done: PoolTotalCallback<T>) {
        let data = array?.[index] || index
        let node = this.getNode()
        done(node, data, index)
    }
    private surplus(length: number) {
        try {
            while (this._uses.length - length) {
                let obj = this._uses.pop()
                if (obj instanceof Node) {
                    obj.removeFromParent()
                } else {
                    obj.node.removeFromParent()
                }
                this._caches.push(obj)
            }
        } catch (error) {
            console.error(error)
        }
    }
    private getNode(): T | Node {
        if (this._caches.length == 0) {
            let obj = instantiate(this.prefab) as Node
            let item = obj.getComponent(this.component) || obj
            this._caches.push(item)
        }
        let node = this._caches.pop()
        if (node instanceof Node) {
            node.parent = this.parent
        } else {
            node.node.parent = this.parent
        }
        this._uses.push(node)
        return node
    }
    private * getGenerator(length: number, callback: Function, ...params: any): Generator {
        for (let i = 0; i < length; i++) {
            yield callback(i, ...params)
        }
    }
    private exeGenerator(generator: Generator, duration: number) {
        return new Promise((resolve, reject) => {
            let gen = generator
            let execute = () => {
                let startTime = new Date().getTime()
                for (let iter = gen.next(); ; iter = gen.next()) {
                    if (iter == null || iter.done) {
                        resolve(null)
                        return
                    }
                    if (new Date().getTime() - startTime > duration) {
                        setTimeout(() => {
                            execute()
                        }, director.getDeltaTime() * 1000)
                        return
                    }
                }
            }
            execute()
        })
    }
}

使用方法

        this.pool = new Pool(this.prefab, this.content, RootItem)
        // 你的数据
        var dataArray = [1, 2, 3, 4, 5, 6, 7, 8, 9]
        // 批量创建 方法一 直接传入数据数组
        await this.pool.total(dataArray, (item: RootItem, data: any, index: number) => {
            // data = dataArray[index]
            item.show(data)
        })
        // 批量创建 方法二 传入数量
        await this.pool.total(dataArray.length, (item: RootItem, data: any, index: number) => {
            // data = null
            item.show(dataArray[index])
        })
        // 数据发生变化时重新调用total
        dataArray = [1,2,3]
        await this.pool.total(dataArray, (item: RootItem, data: any, index: number) => {
            // data = dataArray[index]
            item.show(data)
        })

        // 再添加一个对象
        this.pool.push((item: RootItem, index: number) => {
            item.show(index)
        })

        // 回收一个到对象池
        let node = this.pool.uses[0] //找到要回收的那个对象
        this.pool.pop(node)

        // 回收所有
        this.pool.recycle()

        // 清理对象池 删除对象
        this.pool.clear()
10赞

mark mark

markmark

markmark

我喜欢挖坟!手动点赞!

mark~

插个眼!!!

mark!!

看了下,你这个用法是:只要我数据(数组)长度发生了变化,我就得调用this.pool.total()?
没什么问题,只是感觉怪怪的,这个total方法给我第一感觉是池的初始创建,我还纳闷怎么没获取,原来它就是,感谢分享哦

实际上就是初始化创建