[CocosCreator]封装对象池 您所在的位置:网站首页 cocoscreator游戏开发中会用到什么框架 [CocosCreator]封装对象池

[CocosCreator]封装对象池

2024-07-10 16:50| 来源: 网络整理| 查看: 265

对象池的概念(摘自CocosCreator手册介绍)

        对象池就是一组可回收的节点对象,我们通过创建 cc.NodePool 的实例来初始化一种节点的对象池。通常当我们有多个 prefab 需要实例化时,应该为每个 prefab 创建一个 cc.NodePool 实例。当我们需要创建节点时,向对象池申请一个节点,如果对象池里有空闲的可用节点,就会把节点返回给用户,用户通过 node.addChild 将这个新节点加入到场景节点树中。

当我们需要销毁节点时,调用对象池实例的 put(node) 方法,传入需要销毁的节点实例,对象池会自动完成把节点从场景节点树中移除的操作,然后返回给对象池。这样就实现了少数节点的循环利用。假如玩家在一关中要杀死 100 个敌人,但同时出现的敌人不超过 5 个,那我们就只需要生成 5 个节点大小的对象池,然后循环使用就可以了。

但是每次创建都需要写一个新的对象池和脚本对象,我个人觉得比较麻烦,也不方便使用,于是乎我就自己封装了一个对象池基类,如果有新的对象池直接继承这个基类使用即可了,直接上码:

首先考虑对象池的一个特性,是随时全局调用,我立马想到了单例模式,所以首先撸一个单例模式基类,

export default class Singleton { private static _instance: any = null; static getInstance(): T { if (this._instance == null) { this._instance = new this(); } return this._instance; } }

 单例基类有了,我们在封装一个对象池基类,该池基类继承单例:

/** * 对象池基类 * 只要继承本类,并重写前4个变量即可 */ import Singleton from "../../base/Singleton"; export class BasePool extends Singleton { //预制体url 子类实现赋值 prefabUrl: string; //(可选)对象脚本组件 子类实现赋值 script: { prototype: cc.Component } | string; //(可选)初始化池量 子类实现赋值 poolSize: number = 10; //(可选)组件名字 nodeName: string; //(可选)VIP标记 回收判定是否本池实例 MemberFlag: string; private _pool: cc.NodePool; private _prefab: cc.Prefab; private _nodeName: string; /** * 初始化对象池入口 * fixme 注意:由于使用了动态资源加载,会有出现资源加载延迟现象,使用是不能直接链式编程获取对象,等实例化完成在获取对象池 * @param size 初始池量 */ initPool(size?: number) { this.clean(); cc.resources.load(this.prefabUrl, cc.Prefab, (err, prefab: cc.Prefab) => { if (err) { cc.error(err.message); return; } prefab.addRef(); this._prefab = prefab; this._nodeName = prefab.name; this.initPoolSize(size); }); } /** * 获取成员对象 */ getNode() { let obj: cc.Node; if (this._pool.size() > 0) { obj = this._pool.get(); cc.log(`BasePool Tip: 获取${this.MemberFlag}Pool 成员成功,当前池容量剩余:${this._pool.size()}`); } else { obj = this.buildNode(); cc.log(`BasePool Tip: 获取${this.MemberFlag}Pool 成员成功,该池容量不足,产生新成员`); } if (!obj) { throw new Error(`BasePool Tip: 获取${this.MemberFlag}Pool 成员失败`); } return obj; } /** * 回收对象接口 * @param obj 回收对象 */ putNode(obj: cc.Node) { if (this.isPoolMember(obj)) { //回收对象 this._pool.put(obj); cc.log(`BasePool Tip: ${this.MemberFlag}Pool回收对象成功,当前池容量提升至:${this._pool.size()}`); } else { if (obj && obj.name) { cc.warn(`BasePool Tip: ${this.MemberFlag}Pool回收对象错误,${obj.name}并该对象池成员,回收失败`); } else { throw new Error(`BasePool Tip: 获取${this.MemberFlag}Pool回收对象错误,对象不存在`); } } } /** * 清空对象池 */ clean() { if (this._pool) { this._pool.clear(); cc.log(`BasePool Tip: 清空${this.MemberFlag}Pool成功,当前池容量:${this._pool.size()}`); } //释放预制体组件资源 if (this._prefab) { this._prefab.decRef(); this._prefab = null; cc.log(`BasePool Tip: 释放${this.MemberFlag}Pool 资源数据`); } } /** * 初始化对象池 * @param size 数量 */ private initPoolSize(size?: number) { if (size > 0) { this.poolSize = size; } //对象池 if (this._prefab) { if (!this.MemberFlag) { this.MemberFlag = this.constructor.name; } if (this.script) { this._pool = new cc.NodePool(this.script); } else { this._pool = new cc.NodePool(); } for (let i = 0; i < this.poolSize; i++) { let obj = this.buildNode(); this._pool.put(obj); } cc.log(`BasePool Tip: 初始化对象池(${this.MemberFlag}Pool)完成,初始容量为:${this._pool.size()}`); } else { cc.error(`BasePool Tip: ${this.MemberFlag}pool 初始化失败`) } } /** * 实例化对象并标记为VIP */ private buildNode(): cc.Node { let node: cc.Node = cc.instantiate(this._prefab); node["memberFlag"] = this.MemberFlag; if (this.nodeName) { node.name = this.nodeName; } return node; } /** * 判定是否VIP * @param node 回收对象 */ private isPoolMember(node: cc.Node): boolean { return node instanceof cc.Node && this.MemberFlag == node["memberFlag"]; } /** * 获取组件名字 */ getNodeName(): string { return this.nodeName ? this.nodeName : this._nodeName; } }

核心代码很简单,我也在没个函数和参数上都加满了注释,这里我就过多介绍,接下来我们看实战代码

import ResourcesConfig from "../../common/ResourcesConfig"; import {BasePool} from "./BasePool"; import PKInjury from "../model/PKInjury"; /** * 战斗扣血对象池 */ export default class PKInjuryPool extends BasePool { private constructor() { super(); //必须赋值(预制体路径) this.prefabUrl = ResourcesConfig.Prefab_PKInjury_Common; //可选 this.MemberFlag = "PKInjury"; this.script = PKInjury; this.nodeName = "PKInjury"; // this.poolSize = 15;//默认为10 } //重写抽象类的方法 static get Instance(): PKInjuryPool { return super.getInstance(); } }

实现复用对象看起来也非常简单,只需要重写构造函数的属性,和单例的抽象方法即可,实用性还是非常强的,非常好用,小伙伴们还有更好的意见么,可以留言一起探讨哦~

                                                             (老规矩:广告位留给自己)

        欢迎喜欢或者从事CocosCreator开发的小伙伴请加入我的大家庭CocosCreator游戏开发Q群:26855530 

使用时:有一点需要注意

PKInjuryPool.Instance.initPool();//初始化扣血对象池 ------对象池初始化时,必须要提前,这里我在代码有说明----- let injuryNode: cc.Node = PKInjuryPool.Instance.getNode();//复用 PKInjuryPool.Instance.putNode(injuryNode);//回收 PKInjuryPool.Instance.clean();//清空扣血对象池

本期内容就到此为止了,原创不易,小伙伴们,记得点赞收藏给个小鼓励哦,下期见!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有