深圳建站公司招聘,如何用文档做网站,南通制作网站的有哪些公司吗,it人力外包服务版本#xff1a; cocosCreator 3.4.0
语言#xff1a; TypeScript
环境#xff1a; Mac NodePool 在项目中频繁的使用instantiate和node.destory对性能有很大的耗费#xff0c;比如飞机射击中的子弹使用和销毁。
因此官方提供了NodePool#xff0c;它被作为管理节点对象…版本 cocosCreator 3.4.0
语言 TypeScript
环境 Mac NodePool 在项目中频繁的使用instantiate和node.destory对性能有很大的耗费比如飞机射击中的子弹使用和销毁。
因此官方提供了NodePool它被作为管理节点对象的缓存池使用。定义如下
export class NodePool {// 缓冲池处理组件用于节点的回收和复用逻辑这个属性可以是组件类名或组件的构造函数poolHandlerComp?: ConstructorIPoolHandlerComponent | string;// 构造函数可传递组件或名称用于处理节点的复用和回收constructor(poolHandlerComp?: ConstructorIPoolHandlerComponent | string);// 获取当前缓冲池的可用对象数量size(): number;// 销毁对象池中缓存的所有节点clear(): void;/*func: 向缓冲池中存入一个不再需要的节点对象param: 回收的目标节点注意1. 该函数会自动将目标节点从父节点上移除但是不会进行 cleanup 操作2. 如果存在poolHandlerComp组件和函数会自动调用 unuse函数*/put(obj: Node): void;/*func: 获取对象池中的对象如果对象池没有可用对象则返回空param: 如果组件和函数存在会向 poolHandlerComp 中的 reuse 函数传递的参数*/get(...args: any[]): Node | null;
}接口汇总
接口说明new()创建对象池put()将节点放到对象池中get()从对象池中获取节点size()获取对象池中对象的数目clear()销毁对象池中的所有对象
使用NodePool的大概思路
通过NodePool创建对象池获取节点时可先检测对象池的数目如果 0 则克隆节点并放到对象池中如果 0 则从对象池中获取节点不使用的时候如果没有对象池则调用node.destory否则将节点放到对象池中
在对象池中有个get(...args: any[])的方法方法参数的使用主要针对于对象池创建时添加了可选参数。
以飞机射击中子弹的构建为目的看下关于对象池的使用示例相关
// GameManager.ts 游戏管理类
import { BulletItem } from ../bullet/BulletItem;ccclass(GameManager)
export class GameManager extends Component {property(Prefab) bullet: Prefab null; // 子弹预制体private _bulletPool: NodePool null; // 子弹对象池onLoad() {// 创建子弹对象池this._bulletPool new NodePool();}// 创建玩家子弹private createPlayerBullet() {// 获取子弹节点const bulletNode this.getBulletNode();const bulletItem bulletNode.getComponent(BulletItem);// 此处将子弹对象池传入子弹对象脚本bulletItem.init(this._bulletPool);}// 获取子弹节点private getBulletNode(): Node {const size this._bulletPool.size();if (size 0) {// 克隆子弹节点const bulletNode instantiate(this.bullet);// 将子弹节点添加到对象池中this._bulletPool.put(bulletNode);}// 从对象池中获取节点return this._bulletPool.get();}onDestroy() {// 销毁对象池this._bulletPool.clear();}
}// BulletItem.ts 子弹对象组件脚本
export class BulletItem extends Component {private _bulletPool: NodePool null;public init(bulletPool: NodePool) {this._bulletPool bulletPool;}private destroyBullet() {// 检测是否存在对象池如果存在则将对象放到对象池中否则销毁if (this._bulletPool) {this._bulletPool.put(this.node);}else {this.node.destory();}}
}如上例子简单演示了下NodePool对象池的使用但需要注意
最好存储同类型的节点方便管理注意检测对象池内的对象数目或通过get获取对象后进行安全判定避免null注意对象池对象的释放 构造函数的可选参数
在上面的定义文件中针对于对象池的构建有着可选参数的支持代码如下
// 缓冲池处理组件用于节点的回收和复用逻辑这个属性可以是组件类名或组件的构造函数
poolHandlerComp?: ConstructorIPoolHandlerComponent | string;
// 构造函数可传递组件或名称用于处理节点的复用和回收事件逻辑
constructor(poolHandlerComp?: ConstructorIPoolHandlerComponent | string);可选参数的支持主要有两种形式
string字符串形式IPoolHandlerComponent 缓存池处理组件形式
对于这两种形式其本质就是增加了对对象池中对象的自定义逻辑处理以组件为参数看下它的定义
export interface IPoolHandlerComponent extends Component {// 在对象被放入对象池的时候进行调用unuse(): void;// 从对象池中获取对象的时候被调用reuse(args: any): void;
}这两个方法的调用看下源码的实现
// 来源于 node-pool.ts
export class NodePool {// 向对象缓存池存入不需要的对象public put (obj: Node) {if (obj this._pool.indexOf(obj) -1) {// 从父节点移除但并不cleanupobj.removeFromParent();// 获取组件poolHandlerComp并检测是否存在 unuse方法如果存在则调用const handler this.poolHandlerComp?obj.getComponent(this.poolHandlerComp):null;if (handler handler.unuse) {handler.unuse();}this._pool.push(obj);}}// 获取对象池中的对象public get (...args: any[]): Node | null {// 检测对象池中是否有对象const last this._pool.length - 1;if (last 0) {return null;} else {// 将对象从缓存池中取出const obj this._pool[last];this._pool.length last;// 获取组件poolHandlerComp并检测是否存在reuse方法如果存在则调用const handlerthis.poolHandlerComp?obj.getComponent(this.poolHandlerComp):null;if (handler handler.reuse) {handler.reuse(arguments);}return obj;}}
}上面的代码有助于对两个方法的调用时机增加一些了解。
下面我们依然以飞机的子弹构建为例代码增加一些拓展用于支持对象池的自定义逻辑处理。
// GameManager.ts 游戏管理类
import { BulletItem } from ../bullet/BulletItem;ccclass(GameManager)
export class GameManager extends Component { onLoad() {// 创建子弹对象池, 参数设定为子弹类的名字this._bulletPool new NodePool(BulletItem);}private getBulletNodePool() {const size this._bulletPool.size();if (size 0) {const bulletNode instantiate(this.bullet_1);this._bulletPool.put(bulletNode);}// 获取子弹节点时可以设置自定义的参数相关return this._bulletPool.get();}
}// BulletItem.ts 子弹对象组件脚本增加
export class BulletItem extends Component implements IPoolHandlerComponent {unuse(): void {console.log(------ 调用了组件的 unuse 方法);}reuse(args: any): void {console.log(------ 调用了组件的 reuse 方法);}
}增加对对象的自定义逻辑处理其要点就是
构建对象池时需要添加可选参数参数的名字或组件一定要是对象的脚本组件相关对象的组件脚本类需要增加implements IPoolHandlerComponent 的实现也就是unuse和reuse方法根据情况自定义设定NodePool.get的参数相关
到这里关于NodePool的基本使用介绍完毕。 NodePool管理器 在上面的例子中关于对象池的使用存在着几个问题 从对象池获取对象和将对象放入对象池的调用在不同的脚本文件中可能会出现维护比较困难的问题 对象池的构建不仅针对于子弹而且可能还有敌机道具等可能会出现多个对象池且代码重复的问题。
因此我们可构建一个对象池的管理类来统一管理多个不同的对象池类似于cocos2d-x中的PoolManager。
大致的属性和接口是
属性或方法说明_nodePoolMap保存所有对象池的容器结构是Map对象池名, NodePool池getNodePoolByName():NodePool通过名字从容器中获取对象池如果没有则创建如果存在则获取getNodeFromPool():Node通过名字从对象池中获取节点如果没有则克隆如果存在则获取putNodeToPool()将节点放入对象池中clearNodePoolByName()通过名字将对象池从容器中移除clearAll()移除容器中的所有对象池
该类使用的是单例模式详细的代码如下
// 对象池管理器
import { _decorator, Component, instantiate, NodePool, Prefab} from cc;
const { ccclass } _decorator;export class NodePoolManager {private static _instance: NodePoolManager null;private _nodePoolMap: Mapstring, NodePool null;static get instance() {if (this._instance) {return this._instance;}this._instance new NodePoolManager();return this._instance;}constructor() {this._nodePoolMap new Mapstring, NodePool();}/*func 通过对象池名字从容器中获取对象池param name 对象池名字return 对象池*/private getNodePoolByName(name: string): NodePool {if (!this._nodePoolMap.has(name)) {let nodePool new NodePool(name);this._nodePoolMap.set(name, nodePool);}let nodePool this._nodePoolMap.get(name);return nodePool;}/*func 通过对象池名字从对象池中获取节点param name 对象池名字param prefab 可选参数对象预制体return 对象池中节点 */public getNodeFromPool(name: string, prefab?: Prefab): Node | null {let nodePool this.getNodePoolByName(name);const poolSize nodePool.size();if (poolSize 0) {let node instantiate(prefab);nodePool.put(node);}return nodePool.get();}/*func 将节点放入对象池中param name 对象池名字param node 节点*/public putNodeToPool(name: string, node: Node) {let nodePool this.getNodePoolByName(name);nodePool.put(node);}// 通过名字将对象池从容器中移除public clearNodePoolByName(name: string) {// 销毁对象池中对象let nodePool this.getNodePoolByName(name);nodePool.clear();// 删除容器元素this._nodePoolMap.delete(name);}// 移除所有对象池public clearAll() {this._nodePoolMap.forEach((value: NodePool, key: string) {value.clear();});this._nodePoolMap.clear();}static destoryInstance() {this._instance null;}
}测试示例
// GameManager.ts
const BULLET_POOL_NAME BulletItem // 子弹内存池// 创建玩家子弹
private createPlayerBullet() {// 获取子弹节点参数节点名子弹预制体const poolManager NodePoolManager.instance;const bulletNode poolManager.getNodeFromPool(BULLET_POOL_NAME, this.bulletPrefab);bulletNode.parent this.bulletRoot;
}// BulletItem.ts
private destroyBullet() {// 检测是否存在对象池如果存在则将对象放到对象池中否则销毁if (this._bulletPool) {//this._bulletPool.put(this.node);const poolManager NodePoolManager.instance;poolManager.putNodeToPool(BULLET_POOL_NAME, this.node);}else {this.node.destory();}
}管理类中有个接口叫做getNodeFromPool(name: string, prefab?: Prefab)第二个参数也可以为prefabName然后通过resource.load进行动态加载类似实现
public getNodeFromPool(name: string, prefabName?: string): Node | null {let nodePool this.getNodePoolByName(name);const poolSize nodePool.size();if (poolSize 0) {const url prefab/ prefabName;resources.load(url, (err, prefab) {if (err) {return console.err(getNodeFromPool resourceload failed: err.message);}let node instantiate(prefab);nodePool.put(node);});}return nodePool.get();
}因resouces.load属于异步操作可能会出现代码未加载完成就获取的问题因此可使用异步编程
public getNodeFromPool(name: string, prefabName?: string): PromiseNode | null {return new PromiseNode | null((resolve, reject) {let nodePool this.getNodePoolByName(name);const poolSize nodePool.size();if (poolSize 0) {const url prefab/ prefabName;resources.load(url, (err, prefab) {if (err) {console.error(getNodeFromPool resourceload failed: err.message);reject(err);} else {let node instantiate(prefab);nodePool.put(node);resolve(nodePool.get());}});} else {resolve(nodePool.get());}});
}关于一些TypeScript的语法相关可参考博客
TypeScript 之 Map
TypeScript 之 异步编程
因工作的某些缘故可能对NodePool的理解及编写示例有所不当请不吝赐教感激不尽
最后祝大家学习生活愉快!