如何使用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 相关文章推荐
javascript 导出数据到Excel(处理table中的元素)
Dec 18 Javascript
Javascript实现单张图片浏览
Dec 18 Javascript
Node.js开发之访问Redis数据库教程
Jan 14 Javascript
DOM基础教程之事件对象
Jan 20 Javascript
JS中产生标识符方式的演变
Jun 12 Javascript
JS实现弹出浮动窗口(支持鼠标拖动和关闭)实例详解
Aug 06 Javascript
jQuery+css实现的换页标签栏效果
Jan 27 Javascript
json传值以及ajax接收详解
May 24 Javascript
Bootstrap媒体对象学习使用
Mar 07 Javascript
解决BootStrap Fileinput手机图片上传显示旋转问题
Jun 01 Javascript
使用veloticy-ui生成文字动画效果
Feb 08 Javascript
详解Bootstrap 学习(一)入门
Apr 12 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 Stream_*系列函数
2010/08/01 PHP
用Simple Excel导出xls实现方法
2012/12/06 PHP
深入理解javascript动态插入技术
2013/11/12 Javascript
使用js写的一个简易的投票
2013/11/27 Javascript
javascript实例分享---具有立体效果的图片特效
2014/06/08 Javascript
JQuery报错Uncaught TypeError: Illegal invocation的处理方法
2015/03/13 Javascript
jQuery中attr()与prop()函数用法实例详解(附用法区别)
2015/12/29 Javascript
Vue组件实例间的直接访问实现代码
2017/08/20 Javascript
Vue 多层组件嵌套二种实现方式(测试实例)
2017/09/08 Javascript
详解vue mint-ui源码解析之loadmore组件
2017/10/11 Javascript
js 提取某()特殊字符串长度的实例
2017/12/06 Javascript
JS实现获取自定义属性data值的方法示例
2018/12/19 Javascript
JavaScript时间日期操作实例小结【5个示例】
2018/12/22 Javascript
使用layui日期控件laydate对开始和结束时间进行联动控制的方法
2019/09/06 Javascript
JS实现滑动拼图验证功能完整示例
2020/03/29 Javascript
JavaScript实现随机点名小程序
2020/10/29 Javascript
Python实现的数据结构与算法之基本搜索详解
2015/04/22 Python
名片管理系统python版
2018/01/11 Python
python 定义n个变量方法 (变量声明自动化)
2018/11/10 Python
python实现简单图片物体标注工具
2019/03/18 Python
python 将字符串完成特定的向右移动方法
2019/06/11 Python
Python实现制度转换(货币,温度,长度)
2019/07/14 Python
python3 实现函数写文件路径的正确方法
2019/11/27 Python
python 字典访问的三种方法小结
2019/12/05 Python
Pyspark读取parquet数据过程解析
2020/03/27 Python
浅谈keras中的后端backend及其相关函数(K.prod,K.cast)
2020/06/29 Python
可自定义箭头样式的CSS3气泡提示框
2016/03/16 HTML / CSS
利用CSS3实现文字折纸效果实例代码
2018/07/10 HTML / CSS
美国精品家居用品网站:US-Mattress
2016/08/24 全球购物
POP文化和音乐灵感的时尚:Hot Topic
2019/06/19 全球购物
指针和引用有什么区别
2013/01/13 面试题
大学生最常用的自我评价
2013/12/07 职场文书
幼儿园小班教学反思
2014/02/02 职场文书
MySQL中存储时间的最佳实践指南
2021/07/01 MySQL
Tomcat项目启动失败的原因和解决办法
2022/04/20 Servers
sql查询语句之平均分、最高最低分及排序语句
2022/05/30 MySQL