如何使用CocosCreator对象池


Posted in Javascript onApril 14, 2021

前言:

在运行时进行节点的创建( cc.instantiate )和销毁( node.destroy )操作是非常耗费性能的,因此我们在比较复杂的场景中,通常只有在场景初始化逻辑( onLoad )中才会进行节点的创建,在切换场景时才会进行节点的销毁。如果制作有大量敌人或子弹需要反复生成和被消灭的动作类游戏,我们要如何在游戏进行过程中随时创建和销毁节点呢?这里就需要对象池的帮助了。

对象池就是一组可回收的节点对象,我们通过创建cc.NodePool的实例来初始化一种节点的对象池。通常当我们有多个 prefab 需要实例化时,应该为每个 prefab 创建一个 cc.NodePool 实例。

当我们需要创建节点时,向对象池申请一个节点,如果对象池里有空闲的可用节点,就会把节点返回给用户,用户通过node.addChild将这个新节点加入到场景节点树中。
当我们需要销毁节点时,调用对象池实例的put(node) 方法,传入需要销毁的节点实例,对象池会自动完成把节点从场景节点树中移除的操作,然后返回给对象池。

这样就实现了少数节点的循环利用。

具体操作

第一步:准备好 Prefab

把你想要创建的节点事先设置好并做成 Prefab 资源,有些朋友可能不会制作预制体?
(只需要将资源管理器里的资源 拉拽到 层级管理器中 然后 将节点在拉拽回 资源管理器)
这样就完成预制体的创建了!

第二步:初始化对象池

在场景加载的初始化脚本中,我们可以将需要数量的节点创建出来,并放进对象池:

properties: {
    enemyPrefab: cc.Prefab  // 所需要的预制体
},
onLoad: function () {
    //  创建对象池
    this.enemyPool = new cc.NodePool();
    
    let initCount = 5;
    for (let i = 0; i < initCount; ++i) {
        let enemy = cc.instantiate(this.enemyPrefab); // 创建节点
        this.enemyPool.put(enemy); // 通过 put 接口放入对象池
    }
}

对象池里需要的初始节点数量可以根据游戏的需要来控制,即使我们对初始节点数量的预估不准确也不要紧,后面我们会进行处理。

第三步:从对象池请求对象

接下来在我们的运行时代码中就可以用下面的方式来获得对象池中储存的对象了:

createEnemy: function (parentNode) {
    let enemy = null;
    if (this.enemyPool.size() > 0) { // 通过 size 接口判断对象池中是否有空闲的对象
        // get()获取对象
        enemy = this.enemyPool.get();
        
    } else { // 如果没有空闲对象,也就是对象池中备用对象不够时,我们就用 cc.instantiate 重新创建
        enemy = cc.instantiate(this.enemyPrefab);
    }
    enemy.parent = parentNode; // 将生成的敌人加入节点树
    enemy.getComponent('Enemy').init(); //接下来就可以调用 enemy 身上的脚本进行初始化
}

安全使用对象池的要点就是在 get 获取对象之前,永远都要先用 size 来判断是否有可用的对象,如果没有就使用正常创建节点的方法,虽然会消耗一些运行时性能,但总比游戏崩溃要好!另一个选择是直接调用 get ,如果对象池里没有可用的节点,会返回 null ,在这一步进行判断也可以。

第四步:将对象返回对象池

当我们杀死敌人时,需要将敌人节点退还给对象池,以备之后继续循环利用,我们用这样的方法:

onEnemyKilled: function (enemy) {
    // enemy 应该是一个 cc.Node
    this.enemyPool.put(enemy); // 和初始化时的方法一样,将节点放进对象池,这个方法会同时调用节点的 removeFromParent
}

这样我们就完成了一个完整的循环,主角需要刷多少怪都不成问题了!将节点放入和从对象池取出的操作不会带来额外的内存管理开销,因此只要是可能,应该尽量去利用。

第五步:使用组件来处理回收和复用的事件

使用构造函数创建对象池时,可以指定一个组件类型或名称,作为挂载在节点上用于处理节点回收和复用事件的组件。

在创建对象池时可以用:

let menuItemPool = new cc.NodePool('MenuItem');  // 指定一个组件类型

这样当使用 menuItemPool.get() 获取节点后,就会调用 MenuItem 里的 reuse方法,完成点击事件的注册。
当使用menuItemPool.put(menuItemNode)回收节点后,会调用 MenuItem 里的 unuse方法,完成点击事件的反注册。

cc.Class({
    extends: cc.Component,
    onLoad: function () {
        this.node.selected = false;
        this.node.on(cc.Node.EventType.TOUCH_END, this.onSelect, this.node);
    },
    // put() 收回对象池时会调用
    unuse: function () {
        this.node.off(cc.Node.EventType.TOUCH_END, this.onSelect, this.node);
    },
    // get()获取对象池内对象时会调用
    reuse: function () {
        this.node.on(cc.Node.EventType.TOUCH_END, this.onSelect, this.node);
    }
});

另外 cc.NodePool.get() 可以传入任意数量类型的参数,这些参数会被原样传递给 reuse 方法:

// BulletManager.js
let myBulletPool = new cc.NodePool('Bullet'); //创建子弹对象池
let newBullet = myBulletPool.get(this); // 传入 manager 的实例,用于之后在子弹脚本中回收子弹

// Bullet.js
reuse (bulletManager) {
    this.bulletManager = bulletManager; // get 中传入的管理类实例
}
hit () {
    this.bulletManager.put(this.node); // 通过之前传入的管理类实例回收子弹
}

第六步:清除对象池

如果对象池中的节点不再被需要,我们可以手动清空对象池,销毁其中缓存的所有节点:

myPool.clear(); // 调用这个方法就可以清空对象池

当对象池实例不再被任何地方引用时,引擎的垃圾回收系统会自动对对象池中的节点进行销毁和回收。但这个过程的时间点不可控,另外如果其中的节点有被其他地方所引用,也可能会导致内存泄露,所以最好在切换场景或其他不再需要对象池的时候手动调用 clear 方法来清空缓存节点。

以上就是如何使用CocosCreator对象池的详细内容,更多关于CocosCreator对象池的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
模仿百度三维地图的js数据分享
May 12 Javascript
Js 时间间隔计算的函数(间隔天数)
Nov 15 Javascript
javascript 事件处理、鼠标拖动效果实现方法详解
May 11 Javascript
多个jquery.datatable共存,checkbox全选异常的快速解决方法
Dec 10 Javascript
Node.js中require的工作原理浅析
Jun 24 Javascript
JS获取各种宽度、高度的简单介绍
Dec 19 Javascript
谈谈javascript中使用连等赋值操作带来的问题
Nov 26 Javascript
Bootstrap模态对话框的简单使用
Apr 29 Javascript
防止重复发送 Ajax 请求
Feb 15 Javascript
在Vuex使用dispatch和commit来调用mutations的区别详解
Sep 18 Javascript
关于JS模块化的知识点分享
Oct 16 Javascript
vue使用svg文件补充-svg放大缩小操作(使用d3.js)
Sep 22 Javascript
CocosCreator如何实现划过的位置显示纹理
JS监听Esc 键触发事键
Apr 14 #Javascript
jQuery实现影院选座订座效果
利用前端HTML+CSS+JS开发简单的TODOLIST功能(记事本)
JavaScript实现复选框全选功能
js实现上传图片到服务器
Apr 11 #Javascript
Canvas跟随鼠标炫彩小球的实现
Apr 11 #Javascript
You might like
精美漂亮的php分页类代码
2013/04/02 PHP
ThinkPHP3.1数据CURD操作快速入门
2014/06/19 PHP
PHP生成随机密码方法汇总
2015/08/27 PHP
PHP获取本周所有日期或者最近七天所有日期的方法
2018/06/20 PHP
Javascript YUI 读码日记之 YAHOO.util.Dom - Part.2 0
2008/03/22 Javascript
Javascript 圆角div的实现代码
2009/10/15 Javascript
写自已的js类库需要的核心代码
2012/07/16 Javascript
Jquery 改变radio/checkbox选中状态,获取选中的值(示例代码)
2013/12/12 Javascript
JS获取文本框,下拉框,单选框的值的简单实例
2014/02/26 Javascript
跟我学习javascript的浮点数精度
2015/11/16 Javascript
js方法数据验证的简单实例
2016/09/17 Javascript
微信小程序 wxapp内容组件 text详细介绍
2016/10/31 Javascript
AngularJS中一般函数参数传递用法分析
2016/11/22 Javascript
javascript中数组(Array)对象和字符串(String)对象的常用方法总结
2016/12/15 Javascript
angular和BootStrap3实现购物车功能
2017/01/25 Javascript
jQuery ajax调用webservice注意事项
2017/10/08 jQuery
vuejs实现折叠面板展开收缩动画效果
2018/09/06 Javascript
[05:06]TI4西雅图DOTA2前线报道 海涛密探LGD训练
2014/07/09 DOTA
[58:58]2018DOTA2亚洲邀请赛 4.4 淘汰赛 TNC vs VG 第二场
2018/04/05 DOTA
python 类详解及简单实例
2017/03/24 Python
浅谈python函数之作用域(python3.5)
2017/10/27 Python
Python数据结构与算法之完全树与最小堆实例
2017/12/13 Python
Django model反向关联名称的方法
2018/12/15 Python
基于python实现高速视频传输程序
2019/05/05 Python
Flask-WTF表单的使用方法
2019/07/12 Python
html5 冒号分隔符对齐的实现
2019/07/31 HTML / CSS
Lookfantastic香港官网:英国知名美妆购物网站
2018/06/19 全球购物
亚马逊意大利站点:Amazon.it
2020/12/31 全球购物
大学毕业登记表自我鉴定
2013/10/09 职场文书
语文教研活动总结
2014/07/02 职场文书
勿忘国耻9.18演讲稿(经典篇)
2014/09/14 职场文书
2014年医院十一国庆节活动方案
2014/09/15 职场文书
小学运动会报道稿
2014/10/04 职场文书
婚宴致辞
2015/07/28 职场文书
幼儿园托班开学寄语(2016春季)
2015/12/03 职场文书
Vue3.0写自定义指令的简单步骤记录
2021/06/27 Vue.js