炸弹人自杀小分队百度云怎么改名

后使用我的收藏没有帐号?
所属分类: &
查看: 55|回复: 1
炸弹人小分队怎么改名
发表于 3&天前
炸弹人小分队怎么改名
点这里&&&&
发表于 3&天前
关于炸弹人的详细攻略介绍,包括天赋符文和出装,想玩炸弹人的赶紧来这里看看吧。你也可以去特玩网LOL专区的问答模块或者综合攻略模块找找啊,挺多的~
指尖每日首次回帖可以赚5金币()收起回复展开回复
点这里&&&&
庆三周年,传递祝福送好礼!容我安利一个游戏 [bombsquad]炸弹小分队 被忽略的好游戏_bilibili吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
容我安利一个游戏 [bombsquad]炸弹小分队 被忽略的好游戏收藏
类似炸弹人的游戏但是多了很多乐趣,可以多人用手机当作手柄操作,与基友们一起玩,其乐无穷~详情请见av3736185
其实我是来打广告的up主的处女作希望能给大家带来欢乐av3736185谢谢大家~
没人回复真可怜
你说楼主你整天玩这破游戏能找到女朋友么
那么 在哪里能下载呢
无心冲锋真难
围观围观( o?_o ?)
玩过,操作蛋疼
这个怎么跑步,我发现只有按住某个方向+拳击键才能跑,看教程只按住某个方向,不会跑起来
还有我发现跑跳+拳击好难打出来
登录百度帐号推荐应用
为兴趣而生,贴吧更懂你。或上文中我们实现了&玩家控制炸弹人&的功能,本文将实现碰撞检测,让炸弹人不能穿过墙。在实现的过程中会发现炸弹人移动的问题,然后会通过设置移动步长来解决。
具体状态类指应用于炸弹人移动状态的状态模式的ConcreState角色的类。这里具体包括WalkLeftState、WalkRightState、WalkUpState、WalkDownState、StandLeftState等类。
实现碰撞检测
本文主要内容
回顾上文更新后的领域模型
对领域模型进行思考
重构PlayerSprite
重构前代码
(function () {
var PlayerSprite = YYC.Class({
//供子类构造函数中调用
Init: function (data) {
this.x = data.x;
this.y = data.y;
this.minX = data.minX;
this.maxX = data.maxX;
this.minY = data.minY;
this.maxY = data.maxY;
this.defaultAnimId = data.defaultAnimId;
this.anims = data.
this.walkSpeed = data.walkS
this._context = new Context(this);
Private: {
_context: null,
_setCoordinate: function (deltaTime) {
this.x = this.x + this.speedX * deltaT
this.y = this.y + this.speedY * deltaT
this._limitMove();
_limitMove: function () {
this.x = Math.max(this.minX, Math.min(this.x, this.maxX));
this.y = Math.max(this.minY, Math.min(this.y, this.maxY));
_getCurrentState: function () {
var currentState = null;
switch (this.defaultAnimId) {
case "stand_right":
currentState = Context.standRightS
case "stand_left":
currentState = Context.standLeftS
case "stand_down":
currentState = Context.standDownS
case "stand_up":
currentState = Context.standUpS
case "walk_down":
currentState = Context.walkDownS
case "walk_up":
currentState = Context.walkUpS
case "walk_right":
currentState = Context.walkRightS
case "walk_left":
currentState = Context.walkLeftS
throw new Error("未知的状态");
return currentS
//精灵的坐标
//精灵的速度
walkSpeed: 0,
speedX: 0,
speedY: 0,
//精灵的坐标区间
maxX: 9999,
maxY: 9999,
anims: null,
//默认的Animation的Id , string类型
defaultAnimId: null,
//当前的Animation.
currentAnim: null,
init: function () {
this._context.setPlayerState(this._getCurrentState());
//设置当前Animation
this.setAnim(this.defaultAnimId);
//重置当前帧
resetCurrentFrame: function (index) {
this.currentAnim && this.currentAnim.setCurrentFrame(index);
//设置当前Animation, 参数为Animation的id, String类型
setAnim: function (animId) {
this.currentAnim = this.anims[animId];
// 更新精灵当前状态.
update: function (deltaTime) {
//每次循环,改变一下绘制的坐标
this._setCoordinate(deltaTime);
if (this.currentAnim) {
this.currentAnim.update(deltaTime);
draw: function (context) {
var frame = null;
if (this.currentAnim) {
frame = this.currentAnim.getCurrentFrame();
context.drawImage(this.currentAnim.getImg(), frame.x, frame.y, frame.width, frame.height, this.x, this.y, frame.imgWidth, frame.imgHeight);
clear: function (context) {
var frame = null;
if (this.currentAnim) {
frame = this.currentAnim.getCurrentFrame();
context.clearRect(0, 0, bomberConfig.canvas.WIDTH, bomberConfig.canvas.HEIGHT);
handleNext: function () {
this._context.walkLeft();
this._context.walkRight();
this._context.walkUp();
this._context.walkDown();
this._context.stand();
window.PlayerSprite = PlayerS
  handleNext改名为changeDir
反思handleNext方法。从方法名来看,它的职责应该为处理本次循环的所有逻辑。然而,经过数次重构后,现在handleNext的职责只是调用状态类的方法,更具体的来说,它的职责为判断和设置炸弹人移动方向。
因此,应该将handleNext改名为changeDir,从而能够反映出它的职责。
  从update方法中分离出move方法
再来审视update方法,发现它有两个职责:
进一步思考,此处&更新坐标&的职责更抽象地来说应该为"炸弹人移动&的职责。应该将其提出,形成move方法。然后去掉&__setCoordinate&方法,将其代码直接写到move方法中
  删除deltaTime
_setCoordinate: function (deltaTime) {
this.x = this.x + this.speedX * deltaT
this.y = this.y + this.speedY * deltaT
this._limitMove();
这里deltaTime其实没有什么作用,因此将其删除。
  重构后相关代码
PlayerSprite
update: function (deltaTime) {
if (this.currentAnim) {
this.currentAnim.update(deltaTime);
draw: function (context) {
var frame = null;
if (this.currentAnim) {
frame = this.currentAnim.getCurrentFrame();
context.drawImage(this.currentAnim.getImg(), frame.x, frame.y, frame.width, frame.height, this.x, this.y, frame.imgWidth, frame.imgHeight);
clear: function (context) {
var frame = null;
if (this.currentAnim) {
frame = this.currentAnim.getCurrentFrame();
context.clearRect(0, 0, bomberConfig.canvas.WIDTH, bomberConfig.canvas.HEIGHT);
move: function () {
this.x = this.x + this.speedX;
this.y = this.y + this.speedY;
this._limitMove();
changeDir: function () {
this._context.walkLeft();
this._context.walkRight();
this._context.walkUp();
this._context.walkDown();
this._context.stand();
要对应修改PlayerLayer
__changeDir: function () {
this.___iterator("changeDir");
___move: function () {
this.___iterator("move");
render: function () {
if (this.P__isChange()) {
this.clear(this.P__context);
this.__changeDir();
this.___move();
this.___update();
this.draw(this.P__context);
this.P__setStateNormal();
分离speedX/speedY属性的语义,提出&方向向量&概念dirX/dirY
状态类WalkLeftState
walkLeft: function () {
var sprite = null;
if (window.keyState[keyCodeMap.A] === true) {
sprite = this.P_context.
sprite.speedX = -sprite.walkS
sprite.speedY = 0;
sprite.setAnim("walk_left");
目前是通过在具体状态类中改变speedX/speedY的正负(如+sprite.walkSpeed或-sprite.walkSpeed),来实现炸弹人移动方向的改变。因此,我发现speedX/speedY属性实际上有两个语义:
炸弹人移动速度
炸弹人移动方向
这样会造成speed语义混淆,不便于阅读和维护。因此,将&炸弹人移动方向&提出来,形成新的属性dirX/dirY,而speedX/speedY则保留&炸弹人移动速度&语义。
  重构后相关代码
PlayerSprite
move: function () {
this.x = this.x + this.speedX * this.dirX;
this.y = this.y + this.speedY * this.dirY;
this._limitMove();
WalkLeftState(其它具体状态类也要做类似的修改)
walkLeft: function () {
var sprite = null;
if (window.keyState[keyCodeMap.A] === true) {
sprite = this.P_context.
sprite.dirX = -1;
sprite.dirY = 0;
sprite.setAnim("walk_left");
首先查阅相关资料,确定碰撞检测的方法,然后再实现炸弹人与地图砖墙的碰撞检测。
初步实现碰撞检测
提出&碰撞检测&的概念
在第2篇博文中提出了&碰撞检测&的概念:
用于检测炸弹人与砖墙、炸弹人与怪物等之间的碰撞。碰撞检测包括矩形碰撞、多边形碰撞等,一般使用矩形碰撞即可。
此处我采用矩形碰撞检测。
增加地形数据TerrainData
首先,我们需要一个存储地图中哪些区域能够通过,哪些区域不能通过的数据结构。
通过参考地图数据mapData,我决定数据结构选用二维数组,且地形数组与地图数组一一对应。
地图数据MapData
(function () {
var ground = bomberConfig.map.type.GROUND,
wall = bomberConfig.map.type.WALL;
var mapData = [
[ground, wall, ground, ground],
[ground, ground, ground, ground],
[ground, wall, ground, ground],
[ground, wall, ground, ground]
window.mapData = mapD
地形数据TerrainData
//地形数据
(function () {
  //0表示可以通过,1表示不能通过
var terrainData = [
[0, 1, 0, 0],
[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0]
window.terrainData = terrainD
重构TerrainData
受到MapData的启示,可以在Config中加入地形数据的枚举值(pass、stop),然后直接在TerrainData中使用枚举值。这样做有以下的好处:
增强可读性
枚举值放到Config中,方便统一管理
terrain: {
TerrainData
//地形数据
(function () {
var pass = bomberConfig.map.terrain.pass,
stop = bomberConfig.map.terrain.
var terrainData = [
[pass, stop, pass, pass],
[pass, pass, pass, pass],
[pass, stop, pass, pass],
[pass, stop, pass, pass]
window.terrainData = terrainD
在PlayerSprite中实现矩形碰撞检测
实现checkCollideWithMap方法:
_checkCollideWithMap: function () {
var i1 = Math.floor((this.y) / bomberConfig.HEIGHT),
i2 = Math.floor((this.y + bomberConfig.player.IMGHEIGHT - 1) / bomberConfig.HEIGHT),
j1 = Math.floor((this.x) / bomberConfig.WIDTH),
j2 = Math.floor((this.x + bomberConfig.player.IMGWIDTH - 1) / bomberConfig.WIDTH),
terrainData = window.terrainData,
pass = bomberConfig.map.terrain.pass,
stop = bomberConfig.map.terrain.
if (terrainData[i1][j1] === pass && terrainData[i1][j2] === pass
&& terrainData[i2][j1] === pass && terrainData[i2][j2] === pass) {
return false;
return true;
在move中判断:
move: function () {
var origin_x = this.x,
origin_y = this.y;
this.x = this.x + this.speedX * this.dirX;
this.y = this.y + this.speedY * this.dirY;
this._limitMove();
if (this._checkCollideWithMap()) {
this.x = origin_x;
this.y = origin_y;
设置移动步长
如果炸弹人每次移动0.2个方格,炸弹人想通过两个障碍物之间的空地,则炸弹人所在矩形区域必须与空地区域平行时才能通过。这通常导致玩家需要调整多次才能顺利通过。
如图所示:
引入&移动步长&概念  
结合参考资料&&,这里可以引出&移动步长&的概念:
即炸弹人一次移动一个地图方格(炸弹人一次会移动多步)。即如果一个方格长为10px,而游戏每次主循环轮询时炸弹人移动2px,则炸弹人一次需要移动5步。在炸弹人的一个移动步长完成之前,玩家不能操作炸弹人,直到炸弹人完成一个移动步长(即移动了一个方格),玩家才能操作炸弹人。
实现移动步长
这里先提出以下概念:
移动步数,炸弹人移动一个方格需要的步数
completeOneMove(该标志会在后面重构中被删除)
炸弹人完成一个移动步长的标志
炸弹人正在移动的标志
炸弹人在一次移动步长中已经移动的次数
首先在游戏开始时,计算一次炸弹人移动一个方格需要的步数;然后在移动前,先判断是否完成一次移动步长,如果正在移动且没有完成一次步长,则moveIndex加1;在移动后,判断该次移动是否完成移动步长,并相应更新移动标志和moveIndex。
将&moveIndex加1&移到状态类中
具体状态类的职责为:负责本状态的逻辑以及决定状态过渡。&moveIndex加1&这个职责属于&本状态的逻辑&,因此应该将其移到具体状态类中,封装为addIndex方法。
将按键判断移到PlayerSprite中
&&按键判断&是状态转换事件的判断,这里因为炸弹人不同状态转换为同一状态的触发事件相同,所以可以将其移到上一层的客户端(调用具体状态类的地方)中,即移到PlayerSprite的changeDir方法中。具体分析详见中的&将触发状态的事件判断移到Warrior类中&。
PlayerSprite
_computeCoordinate: function () {
this.x = this.x + this.speedX * this.dirX;
this.y = this.y + this.speedY * this.dirY;
this._limitMove();
//因为移动次数是向上取整,可能会造成移动次数偏多(如stepX为2.5,取整则stepX为3),
//坐标可能会偏大(大于bomberConfig.WIDTH / bomberConfig.HEIGHT的整数倍),
//因此此处需要向下取整。
if (this.completeOneMove) {
this.x -= this.x % bomberConfig.WIDTH;
this.y -= this.y % bomberConfig.HEIGHT;
//计算移动次数
_computeStep: function () {
this.stepX = Math.ceil(bomberConfig.WIDTH / this.speedX);
this.stepY = Math.ceil(bomberConfig.HEIGHT / this.speedY);
_allKeyUp: function () {
return window.keyState[keyCodeMap.A] === false && window.keyState[keyCodeMap.D] === false
&& window.keyState[keyCodeMap.W] === false && window.keyState[keyCodeMap.S] === false;
_judgeCompleteOneMoveByIndex: function () {
if (!this.moving) {
if (this.moveIndex_x &= this.stepX) {
this.moveIndex_x = 0;
this.completeOneMove = true;
else if (this.moveIndex_y &= this.stepY) {
this.moveIndex_y = 0;
this.completeOneMove = true;
this.completeOneMove = false;
_judgeAndSetDir: function () {
if (window.keyState[keyCodeMap.A] === true) {
this._context.walkLeft();
else if (window.keyState[keyCodeMap.D] === true) {
this._context.walkRight();
else if (window.keyState[keyCodeMap.W] === true) {
this._context.walkUp();
else if (window.keyState[keyCodeMap.S] === true) {
this._context.walkDown();
//一次移动步长中的需要移动的次数
//一次移动步长中已经移动的次数
moveIndex_x: 0,
moveIndex_y: 0,
//是否正在移动标志
moving: false,
//完成一次移动标志
completeOneMove: false,
init: function () {
this._context.setPlayerState(this._getCurrentState());
this._computeStep();
this.setAnim(this.defaultAnimId);
move: function () {
this._judgeCompleteOneMoveByIndex();
this._computeCoordinate();
changeDir: function () {
if (!this.completeOneMove && this.moving) {
this._context.addIndex();
if (this._allKeyUp()) {
this._context.stand();
this._judgeAndSetDir();
(function () {
var Context = YYC.Class({
Init: function (sprite) {
this.sprite =
Private: {
_state: null
sprite: null,
setPlayerState: function (state) {
this._state =
this._state.setContext(this);
walkLeft: function () {
this._state.walkLeft();
walkRight: function () {
this._state.walkRight();
walkUp: function () {
this._state.walkUp();
walkDown: function () {
this._state.walkDown();
stand: function () {
this._state.stand();
addIndex: function () {
this._state.addIndex();
walkLeftState: new WalkLeftState(),
walkRightState: new WalkRightState(),
walkUpState: new WalkUpState(),
walkDownState: new WalkDownState(),
standLeftState: new StandLeftState(),
standRightState: new StandRightState(),
standUpState: new StandUpState(),
standDownState: new StandDownState()
window.Context = C
WalkLeftState(此处只举一个状态类说明,其它状态类与该类类似):
        walkLeft: function () {
var sprite = this.P_context.
sprite.dirX = -1;
sprite.dirY = 0;
sprite.setAnim("walk_left");
sprite.moving = true;
this.addIndex();
addIndex: function () {
this.P_context.sprite.moveIndex_x += 1;
继续完成碰撞检测
对地图障碍物检测进行了修改,并将碰撞检测和边界检测移到具体状态类中。
WalkLeftState(此处只举一个状态类说明,其它状态类与该类类似)
walkLeft: function () {
var sprite = this.P_context.
sprite.setAnim("walk_left");
if (!this.checkPassMap()) {
sprite.moving = false;
sprite.dirX = 0;
sprite.dirX = -1;
sprite.dirY = 0;
sprite.moving = true;
this.addIndex();
//检测是否可通过该地图。可以通过返回true,不能通过返回false
checkPassMap: function () {
return !this.checkCollideWithBarrier();
checkCollideWithBarrier: function () {
var pass = bomberConfig.map.terrain.pass,
stop = bomberConfig.map.terrain.
//计算目的地地形数组下标
var target_x = this.P_context.sprite.x / bomberConfig.WIDTH - 1,
target_y = this.P_context.sprite.y / bomberConfig.HEIGHT;
//超出边界
if (target_x &= terrainData.length || target_y &= terrainData[0].length) {
return true;
if (target_x & 0) {
return true;
if (window.terrainData[target_y][target_x] === stop) {
return true;
return false;
重构PlayerSprite
将move移到状态类中
PlayerSprite的move方法负责炸弹人的移动,其应该属于具体状态类的职责(负责本状态的逻辑),故将PlayerSprite的move移到具体状态类中。
进一步分析
将PlayerSprite的move移到具体状态类中,从职责上来进一步分析,实质是将&炸弹人移动&的职责分散到各个具体状态类中了(如WalkLeftState、WalkRightState只负责X方向的移动,WalkUpState、WalkDownState只负责Y方向的移动)
增加了细粒度的控制。可以控制各个具体状态类下炸弹人的移动。
不好统一管理。当想修改&炸弹人移动&的逻辑时,可能需要修改每个具体状态类的move。
不过这个缺点可以在后面的提取具体状态类的基类的重构中解决。因为该重构会将具体状态类中&炸弹人移动&的职责汇聚到基类中。
重构addIndex
现在PlayerSprite -& changeDir中不用调用addIndex方法了,可以直接在具体状态类的move方法中调用。
这样做的好处是具体状态类不用再公开addIndex方法了,而是将其私有化。
为什么把公有方法addIndex改为私有方法比较好?
这是因为改动一个类的私有成员时,只会影响到该类,而不会影响到与该类关联的其它类;而改动公有成员则可能会影响与之关联的其它类。特别当我们是在创建供别人使用的类库时,如果发布后再来修改公有成员,会对很多人造成影响!这也是符合&高内聚低耦合&的思想。
我们应该对公有权限保持警惕的态度,能设成私有的就私有,只公开必要的接口成员。
PlayerSprite
move: function () {
this._context.move();
WalkLeftState(WalkRightState与之类似)
move: function () {
if (this.P_context.sprite.moving) {
this.addIndex();
this.__judgeCompleteOneMoveByIndex();
this.__computeCoordinate();
__addIndex: function(){
this.P_context.sprite.moveIndex_x += 1;
__judgeCompleteOneMoveByIndex: function () {
var sprite = this.P_context.
if (!sprite.moving) {
if (sprite.moveIndex_x &= sprite.stepX) {
sprite.moveIndex_x = 0;
pleteOneMove = true;
pleteOneMove = false;
__computeCoordinate: function () {
var sprite = this.P_context.
sprite.x = sprite.x + sprite.speedX * sprite.dirX;
//因为移动次数是向上取整,可能会造成移动次数偏多(如stepX为2.5,取整则stepX为3),
//坐标可能会偏大(大于bomberConfig.WIDTH / bomberConfig.HEIGHT的整数倍),
//因此此处需要向下取整。
//x、y为bomberConfig.WIDTH/bomberConfig.HEIGHT的整数倍(向下取整)
if (pleteOneMove) {
sprite.x -= sprite.x % bomberConfig.WIDTH;
WalkUpState(WalkDownState与之类似)
move: function () {
if (this.P_context.sprite.moving) {
this.addIndex();
this.__judgeCompleteOneMoveByIndex();
this.__computeCoordinate();
__addIndex: function(){
this.P_context.sprite.moveIndex_y += 1;
__judgeCompleteOneMoveByIndex: function () {
var sprite = this.P_context.
if (!sprite.moving) {
if (sprite.moveIndex_y &= sprite.stepY) {
sprite.moveIndex_y = 0;
pleteOneMove = true;
pleteOneMove = false;
__computeCoordinate: function () {
var sprite = this.P_context.
sprite.y = sprite.y + sprite.speedY * sprite.dirY;
//因为移动次数是向上取整,可能会造成移动次数偏多(如stepX为2.5,取整则stepX为3),
//坐标可能会偏大(大于bomberConfig.WIDTH / bomberConfig.HEIGHT的整数倍),
//因此此处需要向下取整。
//x、y为bomberConfig.WIDTH/bomberConfig.HEIGHT的整数倍(向下取整)
if (pleteOneMove) {
sprite.y -= sprite.y % bomberConfig.HEIGHT;
重构状态模式
让我们来看看状态类。
我发现具体状态类有很多重复的代码,有些方法有很多相似之处。这促使我提炼出一个高层的共同模式。具体的方法就是提炼出基类,然后用模板模式,在子类中实现不同点。
提炼出WalkState、StandState
因此,我从WalkLeftState,WalkRightState,WalkDownState,WalkUpState中提炼出基类WalkState,从StandLeftState、StandRightState、StandDownState、StandUpState中提炼出基类StandState。
提炼出WalkState_X、WalkState_Y
我发现在WalkLeftState,WalkRightState中和WalkDownState,WalkUpState中,它们分别有共同的模式,而这共同模式不能提到WalkState中。因此,我又从WalkLeftState,WalkRightState中提炼出WalkState_X,WalkDownState,WalkUpState中提炼出WalkState_Y,然后让WalkState_X和WalkState_Y继承于WalkState。
状态模式最新的领域模型
PlayerState
(function () {
var PlayerState = YYC.AClass({
Protected: {
P_context: null
setContext: function (context) {
this.P_context =
Abstract: {
stand: function () { },
walkLeft: function () { },
walkRight: function () { },
walkUp: function () { },
walkDown: function () { },
move: function () { }
window.PlayerState = PlayerS
(function () {
var WalkState = YYC.AClass(PlayerState, {
Protected: {
//*子类可复用的代码
P__checkMapAndSetDir: function () {
var sprite = this.P_context.
this.P__setDir();
if (!this.__checkPassMap()) {
sprite.moving = false;
//sprite.dirX = 0;
this.P__stop();
sprite.moving = true;
Abstract: {
P__setPlayerState: function () { },
//计算并返回目的地地形数组下标
P__computeTarget: function () { },
//检测是否超出地图边界。
//超出返回true,否则返回false
P__checkBorder: function () { },
//设置方向
P__setDir: function () { },
P__stop: function () { }
Private: {
//检测是否可通过该地图。可以通过返回true,不能通过返回false
__checkPassMap: function () {
//计算目的地地形数组下标
var target = this.P__computeTarget();
if (this.P__checkBorder(target)) {
return false;
return !this.__checkCollideWithBarrier(target);
//地形障碍物碰撞检测
__checkCollideWithBarrier: function (target) {
var stop = bomberConfig.map.terrain.
if (window.terrainData[target.y][target.x] === stop) {
return true;
return false;
stand: function () {
this.P__setPlayerState();
this.P_context.stand();
this.P_context.sprite.resetCurrentFrame(0);
this.P_context.sprite.stand = true;
Virtual: {
walkLeft: function () {
this.P_context.setPlayerState(Context.walkLeftState);
this.P_context.walkLeft();
this.P_context.sprite.resetCurrentFrame(0);
walkRight: function () {
this.P_context.setPlayerState(Context.walkRightState);
this.P_context.walkRight();
this.P_context.sprite.resetCurrentFrame(0);
walkUp: function () {
this.P_context.setPlayerState(Context.walkUpState);
this.P_context.walkUp();
this.P_context.sprite.resetCurrentFrame(0);
walkDown: function () {
this.P_context.setPlayerState(Context.walkDownState);
this.P_context.walkDown();
this.P_context.sprite.resetCurrentFrame(0);
Abstract: {
move: function () {
window.WalkState = WalkS
WalkState_X
(function () {
var WalkState_X = YYC.AClass(WalkState, {
Protected: {
Private: {
__judgeCompleteOneMoveByIndex: function () {
var sprite = this.P_context.
if (sprite.moveIndex_x &= sprite.stepX) {
sprite.moveIndex_x = 0;
sprite.moving = false;
sprite.moving = true;
__computeCoordinate: function () {
var sprite = this.P_context.
sprite.x = sprite.x + sprite.speedX * sprite.dirX;
__roundingDown: function () {
this.P_context.sprite.x -= this.P_context.sprite.x % bomberConfig.WIDTH;
move: function () {
if (!this.P_context.sprite.moving) {
this.__roundingDown();
this.P_context.sprite.moveIndex_x += 1;
this.__judgeCompleteOneMoveByIndex();
this.__computeCoordinate();
Abstract: {
window.WalkState_X = WalkState_X;
WalkState_Y
(function () {
var WalkState_Y = YYC.AClass(WalkState, {
Protected: {
Private: {
__judgeCompleteOneMoveByIndex: function () {
var sprite = this.P_context.
if (sprite.moveIndex_y &= sprite.stepY) {
sprite.moveIndex_y = 0;
sprite.moving = false;
sprite.moving = true;
__computeCoordinate: function () {
var sprite = this.P_context.
sprite.y = sprite.y + sprite.speedY * sprite.dirY;
__roundingDown: function () {
this.P_context.sprite.y -= this.P_context.sprite.y % bomberConfig.WIDTH;
move: function () {
if (!this.P_context.sprite.moving) {
this.__roundingDown();
this.P_context.sprite.moveIndex_y += 1;
this.__judgeCompleteOneMoveByIndex();
this.__computeCoordinate();
Abstract: {
window.WalkState_Y = WalkState_Y;
WalkLeftState
(function () {
var WalkLeftState = YYC.Class(WalkState_X, {
Protected: {
P__setPlayerState: function () {
this.P_context.setPlayerState(Context.standLeftState);
P__computeTarget: function () {
var sprite = this.P_context.
x: sprite.x / window.bomberConfig.WIDTH - 1,
y: sprite.y / window.bomberConfig.HEIGHT
P__checkBorder: function (target) {
if (target.x & 0) {
return true;
return false;
P__setDir: function () {
var sprite = this.P_context.
sprite.setAnim("walk_left");
sprite.dirX = -1;
P__stop: function () {
var sprite = this.P_context.
sprite.dirX = 0;
walkLeft: function () {
this.P__checkMapAndSetDir();
window.WalkLeftState = WalkLeftS
WalkRightState
(function () {
var WalkRightState = YYC.Class(WalkState_X, {
Protected: {
P__setPlayerState: function () {
this.P_context.setPlayerState(Context.standRightState);
P__computeTarget: function () {
var sprite = this.P_context.
x: sprite.x / window.bomberConfig.WIDTH + 1,
y: sprite.y / window.bomberConfig.HEIGHT
P__checkBorder: function (target) {
if (target.x &= window.terrainData[0].length) {
return true;
return false;
P__setDir: function () {
var sprite = this.P_context.
sprite.setAnim("walk_right");
sprite.dirX = 1;
P__stop: function () {
var sprite = this.P_context.
sprite.dirX = 0;
walkRight: function () {
this.P__checkMapAndSetDir();
window.WalkRightState = WalkRightS
WalkDownState
(function () {
var WalkDownState = YYC.Class(WalkState_Y, {
Protected: {
P__setPlayerState: function () {
this.P_context.setPlayerState(Context.standDownState);
P__computeTarget: function () {
var sprite = this.P_context.
x: sprite.x / window.bomberConfig.WIDTH,
y: sprite.y / window.bomberConfig.HEIGHT + 1
P__checkBorder: function (target) {
if (target.y &= window.terrainData.length) {
return true;
return false;
P__setDir: function () {
var sprite = this.P_context.
sprite.setAnim("walk_down");
sprite.dirY = 1;
P__stop: function () {
var sprite = this.P_context.
sprite.dirY = 0;
Private: {
walkDown: function () {
this.P__checkMapAndSetDir();
window.WalkDownState = WalkDownS
WalkUpState
(function () {
var WalkUpState = YYC.Class(WalkState_Y, {
Protected: {
P__setPlayerState: function () {
this.P_context.setPlayerState(Context.standUpState);
P__computeTarget: function () {
var sprite = this.P_context.
x: sprite.x / window.bomberConfig.WIDTH,
y: sprite.y / window.bomberConfig.HEIGHT - 1
P__checkBorder: function (target) {
if (target.y & 0) {
return true;
return false;
P__setDir: function () {
var sprite = this.P_context.
sprite.setAnim("walk_up");
sprite.dirY = -1;
P__stop: function () {
var sprite = this.P_context.
sprite.dirY = 0;
walkUp: function () {
this.P__checkMapAndSetDir();
window.WalkUpState = WalkUpS
StandState
(function () {
var StandState = YYC.AClass(PlayerState, {
Protected: {
walkLeft: function () {
this.P_context.sprite.resetCurrentFrame(0);
this.P_context.setPlayerState(Context.walkLeftState);
this.P_context.walkLeft();
walkRight: function () {
this.P_context.sprite.resetCurrentFrame(0);
this.P_context.setPlayerState(Context.walkRightState);
this.P_context.walkRight();
walkUp: function () {
this.P_context.sprite.resetCurrentFrame(0);
this.P_context.setPlayerState(Context.walkUpState);
this.P_context.walkUp();
walkDown: function () {
this.P_context.sprite.resetCurrentFrame(0);
this.P_context.setPlayerState(Context.walkDownState);
this.P_context.walkDown();
move: function () {
Abstract: {
window.StandState = StandS
StandLeftState
(function () {
var StandLeftState = YYC.Class(StandState, {
stand: function () {
var sprite = this.P_context.
sprite.dirX = 0;
sprite.setAnim("stand_left");
sprite.moving = false;
window.StandLeftState = StandLeftS
StandRightState
(function () {
var StandRightState = YYC.Class(StandState, {
stand: function () {
var sprite = this.P_context.
sprite.dirX = 0;
sprite.setAnim("stand_right");
sprite.moving = false;
window.StandRightState = StandRightS
StandDownState
(function () {
var StandDownState = YYC.Class(StandState, {
stand: function () {
var sprite = this.P_context.
sprite.dirY = 0;
sprite.setAnim("stand_down");
sprite.moving = false;
window.StandDownState = StandDownS
StandUpState
(function () {
var StandUpState = YYC.Class(StandState, {
stand: function () {
var sprite = this.P_context.
sprite.dirY = 0;
sprite.setAnim("stand_up");
sprite.moving = false;
window.StandUpState = StandUpS
重构PlayerSprite
changeDir改名为setDir
该方法会在游戏主循环中调用,并不会每次轮询时都改变炸弹人移动方向,因此changDir这个方法名不合理,改为setDir更为合适。
删除completeOneMove
现在可以不需要completeOneMove标志了,故将其删除。&
重构后的PlayerSprite
(function () {
var PlayerSprite = YYC.Class({
Init: function (data) {
//初始坐标
this.x = data.x;
this.y = data.y;
this.speedX = data.speedX;
this.speedY = data.speedY;
//x/y坐标的最大值和最小值, 可用来限定移动范围.
this.minX = data.minX;
this.maxX = data.maxX;
this.minY = data.minY;
this.maxY = data.maxY;
this.defaultAnimId = data.defaultAnimId;
this.anims = data.
this.walkSpeed = data.walkS
this.speedX = data.walkS
this.speedY = data.walkS
this._context = new Context(this);
Private: {
//状态模式上下文类
_context: null,
//更新帧动画
_updateFrame: function (deltaTime) {
if (this.currentAnim) {
this.currentAnim.update(deltaTime);
_computeCoordinate: function () {
this.x = this.x + this.speedX * this.dirX;
this.y = this.y + this.speedY * this.dirY;
//因为移动次数是向上取整,可能会造成移动次数偏多(如stepX为2.5,取整则stepX为3),
//坐标可能会偏大(大于bomberConfig.WIDTH / bomberConfig.HEIGHT的整数倍),
//因此此处需要向下取整。
//x、y为bomberConfig.WIDTH/bomberConfig.HEIGHT的整数倍(向下取整)
if (this.completeOneMove) {
this.x -= this.x % bomberConfig.WIDTH;
this.y -= this.y % bomberConfig.HEIGHT;
_getCurrentState: function () {
var currentState = null;
switch (this.defaultAnimId) {
case "stand_right":
currentState = Context.standRightS
case "stand_left":
currentState = Context.standLeftS
case "stand_down":
currentState = Context.standDownS
case "stand_up":
currentState = Context.standUpS
case "walk_down":
currentState = Context.walkDownS
case "walk_up":
currentState = Context.walkUpS
case "walk_right":
currentState = Context.walkRightS
case "walk_left":
currentState = Context.walkLeftS
throw new Error("未知的状态");
return currentS
//计算移动次数
_computeStep: function () {
this.stepX = Math.ceil(bomberConfig.WIDTH / this.speedX);
this.stepY = Math.ceil(bomberConfig.HEIGHT / this.speedY);
_allKeyUp: function () {
return window.keyState[keyCodeMap.A] === false && window.keyState[keyCodeMap.D] === false
&& window.keyState[keyCodeMap.W] === false && window.keyState[keyCodeMap.S] === false;
_judgeCompleteOneMoveByIndex: function () {
if (!this.moving) {
if (this.moveIndex_x &= this.stepX) {
this.moveIndex_x = 0;
this.completeOneMove = true;
else if (this.moveIndex_y &= this.stepY) {
this.moveIndex_y = 0;
this.completeOneMove = true;
this.completeOneMove = false;
_judgeAndSetDir: function () {
if (window.keyState[keyCodeMap.A] === true) {
this._context.walkLeft();
else if (window.keyState[keyCodeMap.D] === true) {
this._context.walkRight();
else if (window.keyState[keyCodeMap.W] === true) {
this._context.walkUp();
else if (window.keyState[keyCodeMap.S] === true) {
this._context.walkDown();
//精灵的坐标
//精灵的速度
speedX: 0,
speedY: 0,
//精灵的坐标区间
maxX: 9999,
maxY: 9999,
//精灵包含的所有 Animation 集合. Object类型, 数据存放方式为" id : animation ".
anims: null,
//默认的Animation的Id , string类型
defaultAnimId: null,
//当前的Animation.
currentAnim: null,
//精灵的方向系数:
//往下走dirY为正数,往上走dirY为负数;
//往右走dirX为正数,往左走dirX为负数。
//定义sprite走路速度的绝对值
walkSpeed: 0,
//一次移动步长中的需要移动的次数
//一次移动步长中已经移动的次数
moveIndex_x: 0,
moveIndex_y: 0,
//是否正在移动标志
moving: false,
//站立标志
//用于解决调用WalkState.stand后,PlayerLayer.render中P__isChange返回false的问题
//(不调用draw,从而仍会显示精灵类walk的帧(而不会刷新为更新状态后的精灵类stand的帧))。
stand: false,
//设置当前Animation, 参数为Animation的id, String类型
setAnim: function (animId) {
this.currentAnim = this.anims[animId];
//重置当前帧
resetCurrentFrame: function (index) {
this.currentAnim && this.currentAnim.setCurrentFrame(index);
init: function () {
this._context.setPlayerState(this._getCurrentState());
this._computeStep();
//设置当前Animation
this.setAnim(this.defaultAnimId);
// 更新精灵当前状态
update: function (deltaTime) {
this._updateFrame(deltaTime);
draw: function (context) {
var frame = null;
if (this.currentAnim) {
frame = this.currentAnim.getCurrentFrame();
context.drawImage(this.currentAnim.getImg(), frame.x, frame.y, frame.width, frame.height, this.x, this.y, frame.imgWidth, frame.imgHeight);
clear: function (context) {
var frame = null;
if (this.currentAnim) {
frame = this.currentAnim.getCurrentFrame();
//直接清空画布区域
context.clearRect(0, 0, bomberConfig.canvas.WIDTH, bomberConfig.canvas.HEIGHT);
move: function () {
this._context.move();
setDir: function () {
if (this.moving) {
if (this._allKeyUp()) {
this._context.stand();
this._judgeAndSetDir();
window.PlayerSprite = PlayerS
本文最终领域模型
与上文相同,没有增加新的包
对应领域模型
辅助操作层
控件包PreLoadImg
配置包Config
用户交互层
入口包Main
业务逻辑层
工厂包BitmapFactory、LayerFactory、SpriteFactory
事件管理包KeyState、KeyEventManager
游戏主逻辑
主逻辑包Game
层管理实现包PlayerLayerManager、MapLayerManager
层管理抽象包
LayerManager
层实现包PlayerLayer、MapLayer
层抽象包Layer
集合包Collection
精灵包PlayerSprite、Context、PlayerState、WalkState、StandState、WalkState_X、WalkState_Y、StandLeftState、StandRightState、StandUpState、StandDownState、WalkLeftState、WalkRightState、WalkUpState、WalkDownState
动画包Animation、GetSpriteData、SpriteData、GetFrames、FrameData
数据操作层
地图数据操作包MapDataOperate
路径数据操作包GetPath
图片数据操作包Bitmap
地图包MapData、TerrainData
图片路径包ImgPathData
本文参考资料
欢迎浏览上一篇博文:
欢迎浏览下一篇博文:
阅读(...) 评论()}

我要回帖

更多关于 炮弹小分队怎么联机 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信