参考:
deepseek
Laya对象池源码
Cocos对象池源码
对象池是预先创建并管理可重复使用的对象,适用于频繁创建和销毁同类对象的场景。
优点:
1. 避免频繁创建和销毁引起内存分配、垃圾回收等消耗。
2. 避免垃圾回收引起的帧率波动、卡顿。
3. 对象已初始化可直接使用。
场景:
1. 子弹、粒子效果、角色NPC敌人、特效等等。
2. 任何需要频繁创建和销毁的游戏对象。
由上面可知,对象池需要如下几个功能,但是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是创建对象是用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.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();
浙公网安备 33010602011771号