aircraft-war(二)

aircraft-war(二)

双弹道子弹

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


aircraft-war(二)_第1张图片
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);
        }
    },
});
aircraft-war(二)_第2张图片
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) {

    // },
});
aircraft-war(二)_第3张图片
image.png

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


aircraft-war(二)_第4张图片
image.png

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

你可能感兴趣的:(aircraft-war(二))