aircraft-war(二)

aircraft-war(二)

双弹道子弹

双弹道子弹和弹道子弹差别并不大,实现起来也不难。先来找张图分析一下:


image.png

Hero的锚点处于中心位置,两颗子弹分别位于左右两边1/3处,也就是说子弹的分为位于Hero原点的X轴{left: -width/3, right: width/3}位置。剩下的和单发子弹没有区别。
脚本修改如下:
bulletGroup

cc.Class({
    extends: cc.Component,

    properties: {
        bullet: cc.Prefab,
        hero: cc.Node,
        rate: cc.Integer,
        bulletCount: {
            default: 10,
            type: cc.Integer
        },
        bulletBlue: cc.Prefab,
    },

    onLoad: function () {
        // this.genBulletPool();
        this.genBulletBluePool();
        this.schedule(function () {
            this.startShoot(this.bulletBluePool, [this.hero.width / 3, -this.hero.width / 3])
        }.bind(this), this.rate);
    },

    genBulletPool: function () {
        this.bulletPool = new cc.NodePool();
        for (let i = 0; i < this.bulletCount; ++i) {
            let newBullet = cc.instantiate(this.bullet); // 创建节点
            this.bulletPool.put(newBullet); // 通过 putInPool 接口放入对象池
        }
    },
    genBulletBluePool: function () {
        this.bulletBluePool = new cc.NodePool();
        for (let i = 0; i < this.bulletCount; ++i) {
            let newBullet = cc.instantiate(this.bulletBlue); // 创建节点
            this.bulletBluePool.put(newBullet); // 通过 putInPool 接口放入对象池
        }
    },
    //获取子弹位置
    getBulletPosition: function(x){
        let heroP = this.hero.getPosition();
        let newV2_x = heroP.x + x;
        let newV2_y = heroP.y;
        return cc.p(newV2_x, newV2_y);
    },
    startShoot: function (pool, posOfHero) {
        let newNode = null;
        if (pool.size() > 0) {
            for(var i = 0; i < posOfHero.length; i++) {
                newNode = pool.get();
                this.node.addChild(newNode);
                let p = this.getBulletPosition(posOfHero[i]);
                newNode.setPosition(p);
                newNode.getComponent('bullet').bulletGroup = this;
            }
        }
    },
    //销毁子弹
    destroyBullet: function (bullet) {
    }

    // called every frame, uncomment this function to activate update callback
    // update: function (dt) {

    // },
});

bullet:

cc.Class({
    extends: cc.Component,

    properties: {
        speed: cc.Integer,
    },

    // use this for initialization
    onLoad: function () {

    },
    // called every frame, uncomment this function to activate update callback
    update: function (dt) {
        this.node.y += dt * this.speed;
        if (this.node.y > this.node.parent.height){
            this.bulletGroup.bulletBluePool.put(this.node);
        }
    },
});
image.png

好了,目前两种类型的子弹已经实现了功能,但这只是第一步而已。Cocos Creator提供的属性检查器并没有被充分的用到,而且从“前端工程师”的角度来讲,还远远未到组件化。
所以接下来要做的,是进行“解耦”和“封装”。(感谢提供源代码的A123asdo11大神,他的代码写的非常棒,很值得学习,这是项目源码

把都需要用到的抽象出来

先来回顾一下之前的代码,发现两种类型的子弹,可以把属性的设置也抽象出来充分利用属性检查器。两种类型的子弹,位置、Prefab,持续时间,对象池所需数量都不一样,所以把这些通过继承的方式作为类抽离出来。
先按照之前写脚本的逻辑顺序,首先把生成节点、生成对象池的方法抽离出来,接着是发射那块创建节点的方法,还有子弹销毁节点,这都是很多地方需要重复用到的,都需要抽象出来。
首先要创建一个用来放这些抽象函数的地方,然后还需要一个全局变量,作为传递的媒介。看这里
在资源选择器中创建global脚本:

// declare global variable "D"
window.D = {
    // singletons
    common: null, //公共方法
    commonConstant: null, //定义的一些常量
};

接着按照之前的开发逻辑流程,来改造bulletGroup脚本:

// 子弹生成的位置
const bulletPosition = cc.Class({
    name: 'bulletPosition',
    properties: {
        positionX: {
            default: '',
            tooltip: '子弹相对Hero的位置'
        }
    }
});

// 无限时长子弹
const infiniteBullet = cc.Class({
    name: 'infiniteBullet',
    properties: {
        name: '',
        rate: 0,
        poolAmount: 0,
        prefab: cc.Prefab,
        position: {
            default: [],
            type: bulletPosition,
            tooltip: '子弹位置'
        }
    }
});

// 有限时长子弹
const finiteBullet = cc.Class({
    extends: infiniteBullet,
    name: 'finiteBullet',
    properties: {
        duration: 0,
    }
});


// 有限时长子弹
cc.Class({
    extends: cc.Component,

    properties:() => ({
        infiniteBullet: {
            default: null,
            type: infiniteBullet,
            tooltip: '无限子弹'
        },
        finiteBullet: {
            default: [],
            type: finiteBullet,
            tooltip: '有限子弹'
        },
        hero: cc.Node,
    }),

    onLoad: function () {
        // 初始化对象池
        D.common.initNodePool(this, this.infiniteBullet);
        D.common.batchInitNodePool(this, this.finiteBullet);
        this.startAction();
    },
    // 发射子弹,定时器
    startAction: function () {
        this.startShoot = function () {
            this.genNewBullet(this.infiniteBullet);
        }.bind(this);
        // 定时器 发射子弹的就是创建子弹对象
        this.schedule(this.startShoot, this.infiniteBullet.rate);
    },
    // 生成子弹
    genNewBullet: function (bulletInfo) {
        let poolName = bulletInfo.name + 'Pool';
        for(let i = 0; i < bulletInfo.position.length; i++) {
            let newNode = D.common.genNewNode(this[poolName], bulletInfo.prefab, this.node);
            let pos = this.getBulletPosition(bulletInfo.position[i].positionX);
            newNode.setPosition(pos);
            // 这是个很基础的知识点,需要小心!
            // newNode.getComponent('bullet') 找到的是bullet脚本组件
            // 这里将this传进去,是为了下面bullet中调用destroyBullet方法时,this对象不变
            // 如果:newNode.getComponent('bullet').died = this.destroyBullet;
            // 那么只是将这个方法传给了bullet,当在bullet中使用该函数,函数的当前上下文就变了
            // 可以试试在 destroyBullet 中打印一下 this 看看当前的上下文
            newNode.getComponent('bullet').bulletGroup = this;
        }
    },
    //获取子弹位置
    getBulletPosition: function(positionStr){
        let heroP = this.hero.getPosition();
        let newV2_x = heroP.x + eval(positionStr);
        let newV2_y = heroP.y;
        return cc.p(newV2_x, newV2_y);
    },
    //销毁子弹
    destroyBullet: function (node) {
        // bullet中是由bulletGroup调用,所以当前this为bulletGroup
        D.common.putBackPool(this, node);
    }

    // called every frame, uncomment this function to activate update callback
    // update: function (dt) {

    // },
});

下面来展开这部分的代码:
首先需要看一下CCClass的进阶参考,了解一下属性参数继承,然后先别急往下看,因为这里的代码变动比较大,涉及的知识与变动也比较多。
接着看一下那个存放公共函数的脚本,在资源选择器中创建common脚本:

cc.Class({
    extends: cc.Component,

    properties: {

    },

    // use this for initialization
    onLoad: function () {
        D.common = this;
    },
    // 批处理对象池
    batchInitNodePool: function (that, objArray) {
        for(let i=0; i< objArray.length; i++) {
            let objInfo = objArray[i];
            this.initNodePool(that, objInfo);
        }
    },
    // 初始化对象池
    initNodePool: function (that, objInfo) {
        let name = objInfo.name;
        let poolName = name + 'Pool';
        that[poolName] = new cc.NodePool();
        // 创建对象,并放入池中
        for (let i = 0; i < objInfo.poolAmount; i++) {
            let newNode = cc.instantiate(objInfo.prefab);
            that[poolName].put(newNode);
        }
    },

    // 生成节点
    genNewNode: function (pool, prefab, nodeParent) {
        let newNode = null;
        if (pool.size() > 0) { // 通过 size 接口判断对象池中是否有空闲的对象
            newNode = pool.get();
        } else { // 如果没有空闲对象,也就是对象池中备用对象不够时,就用 cc.instantiate 重新创建
            newNode = cc.instantiate(prefab);
        }
        nodeParent.addChild(newNode);
        return newNode;
    },

    // 销毁节点
    putBackPool: function (that, node) {
        let poolName = node.name + "Pool";
        that[poolName].put(node);
    }

    // called every frame, uncomment this function to activate update callback
    // update: function (dt) {

    // },
});
image.png

Prefab的名称要通过属性检查器修改:


image.png

这部分代码需要搞懂,慢一点没什么关系。这部分的源码放在这里

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容