CreatorPrimer | 飞机大战(三)

《飞机大战(一)》介绍摄像机实现地图的滚动和子弹组件的设计;在此基础上《飞机大战(二)》增加了子弹的角度直线发射以及动态角度更新能力,用于实现如:散弹、螺旋扫射等华丽的子弹表现。

本次教程分享的是 Cocos Creator 引擎碰撞检测系统,使玩家的子弹能够击中敌人,让我们的游戏可以真正玩起来!

子弹预制件

子弹发射器 LineEmitter,用于控制子弹的飞行路径,子弹自身的表现则由预制体 Bullet 呈现,看下图:

Bullet 预制体设计了两层,其中 image 节点是子弹的纹理图片,为什么没有直接在 Bullet 节点上挂 Sprite 呢?

这里考虑的是子弹有可能是静态图片,还有可能是动画序列帧(比如带雷电属性的子弹、火焰喷射器),为了增强灵活性,因此将子弹的表现放在了 Bullet 节点的内部。

子弹组件

在 Bullet 子弹预制体上挂载一个同名的 Bullet 组件脚本,

  1. 设置子弹的伤害属性,
  2. 监听碰撞事件做相应的处理

看下面代码:

let Bullet = cc.Class({
    extends: cc.Component,
    properties: {
        damage: 1,
    },

    onLoad() {
        //穿透力
        this.penetrate = 0;
    },

    onCollisionEnter(other, self) {
        //如果子弹有穿透效果,可以在此控制
        this.penetrate--;
        if (this.penetrate <= 0) {
            //销毁节点
            this.node.destroy();
        }
    },
});

module.exports = Bullet;

子弹组件脚本主要是控制子弹在碰撞产生时的表现:销毁节点。

在游戏的制作过程中,Shawn 又为子弹添加了穿透的能力,因此增加了一个 penetrate 的内部属性,用于控制子弹可穿透几次。

敌机预制件

敌机预制体编辑与子弹相仿,看下图:

敌机

下面说明一下敌机预制体的设计思路:

  1. 在 Enemy 内部放入一个 image 节点,用于显示飞机的外型,有可能飞机的呈现不仅仅是一张静态图片,很有可能是一组动画,为了灵活扩展,所以没有将Sprite直接挂载到Enemy上。
  2. 同时飞机还带有一个血条,使用同名的 Enemy 组件监听碰撞、控制内部的血条组件显示。
  3. Enemy节点上挂载碰撞组件,你需要根据 image 节点的外型选择是使用矩形碰撞组件还是圆形碰撞组件,不建议使用多边形碰撞组件。
    【视频】
  4. 飞机在飞行过程中并非是简单的垂直从上往下,而是会根据事先编辑的路径做曲线运动,UpdateRotation组件就是用于更新飞机自身的角度,让它的头部始终朝向前进的路线。

需要注意,不建议使用多边形碰撞组件,是因为Shawn在实践中发现凹多边型碰撞检测不精确,碰撞回调不能正确响应,看下图:
image.png

视频中以激光弹演示,子弹攻击到凹陷处不产生伤害,凸出处可以看到敌机持续消耗HP

具体原因还未深入研究,如果您对引有所见解或更好的方案,欢迎留言讨论。

敌机组件

下面是 Enemy 组件碰撞处理的主要代码:

cc.Class({
    extends: cc.Component,
    properties: {
        maxHP: {...}, //使用属性控制血条UI
        _hp: 0,
        hp: {...},    //使用属性控制血条UI
    },

    start() {
        this._collider = this.node.getComponent(cc.Collider);
    },

    _updateHpBar() {...},
    //监听碰撞
    onCollisionEnter(other, self) {
        //检测碰撞对象是否为子弹
        let bullet = other.getComponent('Bullet');
        if (bullet) {
            //使用子弹伤害减去当前hp
            this.hp -= bullet.damage; 
            //hp小于等于0销毁节点
            if (this.hp <= 0) {
                this._playDestroy();
            }
        }
    },
    
   //播放飞机销毁动画
    _playDestroy() {
        //因为没有爆炸的特效资源,暂时使用淡出+缩放
        this._collider.enabled = false;
        let fadeOut = cc.fadeOut(0.5);
        let scaleTo = cc.scaleTo(0.3, 0.1);
        let spawn = cc.spawn(fadeOut, scaleTo);
        let remove = cc.removeSelf();
        this.node.runAction(cc.sequence(spawn, remove));
    },
});

敌机处理碰撞需要识别碰撞对象,有可能是与玩家的子弹相碰,也有可能是与玩家的飞机相撞,通过 onCollisionEnter 碰撞回调的 other 参数可以知道是谁碰到了我(当前对象)。

碰撞分组

除了子弹、敌机预制体、组件的准备,还需要在引擎中编辑碰撞分组,这里先分析下游戏中有那些对象可能会参与碰撞,我这里列出了四个:

  • 玩家飞机
  • 玩家子弹
  • 敌人飞机
  • 敌机子弹

这里重点只介绍子弹的碰撞:

  1. 玩家的飞机发出子弹可以击中敌人的飞机,因此玩家子弹敌人飞机是一对。
  2. 敌人飞机发出的子弹可以击中玩家飞机,因此敌人子弹玩家飞机是一对。

碰撞分组设置

为了使教程代码清晰简单,Shawn只设置了玩家子弹敌人飞机的碰撞,至于玩家飞机敌人飞机敌机子弹玩家飞机的碰撞就留给大家自行完成了(感觉是在绕口令…汗!)。

碰撞分组有了,设置子弹和敌机节点的Group属性:
子弹Group为my-bullet

敌机Group为enemy

小结

使用 Cocos Creator 提供的碰撞系统:碰撞组件、碰撞分组、碰撞事件监听,可以很方便地实现游戏中的碰撞处理。

当碰撞产生时,所有关联的碰撞对象都会收到碰撞回调通知,因此可以减少对其它模块的依赖,尽可能各自处理自身的游戏逻辑,比如:子弹碰撞只管自己的销毁,它碰到谁都一样(根据具体逻辑处理),如果敌机有盔甲防御等复杂的机制,要以由敌机模块处理伤害计算。

最后需要注意凹多边形问题,尽可能使用矩形、圆型碰撞,如果必须使用多边形碰撞,碰撞接触面不要有凹陷。


欢迎关注我的公众号,我们一起成长!

奎特尔星球

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章