前言

上文中我们加入了1个敌人,使用A*算法寻路。本文会给我们的炸弹人增加放炸弹的能力。

名词解释

  • xx类族
    是指以xx为基类的继承树上的所有类。

本文目的

实现“放炸弹”功能

增加1个敌人,即一共有2个敌人追踪炸弹人

本文主要内容

 

回顾上文更新后的领域模型

炸弹人游戏开发系列(8):放炸弹

查看大图

对领域模型进行思考

Layer类族的render方法改名为run

Layer的render方法负责统一调用Layer的方法,在概念上属于Actor,因此将其改名为run。

开发策略

首先实现“放炸弹”功能。把这个功能分解成很多个子功能,一个一个地实现子功能。

然后再加入1个敌人。实际上就是在Game中往EnemyLayer集合中再加入一个EnemySprite实例,SpriteData增加第2个敌人的数据,SpriteFactory增加工厂方法createEnemy2。

放炸弹流程

 炸弹人游戏开发系列(8):放炸弹

功能分解

炸弹人游戏开发系列(8):放炸弹

显示炸弹和火焰

显示炸弹

首先来实现“地图上显示炸弹”的功能,目前最多显示1个炸弹,玩家、敌人不能穿过炸弹。如果玩家处于炸弹方格中,则敌人会原地等待,玩家离开后,敌人继续追踪。

增加图片

增加图片bomb.png:

炸弹人游戏开发系列(8):放炸弹

增加BomberSprite

增加炸弹精灵类BomberSprite:

(function () {
    var BombSprite = YYC.Class(Sprite, {
        Init: function (data, bitmap) {
            this.base(null, bitmap);
        },
        Public: {
            draw: function (context) {
                context.drawImage(this.bitmap.img, this.x, this.y, this.bitmap.width, this.bitmap.height);
            },
            clear: function (context) {
                    context.clearRect(0, 0, bomberConfig.canvas.WIDTH, bomberConfig.canvas.HEIGHT);
            }
        }
    });

    window.BombSprite = BombSprite;
}());

增加BombLayer

在画布上增加炸弹层。同时增加对应的BombLayer类,它的集合元素为BombSprite类的实例。

将玩家、敌人画布Canvas的zIndex设为3,炸弹画布的zIndex设为1,使得,炸弹画布位于地图画布(zIndex为0)之上,玩家和敌人画布之下。

BomberLayer

(function () {
    var BombLayer = YYC.Class(Layer, {
        Private: {
            ___hasBomb: function () {
                return this.getChilds().length > 0;
            },
            ___render: function () {
                if (this.___hasBomb()) {
                    this.clear();
                    this.draw();
                }
            }
        },
        Public: {
            setCanvas: function () {
                this.P__canvas = document.getElementById("bombLayerCanvas");
                var css = {
                    "position": "absolute",
                    "top": bomberConfig.canvas.TOP,
                    "left": bomberConfig.canvas.LEFT,
                    "z-index": 1
                };

                $("#bombLayerCanvas").css(css);
            },
            draw: function () {
                this.P__iterator("draw", this.P__context);
            },
            clear: function () {
                this.P__iterator("clear", this.P__context);
            },
            run: function () {
                this.___render();
            }
        }
    });

    window.BombLayer = BombLayer;
}());

增加工厂方法

SpriteFactory增加创建炸弹精灵类实例的工厂方法。

LyaerFactory增加创建炸弹层实例的工厂方法。

SpriteFactory

       createBomb: function (playerSprite) {
            return new BombSprite(playerSprite, bitmapFactory.createBitmap({ img: window.imgLoader.get("bomb"), width: bomberConfig.WIDTH, height: bomberConfig.HEIGHT }));
        },

LayerFactory

    createBomb: function () {
            return new BombLayer();
        },

修改PlayerSprite

PlayerSprite增加createBomb方法:

           bombNum: 0,
...
            createBomb: function () {
                if (this.moving || this.bombNum === 1) {
                    return null;
                }

                var bomb = spriteFactory.createBomb();

                bomb.x = this.x;
                bomb.y = this.y;

                this.bombNum += 1;

                return bomb;
            }

修改PlayerLayer

PlayerLayer增加getBomb和createAndAddBomb方法:

            bombLayer: null,
...
            getBomb: function (bombLayer) {
                this.bombLayer = bombLayer;
            },
            createAndAddBomb: function () {
                var bomb = this.getChildAt(0).createBomb();
                if (!bomb) {
                    return false;
                }
                this.bombLayer.appendChild(bomb);
            }

监听空格键

空格键用于炸弹人放炸弹。

KeyCodeMap增加空格键枚举值:

    var keyCodeMap = {
        Left: 65, // A键
        Right: 68, // D键
        Down: 83, // S键
        Up: 87, // W键
        Space: 32   //空格键
    };

    keyState[keyCodeMap.A] = false;
    keyState[keyCodeMap.D] = false;
    keyState[keyCodeMap.W] = false;
    keyState[keyCodeMap.S] = false;
    keyState[keyCodeMap.Space] = false;

然后在PlayerLayer中对KeyState的空格键进行判定:

            run: function () {
                if (keyState[keyCodeMap.Space]) {
                    this.createAndAddBomb();
                    keyState[keyCodeMap.Space] = false;
                }
                this.base();
            }

领域模型

炸弹人游戏开发系列(8):放炸弹

  

显示火焰

火力范围设为1格,分为上下左右四个方向。地图的墙对火焰有阻断作用。

增加图片

爆炸中心为图片boom.png:

炸弹人游戏开发系列(8):放炸弹

火焰为图片explode.png:

炸弹人游戏开发系列(8):放炸弹

增加FireSprite

增加火焰精灵类。

增加FireLayer

在画布上增加火焰画布,同时对应的FireLayer类。

该画布位于地图和炸弹画布之上,玩家和敌人画布之下。

增加工厂方法

SpriteFactory增加创建爆炸中心火焰精灵类实例和创建火焰精灵类实例的工厂方法。

LayerFactory增加创建火焰层实例的工厂方法。

领域模型

 炸弹人游戏开发系列(8):放炸弹

相关代码

Sprite

(function () {
    var Sprite = YYC.AClass({
        Init: function (data, bitmap) {
            this.bitmap = bitmap;

            if (data) {
                this.x = data.x;
                this.y = data.y;

                this.defaultAnimId = data.defaultAnimId;
                this.anims = data.anims;
            }
        },
        Private: {
            //更新帧动画
            _updateFrame: function (deltaTime) {
                if (this.currentAnim) {
                    this.currentAnim.update(deltaTime);
                }
            }
        },
        Public: {
            bitmap: null,

            //精灵的坐标
            x: 0,
            y: 0,

            //精灵包含的所有 Animation 集合. Object类型, 数据存放方式为" id : animation ".
            anims: null,
            //默认的Animation的Id , string类型
            defaultAnimId: null,

            //当前的Animation.
            currentAnim: null,

            //设置当前Animation, 参数为Animation的id, String类型
            setAnim: function (animId) {
                this.currentAnim = this.anims[animId];
            },
            //重置当前帧
            resetCurrentFrame: function (index) {
                this.currentAnim && this.currentAnim.setCurrentFrame(index);
            },
            //取得精灵的碰撞区域,
            getCollideRect: function () {
                return {
                    x1: this.x,
                    y1: this.y,
                    x2: this.x + this.bitmap.width,
                    y2: this.y + this.bitmap.height
                }
            },
            Virtual: {
                //初始化方法
                init: function () {
                    //设置当前Animation
                    this.setAnim(this.defaultAnimId);
                },
                // 更新精灵当前状态.
                update: function (deltaTime) {
                    this._updateFrame(deltaTime);
                },
                //获得坐标对应的方格坐标
                getCellPosition: function (x, y) {
                    return {
                        x: x / bomberConfig.WIDTH,
                        y: y / bomberConfig.HEIGHT
                    }
                },
                draw: function (context) {
                    context.drawImage(this.bitmap.img, this.x, this.y, this.bitmap.width, this.bitmap.height);
                },
                clear: function (context) {
                    //直接清空画布区域
                    context.clearRect(0, 0, bomberConfig.canvas.WIDTH, bomberConfig.canvas.HEIGHT);
                }
            }
        },
        Abstract: {
        }
    });

    window.Sprite = Sprite;
}());
View Code

相关文章: