07 - 飞机大战开发Day3

今天继续完善飞机大战项目,今天的工作量如下:

  • 实现按照波次同时刷新多种敌人
  • 飞机子弹的碰撞系统

实现按照波次同时刷新多种敌人

准备多种敌人

首先要准备敌人的数据,即enemyData.json

// enemyData.json
[
    {
        "id": 2001,
        "name": "normalEnemy",
        "mainImgPath": "enemy10",
        "hp": 5
    },
    {
        "id": 2002,
        "name": "normalEnemy",
        "mainImgPath": "enemy11",
        "hp": 10
    }
]

该数据文件指定了敌人的id,名称(种类),图片路径和血量。为了能够顺利读取该数据文件,别忘了修改DataManager.js

然后要写逻辑,目前还没区分敌人种类,所以它们的逻辑都是在NormalEnemy.js中的:

// NormalEnemy.js
// 初始化血量
setHp: function(hp) {
    this.hp = hp;
    this.hpLabel.string = this.hp;
}
// 初始化贴图和碰撞箱
setSpriteFrame: function (spriteFrame) {
    if (spriteFrame instanceof cc.SpriteFrame) {
        // 设置贴图
        let sprite = this.node.getComponent(cc.Sprite);
        sprite.spriteFrame = spriteFrame;
        // 设置碰撞箱
        let collider = this.node.getComponent(cc.BoxCollider);
        collider.size.width = this.node.width;
        collider.size.height = this.node.height;
    } else {
        console.log('[NormalEnemy]: 贴图设置错误! (不是cc.SpriteFrame类型)');
    }
}
// 受伤了更新血量
hurt: function (hp) {
    this.hp -= hp;
    if (this.hp <= 0) {
        this.node.removeFromParent();
    } else {
        this.hpLabel.string = this.hp;
    }
}

最后在EnemyManager.js中实现创建单个敌人的逻辑:

// EnemyManager.js
createEnemy: function (enemyData) {
    let enemyNode = cc.instantiate(this.enemyPrefab[0])
    enemyNode.parent = this.node;

    // Todo: 敌人分类
    if (enemyData.name === 'normalEnemy') {
        let enemyJs = enemyNode.getComponent('NormalEnemy');
        let mainSpriteFrame = this.resMgr.getSpriteFrameByName(enemyData.mainImgPath);
        enemyJs.setSpriteFrame(mainSpriteFrame);
        enemyJs.setHp(enemyData.hp);
        enemyJs.move();
    }
}

准备多种波次

可在关卡数据levelData.json中添加波次信息:

// levelData.json
{
    "id": 1001,
    "name": "Level1",
    "bgPath": "level_1",
    "waves": [
        {
            "enemyId": [
                2001,
                2002
            ],
            "repeat": [
                8,
                4
            ],
            "interval": [
                3,
                2
            ],
            "delay": 10
        },
        {
            "enemyId": [
                2002
            ],
            "repeat": [
                3
            ],
            "interval": [
                1
            ],
            "delay": 3
        }
    ]
}

其中,waves数组中每一个元素代表一波中出现的敌人,包含敌人们的id、个数repeat、出生间隔interval和波次间的等待时间delay

接下来就能在EnemyManager.js中编写解析这些信息的逻辑了:

// EnemyManager.js
scheduleWaves: function () {
    let interval = 0, delay = 0.1, repeat = 0, id = '', enemyData = {};
    let maxDelay = -1;
    let debugWaveCnt = 1;
    for (let wave of this.wavesData) {
        console.log('Wave ' + debugWaveCnt + ': ');
        console.log(wave);
        for (let i = 0; i < wave.enemyId.length; ++i) {
            interval = wave.interval[i];
            repeat = (wave.repeat[i] - 1 < 0) ? 0 : (wave.repeat[i] - 1);
            id = wave.enemyId[i];
            enemyData = this.dataMgr.getEnemyDataById(id);

            // todo: 按波次安排敌人刷新

            maxDelay = Math.max(interval * repeat, maxDelay);
            delay += 0.5;
        }
        debugWaveCnt++;
        delay += (wave.delay + maxDelay);
    }
}

实现按波次安排敌人刷新

需要用到Cocos2D提供的schedule()接口:

// EnemyManaget.js
// todo: 按波次安排敌人刷新
let createEnemyForScheduleArr = [];

createEnemyForScheduleArr.push((enemyData) => {
    const data = enemyData; // 创建一个独有变量, 非常好GPT
    return (() => {
        // console.log(data);
        this.createEnemy(data);
    });
});

console.log('\ncreateEnemyForSchedule(' + id + ')\ninterval: ' + interval + ', repeat:' + repeat + ', delay: ' + delay);
this.schedule(createEnemyForScheduleArr[i](enemyData), interval, repeat, delay);

这里用到了闭包函数(感谢GPT的帮助),由于schedule()接口的回调函数不允许有参数,而想要创建敌人还需传入它的信息enemyData,于是便能用闭包函数和箭头函数这两个知识点去“生成”符合条件的回调函数。这里会生成一个没有参数的函数,它会利用闭包内的敌人信息来生成敌人。

飞机子弹的碰撞系统

和之前小鸟项目一样,就不说了。

让子弹/敌人消失的函数:self/this.node.removeFromParent();

明天干什么

可能进一步拓展游戏功能,例如玩家飞机种类选择,飞机&子弹进化;敌人种类增加,Boss战等。