炸弹人平七本

常见攻略:
高手进阶:
热点资讯:
部落战争七本防天胖屌丝引导阵相关介绍 防八本威慑七本解析
发布时间:
作者:tanxiong
【秒杀】三炮炮覆盖四库,另左右各一个法塔辅助群伤。而屌丝弓箭之类的射程却无法攻击到法师塔和迫击炮,在打外围过程中直接秒杀。【防炸】用炸弹,炸炸弹人。左右两边看似薄弱的内凹区域,旁边离墙一格摆小炸弹,用于炸炸弹人。给敌方增加困难。另外工人小屋和下方库外围围墙吸引炸弹人破墙。有效
防止炸弹直接炸防空墙。上方左右两侧同理。注意:1.黑库顶点围墙外需要再加一块墙,可以有效保障防止敌方绿皮海冲黑的的时间。保障迫击炮从容输出。2.我的法塔等级4级,迫击炮全5级。一炮刚好秒5星弓箭,请注意升级。如果你的迫击炮等级不够,可能要几炮才能打死一个弓箭。、【城堡】还有一个城堡外置是迫不得已,因为我有黑水。城堡的兵力和可以全地图引的特性注定了城堡兵只能一次性干扰进攻方的进攻节奏。并不能多次往复使用,只有保护号主力输出才是至关重要的。我的考虑是在八本的时候内置使用。【示弱】此阵特点内凹处看起来最弱,打掉防空看起来轻而易举,大家会觉得内凹处最薄弱。其实内凹处杀机最大。一个内凹处集火3加农,1法塔,1电塔,2箭塔,三迫击炮的火力。【屌丝流】射防空根本不可能,他们首先会攻击加农以及箭塔电塔,还有工人房子。在攻击过程会直接被三迫击炮和法师塔电塔箭塔秒杀。打完了这些外围输出才会射击防空,所以防空在此处反而最安全。相关推荐:新手进阶攻略详解 部落战争打鱼实用技巧心得介绍:)coc有史以来最难推平七本阵型相关介绍 部落战争七本神阵推荐:)部落战争八本没有防得了胖子流的阵型相关介绍:)部落战争四本神阵推荐 四本神阵相关图解介绍:)
1.为了让您的评论能被更多玩家看到,请勿恶意灌水。
2.谢绝人身攻击、地域歧视、刷屏、广告等恶性言论。
3.所有评论均代表玩家本人意见,不代表去秀立场。
9月将会出现新的COC更新,而游戏园小编也会更进的,最新预告是什么呢?下面就.....前言上文中我们加入了1个敌人,使用A*算法寻路。本文会给我们的炸弹人增加放炸弹的能力。说明名词解释xx类族是指以xx为基类的继承树上的所有类。本文目的实现&放炸弹&功能增加1个敌人,即一共有2个敌人追踪炸弹人本文主要内容开发策略显示炸弹和火焰使用观察者模式重构炸弹可以炸死炸弹人和敌人移动时放炸弹放置多个炸弹改变地图小结加入1个敌人本文最终领域模型演示本文参考资料回顾上文更新后的领域模型对领域模型进行思考Layer类族的render方法改名为runLayer的render方法负责统一调用Layer的方法,在概念上属于Actor,因此将其改名为run。开发策略首先实现&放炸弹&功能。把这个功能分解成很多个子功能,一个一个地实现子功能。然后再加入1个敌人。实际上就是在Game中往EnemyLayer集合中再加入一个EnemySprite实例,SpriteData增加第2个敌人的数据,SpriteFactory增加工厂方法createEnemy2。放炸弹流程&功能分解显示炸弹和火焰显示炸弹首先来实现&地图上显示炸弹&的功能,目前最多显示1个炸弹,玩家、敌人不能穿过炸弹。如果玩家处于炸弹方格中,则敌人会原地等待,玩家离开后,敌人继续追踪。增加图片增加图片bomb.png:增加BomberSprite增加炸弹精灵类BomberSprite:(function () {
var BombSprite = YYC.Class(Sprite, {
Init: function (data, bitmap) {
this.base(null, bitmap);
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 = BombS}());增加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();
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 = BombL}());增加工厂方法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();
},修改PlayerSpritePlayerSprite增加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;
}修改PlayerLayerPlayerLayer增加getBomb和createAndAddBomb方法:
bombLayer: null,...
getBomb: function (bombLayer) {
this.bombLayer = bombL
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键
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();
}领域模型  显示火焰火力范围设为1格,分为上下左右四个方向。地图的墙对火焰有阻断作用。增加图片爆炸中心为图片boom.png:火焰为图片explode.png:增加FireSprite增加火焰精灵类。增加FireLayer在画布上增加火焰画布,同时对应的FireLayer类。该画布位于地图和炸弹画布之上,玩家和敌人画布之下。增加工厂方法SpriteFactory增加创建爆炸中心火焰精灵类实例和创建火焰精灵类实例的工厂方法。LayerFactory增加创建火焰层实例的工厂方法。领域模型&相关代码Sprite(function () {
var Sprite = YYC.AClass({
Init: function (data, bitmap) {
this.bitmap =
if (data) {
this.x = data.x;
this.y = data.y;
this.defaultAnimId = data.defaultAnimId;
this.anims = data.
Private: {
//更新帧动画
_updateFrame: function (deltaTime) {
if (this.currentAnim) {
this.currentAnim.update(deltaTime);
bitmap: null,
//精灵的坐标
//精灵包含的所有 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 () {
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) {
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 = S}());View CodeFireSprite(function () {
var FireSprite = YYC.Class(Sprite, {
Init: function (data, bitmap) {
this.base(null, bitmap);
window.FireSprite = FireS}());BombSprite(function () {
var BombSprite = YYC.Class(Sprite, {
Init: function (playerSprite, bitmap) {
this.playerSprite = playerS
this.base(null, bitmap);
Protected: {
Private: {
__createFire: function () {
var fires = [],
up = null,
down = null,
left = null,
right = null;
this.__createCenter(fires);
this.__createUp(fires);
this.__createDown(fires);
this.__createLeft(fires);
this.__createRight(fires);
__createCenter: function (fires) {
var center = spriteFactory.createExplode();
center.x = this.x;
center.y = this.y;
fires.push(center);
__createUp: function (fires) {
this.__createOneDir(fires, this.x, this.y - bomberConfig.HEIGHT);
__createDown: function (fires) {
this.__createOneDir(fires, this.x, this.y + bomberConfig.HEIGHT);
__createLeft: function (fires) {
this.__createOneDir(fires, this.x - bomberConfig.WIDTH, this.y);
__createRight: function (fires) {
this.__createOneDir(fires, this.x + bomberConfig.WIDTH, this.y);
__createOneDir: function (fires, x, y) {
var fire = null;
var position = this.getCellPosition(x, y);
if (this.__isNotBorder(position) && this.__isGround(position)) {
fire = spriteFactory.createFire();
fires.push(fire);
__isNotBorder: function (position) {
if (position.x & 0 || position.y & 0) {
return false;
if (position.x &= window.mapData[0].length || position.y &= window.mapData.length) {
return false;
return true;
__isGround: function (position) {
return window.mapData[position.y][position.x] === window.bomberConfig.map.type.GROUND;
__changeTerrainData: function () {
var pass = bomberConfig.map.terrain.pass,
position = this.getCellPosition(this.x, this.y);
window.terrainData[position.y][position.x] =
playerSprite: null,
explode: function () {
this.playerSprite.bombNum -= 1;
this.__changeTerrainData();
return this.__createFire();
window.BombSprite = BombS}());View CodePlayerSprite(function () {
var PlayerSprite = YYC.Class(MoveSprite, {
Init: function (data, bitmap) {
this.base(data, bitmap);
this.P__context = new Context(this);
Private: {
__allKeyUp: function () {
return window.keyState[keyCodeMap.A] === false && window.keyState[keyCodeMap.D] === false
&& window.keyState[keyCodeMap.W] === false && window.keyState[keyCodeMap.S] === false;
__judgeAndSetDir: function () {
if (window.keyState[keyCodeMap.A] === true) {
this.P__context.walkLeft();
else if (window.keyState[keyCodeMap.D] === true) {
this.P__context.walkRight();
else if (window.keyState[keyCodeMap.W] === true) {
this.P__context.walkUp();
else if (window.keyState[keyCodeMap.S] === true) {
this.P__context.walkDown();
__changeTerrainData: function () {
var stop = bomberConfig.map.terrain.stop,
position = this.getCurrentCellPosition();
window.terrainData[position.y][position.x] =
//已放置的炸弹数
bombNum: 0,
move: function () {
this.P__context.move();
setDir: function () {
if (this.moving) {
if (this.__allKeyUp()) {
this.P__context.stand();
this.__judgeAndSetDir();
createBomb: function () {
if (this.moving || this.bombNum === 1) {
return null;
var bomb = spriteFactory.createBomb(this);
bomb.x = this.x;
bomb.y = this.y;
this.bombNum += 1;
this.__changeTerrainData();
window.PlayerSprite = PlayerS}());View CodeLayer//层类(抽象类)//职责:////负责层内组件的统一draw(function () {
var Layer = YYC.AClass(Collection, {
Init: function () {
Private: {
__state: bomberConfig.layer.state.CHANGE,
//默认为change
__getContext: function () {
this.P__context = this.P__canvas.getContext("2d");
Protected: {
//*共用的变量(可读、写)
P__canvas: null,
P__context: null,
//*共用的方法(可读)
P__isChange: function () {
return this.__state === bomberConfig.layer.state.CHANGE;
P__isNormal: function () {
return this.__state === bomberConfig.layer.state.NORMAL;
P__setStateNormal: function () {
this.__state = bomberConfig.layer.state.NORMAL;
P__setStateChange: function () {
this.__state = bomberConfig.layer.state.CHANGE;
P__iterator: function (handler) {
var args = Array.prototype.slice.call(arguments, 1),
nextElement = null;
while (this.hasNext()) {
nextElement = this.next();
nextElement[handler].apply(nextElement, args);
//要指向nextElement
this.resetCursor();
P__render: function () {
if (this.P__isChange()) {
this.clear();
this.draw();
this.P__setStateNormal();
addElements: function (elements) {
this.appendChilds(elements);
Virtual: {
init: function () {
this.__getContext();
//更改状态
change: function () {
this.__state = bomberConfig.layer.state.CHANGE;
Abstract: {
setCanvas: function () {
clear: function () {
//统一绘制
draw: function () { },
//游戏主线程调用的函数
run: function () { }
window.Layer = L}());View CodeFireLayer(function () {
var FireLayer = YYC.Class(Layer, {
Private: {
___hasFire: function(){
return this.getChilds().length & 0;
setCanvas: function () {
this.P__canvas = document.getElementById("fireLayerCanvas");
var css = {
"position": "absolute",
"top": bomberConfig.canvas.TOP,
"left": bomberConfig.canvas.LEFT,
"z-index": 2
$("#fireLayerCanvas").css(css);
draw: function () {
this.P__iterator("draw", this.P__context);
clear: function () {
this.P__iterator("clear", this.P__context);
change: function () {
if (this.___hasFire()) {
this.base();
run: function () {
this.P__render();
window.FireLayer = FireL}());BombLayer(function () {
var BombLayer = YYC.Class(Layer, {
Private: {
___hasBomb: function(){
return this.getChilds().length & 0;
___removeBomb: function (bomb) {
//*注意顺序!
this.clear();
this.remove(bomb);
___removeAllFire: function () {
//*注意顺序!
this.fireLayer.clear();
this.fireLayer.removeAll();
fireLayer: null,
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);
getFire: function (fireLayer) {
this.fireLayer = fireL
explode: function (bomb) {
var self = this;
this.fireLayer.addElements(bomb.explode());
this.___removeBomb(bomb);
//定时清空fireLayer(火焰消失)
setTimeout(function () {
self.___removeAllFire();
change: function(){
if (this.___hasBomb()) {
this.base();
run: function () {
this.P__render();
window.BombLayer = BombL}());SpriteFactory
createFire: function () {
return new FireSprite(null, bitmapFactory.createBitmap({ img: window.imgLoader.get("fire"), width: bomberConfig.WIDTH, height: bomberConfig.HEIGHT }));
createExplode: function () {
return new FireSprite(null, bitmapFactory.createBitmap({ img: window.imgLoader.get("explode"), width: bomberConfig.WIDTH, height: bomberConfig.HEIGHT }));
}LayerFactory  
createFire: function () {
return new FireLayer();
}&显示炸弹和火焰&演示演示地址使用观察者模式观察者模式介绍详见Javascript设计模式之我见:观察者模式。应用场景墙被炸掉后,会变成空地。实现思路Maplayer的changeSpriteImg负责更改地图图片,BombSprite的explode负责处理爆炸逻辑。需要在explode中调用Maplayer的changeSpriteImg。因此,决定在Game中订阅Maplayer的changeSpriteImg方法,然后在BombSprite的explode方法中发布。   为什么此处用观察者模式因为MapLayer的Layer类族在BombSprite的Sprite类族的上层,我不希望下层BombSprite与上层MapLayer耦合。因此,采用观察者模式来解除两者的耦合。领域模型使用观察模式前&使用观察模式后相关代码Subject(function () {
if (!Array.prototype.forEach) {
Array.prototype.forEach = function (fn, thisObj) {
var scope = thisObj ||
for (var i = 0, j = this. i & ++i) {
fn.call(scope, this[i], i, this);
if (!Array.prototype.filter) {
Array.prototype.filter = function (fn, thisObj) {
var scope = thisObj ||
var a = [];
for (var i = 0, j = this. i & ++i) {
if (!fn.call(scope, this[i], i, this)) {
a.push(this[i]);
Subject = function () {
this._events = [];
Subject.prototype = (function () {
//订阅方法
subscribe: function (context, fn) {
if (arguments.length == 2) {
this._events.push({ context: arguments[0], fn: arguments[1] });
this._events.push(arguments[0]);
//发布指定方法
publish: function (context, fn, args) {
var args = Array.prototype.slice.call(arguments, 2);
//获得函数参数
var _context = null;
var _fn = null;
this._events.filter(function (el) {
if (el.context) {
_context = el.
_context =
if (_fn === fn) {
}).forEach(function (el) {
//指定方法可能有多个
el.apply(_context, args);
//执行每个指定的方法
unSubscribe: function (fn) {
var _fn = null;
this._events = this._events.filter(function (el) {
if (el.fn) {
if (_fn !== fn) {
//全部发布
publishAll: function (context, args) {
var args = Array.prototype.slice.call(arguments, 1);
//获得函数参数
var _context = null;
var _fn = null;
this._events.forEach(function (el) {
if (el.context) {
_context = el.
_context =
_fn.apply(_context, args);
//执行每个指定的方法
dispose: function () {
this._events = [];
YYC.Pattern.Subject = S})();View CodeMapLayer
//改变指定精灵类的img对象
//x:x坐标(方格对应值);y:y坐标(方格对应值);img:要替换的img对象
changeSpriteImg: function (x, y, img) {
var index = y * window.bomberConfig.map.COL +
this.getChildAt(index).bitmap.img =
},BombSprite
__destroyOneDir: function (x, y) {...
window.observer.publishAll(null, position.x, position.y, groundImg);...
//观察者全局实例
window.observer = null
var Game = YYC.Class({
Init: function () {
window.observer = new YYC.Pattern.Observer();
init: function () {...
//观察者模式 -& 订阅
window.observer.subscribe(this.layerManager.getLayer("mapLayer"), this.layerManager.getLayer("mapLayer").changeSpriteImg);
},重构增加TerrainDataOperate增加TerrainData地形数据操作类TerrainDataOperate领域模型重构前重构后相关代码TerrainDataOperate(function () {
var terrainDataOperate = {
getTerrainData: function () {
return YYC.Tool.array.clone(window.terrainData);
setTerrainData: function (x, y, data) {
window.terrainData[y][x] =
window.terrainDataOperate = terrainDataO}());增加火力范围将范围从1格改为2格,方便演示游戏。增加游戏全局状态GameState在Game的run方法中,需要判断敌人是否抓住了玩家(是否与玩家碰撞):
run: function () {
if (this.layerManager.getLayer("enemyLayer").collideWidthPlayer()) {
YYC.Tool.asyn.clearAllTimer(this.mainLoop);
alert("Game Over!");
}这里注意到,Game需要知道EnemyLayer的collideWidthPlayer方法:但Game类只应该知道LayerManager,而不应该知道Layer(见&炸弹人游戏开发系列(1):准备工作&中的概念层次结构)。因此,增加游戏全局状态GameState,在Game的run判断GameState,然后把与炸弹人的碰撞检测的任务放到EnemyLayer的run方法中:重构后相关代码Config
NORAML: 1,
//游戏全局状态
window.gameState = window.bomberConfig.game.state.NORMAL;...
run: function () {
if (window.gameState === window.bomberConfig.game.state.OVER) {
this.gameOver();
gameOver: function () {
YYC.Tool.asyn.clearAllTimer(this.mainLoop);
alert("Game Over!");
}EnemyLayer
run: function () {
if (this.collideWidthPlayer()) {
window.gameState = window.bomberConfig.game.state.OVER;
}...炸弹可以炸死炸弹人和敌人在炸弹爆炸时,判断与炸弹人、敌人是否碰撞并进行相应处理。领域模型相关代码BombLayer___collideFireWithPlayer: function (bomb) {
if (bomb.collideFireWithCharacter(this.playerLayer.getChildAt(0))) {
window.gameState = window.bomberConfig.game.state.OVER;
}},___collideFireWithEnemy: function (bomb) {
var i = 0,
enemySprites = this.enemyLayer.getChilds();
for (i = 0, len = enemySprites. i & i++) {
if (bomb.collideFireWithCharacter(enemySprites[i])) {
this.___removeEnemy(enemySprites[i]);
}},___removeEnemy: function (enemy) {
//*注意顺序!
this.enemyLayer.clear();
this.enemyLayer.remove(enemy);},___handleCollid: function (bomb) {
//判断与炸弹人碰撞
this.___collideFireWithPlayer(bomb)
//判断与每个敌人碰撞
this.___collideFireWithEnemy(bomb);}...enemyLayer: null,playerLayer: null,...explode: function (bomb) {
var self = this,
result = null;
//处理碰撞
this.___handleCollid(bomb);...移动时放炸弹因为炸弹人移动时,根据炸弹人状态的不同,炸弹放置的坐标策略也不同(即如果炸弹人往上走,则炸弹放在炸弹人所在方格的上面相邻方格;如果往左走,则炸弹放在炸弹人所在方格的左侧相邻方格)。所以将PlayerSprite的createBomb方法委托给状态类处理。具体来说,就是把createBomb方法移到状态类的WalkState类和Stand类中来分别处理。领域模型分析因为PlayerSprite、EnemySprite都使用了状态类,因此两者都与BombSprite耦合。但只有PlayerSprite需要使用createBomb方法,EnemySprite并不需要使用该方法。所以此处违反了迪米特法则。目前这种情况在可以接受的范围之内。如果在后面的开发中EnemySprite与BombSprite耦合得很严重,再来考虑解耦。放置多个炸弹可以最多放3个炸弹,炸弹爆炸时会引爆在火力范围内的炸弹。不能在一个方格叠加多个炸弹在状态类WalkState类族、StandState类族的createBomb中判断方格是否有炸弹(判断地形数据TerrainData来实现)。改变地图炸掉墙如果墙处于火焰范围内,则修改MapData,将墙的图片换成空地图片,同时对应修改TerrainData,将墙所在的方格设成可通过。刷新地图在炸掉墙后,在BombLayer中需要调用MapLayer的setStateChange方法,将MapLayer的state设为change,从而能够在游戏的下一个主循环中,刷新地图,从而显示为空地。领域模型相关代码BombLayer___mapChange: function (mapChange) {
if (mapChange) {
this.mapLayer.setStateChange();
}}小结现在我们就完成了&放炸弹&的功能,来看下成果吧~&放炸弹&演示演示地址相关代码FireSprite(function () {
var FireSprite = YYC.Class(Sprite, {
Init: function (data, bitmap) {
this.base(null, bitmap);
window.FireSprite = FireS}());FireLayer(function () {
var FireLayer = YYC.Class(Layer, {
Private: {
___hasFire: function(){
return this.getChilds().length & 0;
setCanvas: function () {
this.P__canvas = document.getElementById("fireLayerCanvas");
var css = {
"position": "absolute",
"top": bomberConfig.canvas.TOP,
"left": bomberConfig.canvas.LEFT,
"z-index": 2
$("#fireLayerCanvas").css(css);
draw: function () {
this.P__iterator("draw", this.P__context);
clear: function () {
this.P__iterator("clear", this.P__context);
change: function () {
if (this.___hasFire()) {
this.setStateChange();
run: function () {
this.P__render();
window.FireLayer = FireL}());View CodePlayerSprite(function () {
var PlayerSprite = YYC.Class(MoveSprite, {
Init: function (data, bitmap) {
this.base(data, bitmap);
this.P__context = new Context(this);
Private: {
__allKeyUp: function () {
return window.keyState[keyCodeMap.A] === false && window.keyState[keyCodeMap.D] === false
&& window.keyState[keyCodeMap.W] === false && window.keyState[keyCodeMap.S] === false;
__judgeAndSetDir: function () {
if (window.keyState[keyCodeMap.A] === true) {
this.P__context.walkLeft();
else if (window.keyState[keyCodeMap.D] === true) {
this.P__context.walkRight();
else if (window.keyState[keyCodeMap.W] === true) {
this.P__context.walkUp();
else if (window.keyState[keyCodeMap.S] === true) {
this.P__context.walkDown();
__changeTerrainData: function () {
var stop = bomberConfig.map.terrain.stop,
position = this.getCurrentCellPosition();
terrainDataOperate.setTerrainData(position.x, position.y, stop);
//已放置的炸弹数
bombNum: 0,
move: function () {
this.P__context.move();
setDir: function () {
if (this.moving) {
if (this.__allKeyUp()) {
this.P__context.stand();
this.__judgeAndSetDir();
createBomb: function () {
if (this.bombNum === 3) {
return null;
return this.P__context.createBomb();
window.PlayerSprite = PlayerS}());View CodeBomberSprite(function () {
var BombSprite = YYC.Class(Sprite, {
Init: function (playerSprite, bitmap) {
this.playerSprite = playerS
this.base(null, bitmap);
Protected: {
Private: {
//返回火焰范围
//返回顺序为[center、[up]、[down]、[left]、[right]]
__getFireAllRange: function () {
{ x: this.x, y: this.y },
{ x: this.x, y: this.y - bomberConfig.HEIGHT },
{ x: this.x, y: this.y - bomberConfig.HEIGHT * 2 }
{ x: this.x, y: this.y + bomberConfig.HEIGHT },
{ x: this.x, y: this.y + bomberConfig.HEIGHT * 2 }
{ x: this.x - bomberConfig.WIDTH, y: this.y },
{ x: this.x - bomberConfig.WIDTH * 2, y: this.y }
{ x: this.x + bomberConfig.WIDTH, y: this.y },
{ x: this.x + bomberConfig.WIDTH * 2, y: this.y }
__getCenterEffectiveRange: function (effectiveRange, center) {
effectiveRange.center = { x: center.x, y: center.y };
__getFourDirEffectiveRange: function (effectiveRange, allRange) {
var i = 0,
firePos = null,
cellPos = null,
groundRange = [],
wallRange = [];
for (i = 0, len1 = allRange. i & len1; i++) {
for (j = 0, len2 = allRange[i]. j & len2; j++) {
firePos = allRange[i][j];
cellPos = this.getCellPosition(firePos.x, firePos.y);
if (this.__isNotBorder(cellPos)) {
if (this.__isGround(cellPos)) {
groundRange.push(firePos);
else if (this.__isWall(cellPos)) {
wallRange.push(firePos);
throw new Error("未知的地图类型");
effectiveRange.groundRange = groundR
effectiveRange.wallRange = wallR
__createFire: function (effectiveRange) {
var fires = [];
this.__createCenter(fires, effectiveRange);
this.__createFourDir(fires, effectiveRange);
__createCenter: function (fires, effectiveRange) {
var center = spriteFactory.createExplode();
center.x = effectiveRange.center.x;
center.y = effectiveRange.center.y;
fires.push(center);
__createFourDir: function (fires, effectiveRange) {
var i = 0,
fire = null,
groundRange = effectiveRange.groundR
for (i = 0, len = groundRange. i & i++) {
fire = spriteFactory.createFire();
fire.x = groundRange[i].x;
fire.y = groundRange[i].y;
fires.push(fire);
__isNotBorder: function (position) {
if (position.x & 0 || position.y & 0) {
return false;
if (position.x &= window.mapData[0].length || position.y &= window.mapData.length) {
return false;
return true;
__isGround: function (position) {
return window.mapDataOperate.getMapData()[position.y][position.x] === window.bomberConfig.map.type.GROUND;
__bombPass: function () {
var pass = bomberConfig.map.terrain.pass,
position = this.getCellPosition(this.x, this.y);
terrainDataOperate.setTerrainData(position.x, position.y, pass);
__destroyWall: function (effectiveRange) {
var i = 0,
mapChange = false,
wallRange = effectiveRange.wallRange,
cellPos = null,
ground = bomberConfig.map.type.GROUND,
groundImg = window.imgLoader.get("ground"),
wall = bomberConfig.map.type.WALL,
pass = bomberConfig.map.terrain.pass,
stop = bomberConfig.map.terrain.
for (i = 0, len = wallRange. i & i++) {
cellPos = this.getCellPosition(wallRange[i].x, wallRange[i].y);
window.mapDataOperate.setMapData(cellPos.x, cellPos.y, ground);
window.terrainDataOperate.setTerrainData(cellPos.x, cellPos.y, pass);
//观察者模式 -& 发布
//调用mapLayer.changeSpriteImg,改变地图层对应精灵类的img对象
window.observer.publishAll(null, cellPos.x, cellPos.y, groundImg);
if (!mapChange) {
mapChange = true;
return mapC
__isWall: function (position) {
return window.mapDataOperate.getMapData()[position.y][position.x] === window.bomberConfig.map.type.WALL;
__isInEffectiveRange: function (effectiveRange) {
var range = null;
range = effectiveRange.groundRange.concat(effectiveRange.wallRange);
range.push(effectiveRange.center);
if (this.__isInRange(range)) {
return true;
return false;
__isInRange: function (range) {
var i = 0,
for (i = 0, len = range. i & i++) {
if (range[i].x === this.x && range[i].y === this.y) {
return true;
return false;
playerSprite: null,
//是否已爆炸标志
exploded: false,
explode: function () {
var fires = null,
mapChange = false,
effectiveRange = [];
this.playerSprite.bombNum -= 1;
this.exploded = true;
this.__bombPass();
effectiveRange = this.getFireEffectiveRange();
fires = this.__createFire(effectiveRange);
mapChange = this.__destroyWall(effectiveRange);
fires: fires,
mapChange: mapChange
//检测火焰与玩家人物、敌人的碰撞
collideFireWithCharacter: function (sprite) {
var effectiveRange = this.getFireEffectiveRange(),
range = [],
fire = {},
obj2 = {},
//放到数组中
range.push(effectiveRange.center);
range = range.concat(effectiveRange.groundRange, effectiveRange.wallRange);
for (i = 0, len = range. i & i++) {
x: range[i].x,
y: range[i].y,
width: this.bitmap.width,
height: this.bitmap.height
x: sprite.x,
y: sprite.y,
width: sprite.bitmap.width,
height: sprite.bitmap.height
if (YYC.Tool.collision.col_Between_Rects(fire, obj2)) {
return true;
return false;
//返回有效范围。(考虑墙、边界阻挡等问题)
//返回值形如:{center: {x: 1,y: 1}}, {groundRange: [{{x: 1,y: 1}]}, {wallRange: [{{x: 1,y: 1}]}
getFireEffectiveRange: function () {
var effectiveRange = {},
allRange = this.__getFireAllRange();
this.__getCenterEffectiveRange(effectiveRange, allRange.shift());
this.__getFourDirEffectiveRange(effectiveRange, allRange);
return effectiveR
isInEffectiveRange: function (bomb) {
return this.__isInEffectiveRange(bomb.getFireEffectiveRange());
window.BombSprite = BombS}());View CodeSprite(function () {
var Sprite = YYC.AClass({
Init: function (data, bitmap) {
this.bitmap =
if (data) {
this.x = data.x;
this.y = data.y;
this.defaultAnimId = data.defaultAnimId;
this.anims = data.
Private: {
//更新帧动画
_updateFrame: function (deltaTime) {
if (this.currentAnim) {
this.currentAnim.update(deltaTime);
bitmap: null,
//精灵的坐标
//精灵包含的所有 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 () {
var obj = {
x: this.x,
y: this.y,
width: this.bitmap.width,
height: this.bitmap.height
return YYC.Tool.collision.getCollideRect(obj);
Virtual: {
//初始化方法
init: function () {
//设置当前Animation
this.setAnim(this.defaultAnimId);
// 更新精灵当前状态.
update: function (deltaTime) {
this._updateFrame(deltaTime);
//获得坐标对应的方格坐标(向下取值)
getCellPosition: function (x, y) {
x: Math.floor(x / bomberConfig.WIDTH),
y: Math.floor(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);
window.Sprite = S}());View CodePlayerLayer(function () {
var PlayerLayer = YYC.Class(CharacterLayer, {
Init: function (deltaTime) {
this.base(deltaTime);
Private: {
___keyDown: function () {
if (keyState[keyCodeMap.A] === true || keyState[keyCodeMap.D] === true
|| keyState[keyCodeMap.W] === true || keyState[keyCodeMap.S] === true) {
return true;
return false;
___spriteMoving: function () {
return this.getChildAt(0).moving
___spriteStand: function () {
if (this.getChildAt(0).stand) {
this.getChildAt(0).stand = false;
return true;
return false;
bombLayer: null,
setCanvas: function () {
this.P__canvas = document.getElementById("playerLayerCanvas");
$("#playerLayerCanvas").css({
"position": "absolute",
"top": bomberConfig.canvas.TOP,
"left": bomberConfig.canvas.LEFT,
"border": "1px solid red",
"z-index": 3
init: function (layers) {
this.bombLayer = layers.bombL
this.base();
change: function () {
if (this.___keyDown() || this.___spriteMoving() || this.___spriteStand()) {
this.base();
createAndAddBomb: function () {
var bomb = this.getChildAt(0).createBomb();
var self = this;
if (!bomb) {
return false;
this.bombLayer.appendChild(bomb);
//3s后炸弹爆炸
setTimeout(function () {
if (!bomb.exploded) {
self.bombLayer.explode(bomb);
run: function () {
if (keyState[keyCodeMap.Space]) {
this.createAndAddBomb();
keyState[keyCodeMap.Space] = false;
this.base();
window.PlayerLayer = PlayerL}());View CodeBomberLayer(function () {
var BombLayer = YYC.Class(Layer, {
Private: {
___hasBomb: function(){
return this.getChilds().length & 0;
___removeBomb: function (bomb) {
//*注意顺序!
this.clear(bomb);
this.remove(bomb);
___removeAllFire: function () {
//*注意顺序!
this.fireLayer.clear();
this.fireLayer.removeAll();
___removeEnemy: function (enemy) {
//*注意顺序!
this.enemyLayer.clear(enemy);
this.enemyLayer.remove(enemy);
___mapChange: function (mapChange) {
if (mapChange) {
this.mapLayer.setStateChange();
___collideFireWithPlayer: function (bomb) {
if (bomb.collideFireWithCharacter(this.playerLayer.getChildAt(0))) {
window.gameState = window.bomberConfig.game.state.OVER;
___collideFireWithEnemy: function (bomb) {
var i = 0,
enemySprites = this.enemyLayer.getChilds();
for (i = 0, len = enemySprites. i & i++) {
if (bomb.collideFireWithCharacter(enemySprites[i])) {
this.___removeEnemy(enemySprites[i]);
___handleCollid: function (bomb) {
//判断与玩家人物碰撞
this.___collideFireWithPlayer(bomb)
//判断与每个敌人碰撞
this.___collideFireWithEnemy(bomb);
___explodeInEffectiveRange: function (bomb) {
var eachBomb = null;
this.resetCursor();
while (this.hasNext()) {
eachBomb = this.next();
if (eachBomb.isInEffectiveRange.call(eachBomb, bomb)) {
this.explode(eachBomb);
this.resetCursor();
fireLayer: null,
mapLayer: null,
playerLayer: null,
enemyLayer: null,
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);
init: function(layers){
this.fireLayer = layers.fireL
this.mapLayer = layers.mapL
this.playerLayer = layers.playerL
this.enemyLayer = layers.enemyL
this.base();
draw: function () {
this.P__iterator("draw", this.P__context);
explode: function (bomb) {
var self = this,
result = null;
//处理碰撞
this.___handleCollid(bomb);
result = bomb.explode();
this.fireLayer.addSprites(result.fires);
this.___mapChange(result.mapChange);
this.___removeBomb(bomb);
//炸弹爆炸时会引爆在火力范围内的炸弹。
this.___explodeInEffectiveRange(bomb);
//定时清空fireLayer(火焰消失)
setTimeout(function () {
self.___removeAllFire();
change: function(){
if (this.___hasBomb()) {
this.setStateChange();
run: function () {
this.P__render();
window.BombLayer = BombL}());View CodeLayer(function () {
var Layer = YYC.AClass(Collection, {
Init: function () {
Private: {
__state: bomberConfig.layer.state.CHANGE,
//默认为change
__getContext: function () {
this.P__context = this.P__canvas.getContext("2d");
Protected: {
//*共用的变量(可读、写)
P__canvas: null,
P__context: null,
//*共用的方法(可读)
P__isChange: function(){
return this.__state === bomberConfig.layer.state.CHANGE;
P__isNormal: function () {
return this.__state === bomberConfig.layer.state.NORMAL;
P__iterator: function (handler) {
var args = Array.prototype.slice.call(arguments, 1),
nextElement = null;
while (this.hasNext()) {
nextElement = this.next();
nextElement[handler].apply(nextElement, args);
//要指向nextElement
this.resetCursor();
P__render: function () {
if (this.P__isChange()) {
this.clear();
this.draw();
this.setStateNormal();
remove: function (sprite) {
this.base(function (e, obj) {
if (e.x === obj.x && e.y === obj.y) {
return true;
return false;
}, sprite);
addSprites: function(elements){
this.appendChilds(elements);
//设置状态为NORMAL
setStateNormal: function () {
this.__state = bomberConfig.layer.state.NORMAL;
//设置状态为CHANGE
setStateChange: function () {
this.__state = bomberConfig.layer.state.CHANGE;
Virtual: {
init: function () {
this.__getContext();
clear: function (sprite) {
if (arguments.length === 0) {
this.P__iterator("clear", this.P__context);
else if (arguments.length === 1) {
sprite.clear(this.P__context);
Abstract: {
setCanvas: function () {
//判断并更改状态
change: function () {
//统一绘制
draw: function () { },
//游戏主线程调用的函数
run: function () { }
window.Layer = L}());View CodeSpriteFactory
createBomb: function (playerSprite) {
return new BombSprite(playerSprite, bitmapFactory.createBitmap({ img: window.imgLoader.get("bomb"), width: bomberConfig.WIDTH, height: bomberConfig.HEIGHT }));
createFire: function () {
return new FireSprite(null, bitmapFactory.createBitmap({ img: window.imgLoader.get("fire"), width: bomberConfig.WIDTH, height: bomberConfig.HEIGHT }));
createExplode: function () {
return new FireSprite(null, bitmapFactory.createBitmap({ img: window.imgLoader.get("explode"), width: bomberConfig.WIDTH, height: bomberConfig.HEIGHT }));
}LayerFactory
createBomb: function () {
return new BombLayer();
createFire: function () {
return new FireLayer();
}加入1个敌人往EnemyLayer集合中再加入一个EnemySprite实例,SpriteData增加第2个敌人的数据,SpriteFactory增加工厂方法createEnemy2。相关代码Game
_createEnemyLayerElement: function () {
var element = [],
enemy = spriteFactory.createEnemy(),
enemy2 = spriteFactory.createEnemy2();
enemy.init();
enemy2.init();
element.push(enemy);
element.push(enemy2);
},SpriteDataenemy2: {
//初始坐标
x: bomberConfig.WIDTH * 10,
y: bomberConfig.HEIGHT * 10,
//定义sprite走路速度的绝对值
walkSpeed: bomberConfig.enemy.speed.NORMAL,
speedX: 1,
speedY: 1,
maxX: bomberConfig.canvas.WIDTH - bomberConfig.player.IMGWIDTH,
maxY: bomberConfig.canvas.HEIGHT - bomberConfig.player.IMGHEIGHT,
defaultAnimId: "stand_left",
"stand_right": new Animation(getFrames("enemy", "stand_right")),
"stand_left": new Animation(getFrames("enemy", "stand_left")),
"stand_down": new Animation(getFrames("enemy", "stand_down")),
"stand_up": new Animation(getFrames("enemy", "stand_up")),
"walk_up": new Animation(getFrames("enemy", "walk_up")),
"walk_down": new Animation(getFrames("enemy", "walk_down")),
"walk_right": new Animation(getFrames("enemy", "walk_right")),
"walk_left": new Animation(getFrames("enemy", "walk_left"))
}}SpriteFactory
createEnemy2: function () {
return new EnemySprite(getSpriteData("enemy2"), bitmapFactory.createBitmap({ img: window.imgLoader.get("enemy"), width: bomberConfig.player.IMGWIDTH, height: bomberConfig.player.IMGHEIGHT }));
},炸死所有敌人后,提示游戏胜利GameState增加WIN枚举值。在BombLayer中判断是否将敌人都炸死了,如果都炸死了则设置GameState为WIN。在Game中判断GameState,调用相应的方法。领域模型相关代码BombLayer
___collideFireWithEnemy: function (bomb) {
var i = 0,
enemySprites = this.enemyLayer.getChilds();
for (i = 0, len = enemySprites. i & i++) {
if (bomb.collideFireWithCharacter(enemySprites[i])) {
this.___removeEnemy(enemySprites[i]);
//如果敌人都被炸死了,则游戏胜利!
if (this.enemyLayer.getChilds().length === 0) {
window.gameState = window.bomberConfig.game.state.WIN;
},Game_judgeGameState: function () {
switch (window.gameState) {
case window.bomberConfig.game.state.NORMAL:
case window.bomberConfig.game.state.OVER:
this.gameOver();
case window.bomberConfig.game.state.WIN:
this.gameWin();
throw new Error("未知的游戏状态");
return;}...run: function () {
this._judgeGameState();
this.layerManager.run();
this.layerManager.change();},本文最终领域模型高层划分炸弹层和炸弹精灵、火焰层和火焰精灵应该放到哪个包?炸弹层和玩家层、炸弹精灵和玩家精灵紧密关联,火焰层和火焰精灵与炸弹层和炸弹精灵紧密关联,因此将炸弹层和炸弹精灵、火焰层和火焰精灵移到人物包中。新增包全局包GameState观察者模式包Subject炸弹实现包BombSprite、FireSprite、BombLayer、FireLayer层、包对应领域模型辅助操作层控件包PreLoadImg配置包Config用户交互层入口包Main业务逻辑层辅助逻辑工厂包BitmapFactory、LayerFactory、SpriteFactory事件管理包KeyState、KeyEventManager抽象包Layer、Sprite、Hash、Collection全局包GameState游戏主逻辑主逻辑包Game层管理层管理包LayerManager实现人物实现包PlayerLayer、MoveSprite、PlayerSprite、EnemySprite、CharacterLayer、PlayerLayer、EnemyLayer、Context、PlayerState、WalkState、StandState、WalkState_X、WalkState_Y、StandLeftState、StandRightState、StandUpState、StandDownState、WalkLeftState、WalkRightState、WalkUpState、WalkDownState炸弹实现包BombSprite、FireSprite、BombLayer、FireLayer地图实现包MapLayer、MapElementSprite算法包FindPath动画包Animation、GetSpriteData、SpriteData、GetFrames、FrameData观察者模式包Subject数据操作层地图数据操作包MapDataOperate、TerrainDataOperate路径数据操作包GetPath图片数据操作包Bitmap数据层地图包MapData、TerrainData图片路径包ImgPathData演示地址演示地址本文参考资料深入理解JavaScript系列(32):设计模式之观察者模式《设计模式之禅》欢迎浏览上一篇博文:炸弹人游戏开发系列(7):加入敌人,使用A*算法寻路欢迎浏览下一篇博文:炸弹人游戏开发系列(9):总结&

我要回帖

更多关于 国世平本人 的文章

 

随机推荐