有人买过这款类似于买房记的游戏炸弹人的游戏吗目前正在打折,好像多

【图片】有人买过这款类似炸弹人的游戏吗目前正在打折,好像多人玩,挺欢乐的,求测评【ns吧】_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0可签7级以上的吧50个
本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:136,859贴子:
有人买过这款类似炸弹人的游戏吗目前正在打折,好像多人玩,挺欢
有人买过这款类似炸弹人的游戏吗目前正在打折,好像多人玩,挺欢乐的,求测评
看来没人玩过
请你来做个测试吧
这不就是盗版的泡泡堂么
看来好多人都没玩过,我就当个小白鼠有故事模式还有多人的模式就是每个角色都有自己特定的技能我就玩了第一个 他的技能如果炸到敌人 就是会使对手 方向相反
电脑上玩过,个人觉得比泡泡糖好玩,但ns版没中文……所以我在纠结要不要买
多人模式等我找到小伙伴玩一下再测评
最多几个人玩
能不能一块闯关
等待详细评测
我的炸弹人买了就没怎么玩,3d看的我头晕
这个港版不能下吗?
这个看起来不错
哪个服打折,有中文吗?
等塞尔达过了以后再来马车
7.99是多少钱?
steam上买过
mark,明天上eshop看看
我的是美服的号,怎么买数字版
马,但是我有炸弹人了…
一句话,比炸弹人好玩不?好的话,我就入了
贴吧热议榜
使用签名档&&
保存至快速回贴查看: 2145|回复: 6
骑士, 积分 1625, 距离下一级还需 1375 积分
精华0帖子威望0 点积分1625 点注册时间最后登录
本帖最后由 l9353223 于
22:58 编辑
素质如何,个人还是比较想买一款类似炸弹人的游戏偶尔耍一耍的,这个游戏打折后价格也很便宜8美刀,另外还有一款炸弹人类型2D像素风格的游戏《bombslinger》(支持中文)
喜欢炸弹人类型游戏可以关注一下
公民, 积分 120, 距离下一级还需 180 积分
精华0帖子威望0 点积分120 点注册时间最后登录
哪个好玩?brawl看着舒服一点,可惜没中文,不知道有没有道具什么的
骑士, 积分 1625, 距离下一级还需 1375 积分
精华0帖子威望0 点积分1625 点注册时间最后登录
YY鱼 发表于
哪个好玩?brawl看着舒服一点,可惜没中文,不知道有没有道具什么的
steam上有评价褒贬不一,可惜网上没有实际的游戏视频作为参考
公民, 积分 120, 距离下一级还需 180 积分
精华0帖子威望0 点积分120 点注册时间最后登录
l9353223 发表于
steam上有评价褒贬不一,可惜网上没有实际的游戏视频作为参考
搜了一圈,居然一个视频都没有,不知道买哪个好了
骑士, 积分 1625, 距离下一级还需 1375 积分
精华0帖子威望0 点积分1625 点注册时间最后登录
没人玩,超级冷门
骑士, 积分 1625, 距离下一级还需 1375 积分
精华0帖子威望0 点积分1625 点注册时间最后登录
YY鱼 发表于
搜了一圈,居然一个视频都没有,不知道买哪个好了
外媒的评价,评语基本上说游戏还可以,但是在操作上有个严重的问题可能会让部分玩家无法接受。
斗殴是一种令人愉快的游戏,它成功地纠正了它的前任所犯的错误。一个非常好的艺术风格和出色的声音才能使这个可怕的主题令人愉快。每个角色都有自己的单人故事和一个普通的本地多人模式,这意味着无人居住的多人模式不会让这个游戏避免。遗憾的是,在任何一个部门,它都不擅长,对一些人来说,缓慢而笨拙的控制可能会带来麻烦。
公民, 积分 120, 距离下一级还需 180 积分
精华0帖子威望0 点积分120 点注册时间最后登录
l9353223 发表于
http://www.psnation.com//review-brawl-ps4/
外媒的评价,评语基本上说游戏还可以,但是在操 ...
那还是买有中文的吧,就是画风有点不喜欢
Powered by
扫描二维码
下载 A9VG 客户端(iOS, Android)上文中我们实现了炸弹人与墙的碰撞检测,以及设置移动步长来解决发现的问题。本文会加入1个AI敌人,敌人使用A*算法追踪炸弹人。
加入敌人,追踪炸弹人
本文主要内容
回顾上文更新后的领域模型
首先实现&加入敌人&功能。通过参考&&中的实现,可以初步分析出需要加入敌人图片、敌人帧数据和精灵数据、敌人精灵类EnemySprite、敌人层EnemyLayer和敌人层管理类EnemyLayerManager。
然后实现&追踪炸弹人&功能。需要新建一个算法类FindPath,负责使用A*算法计算并返回路径数据。
敌人精灵类与算法类的交互关系:
可以并行开发&加入敌人&和&追踪炸弹人&。
先定义一个FindPath类的的接口,指定findPath方法输入参数和返回参数的格式。
实现&加入敌人&功能时,可以按照接口指定的格式使用假的路径数据来测试EnemySprite类;实现&追踪炸弹人&功能时,按照接口指定格式使用假的坐标数据来测试FindPath类。
在EnemySprit和FindPath都实现后,再集成在一起测试。因为两者接口一致,因此集成时不会有什么困难。
现在地图大小为4*4,太小了。
加入一个敌人后:
可玩性太低很快游戏就结束了;玩家操作炸弹人躲避敌人的空间太小了。
不方便演示和测试游戏由于游戏很快就结束,因此不方便演示和测试游戏。
因此,将地图扩大为20*20。
要实现这个功能,只需要修改MapData和TerrainData即可。
//地图数据
(function () {
var ground = bomberConfig.map.type.GROUND,
wall = bomberConfig.map.type.WALL;
var mapData = [
wall, ground, ground, ground, ground,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground
wall, ground, wall, wall, wall,
wall, wall, wall, ground, ground,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground
wall, ground, ground, ground, wall,
ground, ground, wall, wall, ground,
ground, ground, ground, wall, ground,
ground, ground, ground, ground, ground
wall, ground, ground, ground, wall,
ground, ground, wall, wall, wall,
ground, wall, ground, wall, ground,
ground, ground, ground, ground, ground
wall, ground, ground, ground, wall,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground
wall, ground, wall, ground, ground,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground
wall, ground, ground, ground, wall,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground
wall, ground, ground, ground, wall,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground
wall, ground, ground, ground, wall,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground
wall, ground, ground, ground, wall,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground
wall, ground, ground, ground, wall,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground
wall, ground, ground, ground, wall,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground
wall, ground, ground, ground, wall,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground
wall, ground, ground, ground, wall,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground
wall, ground, ground, ground, wall,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground
wall, ground, ground, ground, wall,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground
wall, ground, ground, ground, wall,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground
wall, ground, ground, ground, wall,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground
wall, ground, ground, ground, wall,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground
wall, ground, ground, ground, wall,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground,
ground, ground, ground, ground, ground
window.mapData = mapD
TerrainData
(function () {
var pass = bomberConfig.map.terrain.pass,
stop = bomberConfig.map.terrain.
var terrainData = [
stop, pass, pass, pass, pass,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass
stop, pass, stop, stop, stop,
stop, stop, stop, pass, pass,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass
stop, pass, pass, pass, stop,
pass, pass, stop, stop, pass,
pass, pass, pass, stop, pass,
pass, pass, pass, pass, pass
stop, pass, pass, pass, stop,
pass, pass, stop, stop, stop,
pass, stop, pass, stop, pass,
pass, pass, pass, pass, pass
stop, pass, pass, pass, stop,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass
stop, pass, stop, pass, pass,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass
stop, pass, pass, pass, stop,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass
stop, pass, pass, pass, stop,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass
stop, pass, pass, pass, stop,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass
stop, pass, pass, pass, stop,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass
stop, pass, pass, pass, stop,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass
stop, pass, pass, pass, stop,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass
stop, pass, pass, pass, stop,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass
stop, pass, pass, pass, stop,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass
stop, pass, pass, pass, stop,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass
stop, pass, pass, pass, stop,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass
stop, pass, pass, pass, stop,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass
stop, pass, pass, pass, stop,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass
stop, pass, pass, pass, stop,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass
stop, pass, pass, pass, stop,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass,
pass, pass, pass, pass, pass
window.terrainData = terrainD
加入敌人图片和数据
首先加入敌人的精灵图片
然后加入敌人帧动画数据类
(function () {
var getEnemyFrames = (function () {
//一个动作在图片中的宽度
var width = bomberConfig.enemy.WIDTH,
//一个动作在图片中的高度
height = bomberConfig.enemy.HEIGHT,
//一个动作的偏移量
offset = {
x: bomberConfig.enemy.offset.X,
y: bomberConfig.enemy.offset.Y
//一个动作横向截取的长度
sw = bomberConfig.enemy.SW,
//一个动作纵向截取的长度
sh = bomberConfig.enemy.SH,
//一个动作图片在canvas中的宽度
imgWidth = bomberConfig.enemy.IMGWIDTH,
//一个动作图片在canvas中的高度
imgHeight = bomberConfig.enemy.IMGHEIGHT;
var frames = function () {
//向右站立
stand_right: {
img: window.imgLoader.get("enemy"),
{ x: offset.x, y: offset.y + 2 * height, width: sw, height: sh, imgWidth: imgWidth, imgHeight: imgHeight, duration: 100 }
//向左站立
stand_left: {
img: window.imgLoader.get("enemy"),
{ x: offset.x, y: offset.y + height, width: sw, height: sh, imgWidth: imgWidth, imgHeight: imgHeight, duration: 100 }
//向上站立
stand_up: {
img: window.imgLoader.get("enemy"),
{ x: offset.x, y: offset.y + 3 * height, width: sw, height: sh, imgWidth: imgWidth, imgHeight: imgHeight, duration: 100 }
//向下站立
stand_down: {
img: window.imgLoader.get("enemy"),
{ x: offset.x, y: offset.y, width: sw, height: sh, imgWidth: imgWidth, imgHeight: imgHeight, duration: 100 }
walk_up: {
img: window.imgLoader.get("enemy"),
{ x: offset.x, y: offset.y + 3 * height, width: sw, height: sh, imgWidth: imgWidth, imgHeight: imgHeight, duration: 100 },
{ x: offset.x + width, y: offset.y + 3 * height, width: sw, height: sh, imgWidth: imgWidth, imgHeight: imgHeight, duration: 100 },
{ x: offset.x + 2 * width, y: offset.y + 3 * height, width: sw, height: sh, imgWidth: imgWidth, imgHeight: imgHeight, duration: 100 },
{ x: offset.x + 3 * width, y: offset.y + 3 * height, width: sw, height: sh, imgWidth: imgWidth, imgHeight: imgHeight, duration: 100 }
walk_down: {
img: window.imgLoader.get("enemy"),
{ x: offset.x, y: offset.y, width: sw, height: sh, imgWidth: imgWidth, imgHeight: imgHeight, duration: 100 },
{ x: offset.x + width, y: offset.y, width: sw, height: sh, imgWidth: imgWidth, imgHeight: imgHeight, duration: 100 },
{ x: offset.x + 2 * width, y: offset.y, width: sw, height: sh, imgWidth: imgWidth, imgHeight: imgHeight, duration: 100 },
{ x: offset.x + 3 * width, y: offset.y, width: sw, height: sh, imgWidth: imgWidth, imgHeight: imgHeight, duration: 100 }
walk_right: {
img: window.imgLoader.get("enemy"),
{ x: offset.x, y: offset.y + 2 * height, width: sw, height: sh, imgWidth: imgWidth, imgHeight: imgHeight, duration: 100 },
{ x: offset.x + width, y: offset.y + 2 * height, width: sw, height: sh, imgWidth: imgWidth, imgHeight: imgHeight, duration: 100 },
{ x: offset.x + 2 * width, y: offset.y + 2 * height, width: sw, height: sh, imgWidth: imgWidth, imgHeight: imgHeight, duration: 100 },
{ x: offset.x + 3 * width, y: offset.y + 2 * height, width: sw, height: sh, imgWidth: imgWidth, imgHeight: imgHeight, duration: 100 }
walk_left: {
img: window.imgLoader.get("enemy"),
{ x: offset.x, y: offset.y + height, width: sw, height: sh, imgWidth: imgWidth, imgHeight: imgHeight, duration: 100 },
{ x: offset.x + width, y: offset.y + height, width: sw, height: sh, imgWidth: imgWidth, imgHeight: imgHeight, duration: 100 },
{ x: offset.x + 2 * width, y: offset.y + height, width: sw, height: sh, imgWidth: imgWidth, imgHeight: imgHeight, duration: 100 },
{ x: offset.x + 3 * width, y: offset.y + height, width: sw, height: sh, imgWidth: imgWidth, imgHeight: imgHeight, duration: 100 }
return function (animName) {
return frames()[animName];
window.getEnemyFrames = getEnemyF
然后加入敌人精灵类数据
(function () {
var getSpriteData = (function () {
var data = function(){
//敌人精灵类
//初始坐标
x: bomberConfig.WIDTH * 10,
y: bomberConfig.HEIGHT * 3,
//定义sprite走路速度的绝对值
walkSpeed: bomberConfig.enemy.speed.NORMAL,
speedX: 1,
speedY: 1,
//注意坐标起始点为图片左上点!
maxX: bomberConfig.canvas.WIDTH - bomberConfig.player.IMGWIDTH,
//maxX: 500,
maxY: bomberConfig.canvas.HEIGHT - bomberConfig.player.IMGHEIGHT,
defaultAnimId: "stand_left",
"stand_right": new Animation(getEnemyFrames("stand_right")),
"stand_left": new Animation(getEnemyFrames("stand_left")),
"stand_down": new Animation(getEnemyFrames("stand_down")),
"stand_up": new Animation(getEnemyFrames("stand_up")),
"walk_up": new Animation(getEnemyFrames("walk_up")),
"walk_down": new Animation(getEnemyFrames("walk_down")),
"walk_right": new Animation(getEnemyFrames("walk_right")),
"walk_left": new Animation(getEnemyFrames("walk_left"))
return function (spriteName) {
return data()[spriteName];
window.getSpriteData = getSpriteD
加入EnemySprite类
增加敌人精灵类。
创建假的A*算法类FindPath类
创建返回假数据的FindPath类,用于测试EnemySprite类。
(function () {
//构造假数据
var findPath = {
aCompute: function (terrainData, begin, target) {
path: [{ x: 8, y: 0 }, { x: 7, y: 0 }, { x: 6, y: 0 }, { x: 5, y: 0 }, { x: 4, y: 0 }],
window.findPath = findP
EnemySprite
(function () {
var EnemySprite = 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 = new StandRightState();
case "stand_left":
currentState = new StandLeftS
case "stand_down":
currentState = new StandDownS
case "stand_up":
currentState = new StandUpS
case "walk_down":
currentState = new WalkDownS
case "walk_up":
currentState = new WalkUpS
case "walk_right":
currentState = new WalkRightS
case "walk_left":
currentState = new WalkLeftS
throw new Error("未知的状态");
return currentS
//计算移动次数
__computeStep: function () {
this.stepX = Math.ceil(bomberConfig.WIDTH / this.speedX);
this.stepY = Math.ceil(bomberConfig.HEIGHT / this.speedY);
//精灵的坐标
//精灵的速度
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,
//寻找的路径
path: null,
//设置当前Animation, 参数为Animation的id, String类型
setAnim: function (animId) {
this.currentAnim = this.anims[animId];
init: function () {
this.__context.setPlayerState(this.__getCurrentState());
this.__computeStep();
this.path = window.findPath.aCompute().
//设置当前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) {
//返回并移除要移动到的坐标
var target = this.path.shift();
//当前坐标
var now = {
x: self.x / bomberConfig.WIDTH,
y: self.y / bomberConfig.HEIGHT
//判断要移动的方向,调用相应的方法
if (target.x & now.x) {
this.__context.walkRight();
else if (target.x & now.x) {
this.__context.walkLeft();
else if (target.y & now.y) {
this.__context.walkDown();
else if (target.y & now.y) {
this.__context.walkUp();
this.__context.stand();
window.EnemySprite = EnemyS
增加敌人精灵类工厂
SpriteFactory新增工厂方法createEnemy,用于创建EnemySprite实例。
SpriteFactory
createEnemy: function () {
return new EnemySprite(getSpriteData("enemy"));
新增EnemyLayer
增加敌人画布,该画布于地图画布之上,与玩家画布的zIndex相同。同时增加对应的敌人层类EnemyLayer,它的集合元素为EnemySprite类的实例。
EnemyLayer
(function () {
var EnemyLayer = YYC.Class(Layer, {
Init: function (deltaTime) {
this.___deltaTime = deltaT
Private: {
___deltaTime: 0,
___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();
___update: function (deltaTime) {
this.___iterator("update", deltaTime);
__setDir: function () {
this.___iterator("setDir");
___move: function (deltaTime) {
this.___iterator("move", deltaTime);
setCanvas: function () {
this.P__canvas = document.getElementById("enemyLayerCanvas");
$("#enemyLayerCanvas").css({
"position": "absolute",
"top": bomberConfig.canvas.TOP,
"left": bomberConfig.canvas.LEFT,
"border": "1px solid black",
"z-index": 1
//z-index与playerLayer相同
draw: function (context) {
this.___iterator("draw", context);
clear: function (context) {
this.___iterator("clear", context);
render: function () {
this.__setDir();
this.___move(this.___deltaTime);
//判断__state是否为change状态,如果是则调用canvas绘制精灵。
if (this.P__isChange()) {
this.clear(this.P__context);
this.___update(this.___deltaTime);
this.draw(this.P__context);
this.P__setStateNormal();
window.EnemyLayer = EnemyL
增加敌人层类工厂
LayerFactory新增工厂方法createEnemy,用于创建EnemyLayer实例
LayerFactory
createEnemy: function (deltaTime) {
return new EnemyLayer(deltaTime);
新增EnemyLayerManager
增加EnemyLayer的管理类EnemyLayerManager
var EnemyLayerManager = YYC.Class(LayerManager, {
Init: function (layer) {
this.base(layer);
createElement: function () {
var element = [],
enemy = spriteFactory.createEnemy();
enemy.init();
element.push(enemy);
change: function () {
this.layer.change();
实现寻路算法
游戏中的敌人采用A*算法寻路。参考资料:
敌人寻路模式
游戏开始时,敌人以它的当前位置为起始点,炸弹人的位置为终点寻路。如果敌人到达终点后,没有碰撞到炸弹人,则再一次以它的当前位置为起始点,炸弹人的位置为终点寻路。
敌人寻路流程
实现FindPath类
(function () {
var map_w, beginx, beginy, endx,
var arr_path_out = new Array();
var pass = bomberConfig.map.terrain.pass,
stop = bomberConfig.map.terrain.
var arr_map = new Array();
var open_list = new Array(); //创建OpenList
var close_list = new Array(); //创建CloseList
var tmp = new Array(); //存放当前节点的八个方向的节点
var arr_map_tmp = window.mapD //存储从游戏中读入的地图数据
var map_w = arr_map_tmp.
function aCompute(mapData, begin, end) {
//计算运行时间
var startTime, endT
var d = new Date();
startTime = d.getTime();
var arr_path = new Array();
var stopn = 0;
/********************函数主体部分*************************/
arr_map = setMap(mapData);
map_w = mapData.
beginx = begin.x;
beginy = map_w - 1 - begin.y;
endx = end.x;
endy = map_w - 1 - end.y;
var startNodeNum = tile_num(beginx, beginy);
var targetNodeNum = tile_num(endx, endy);
if (arr_map[targetNodeNum] && arr_map[targetNodeNum][0] == 0) {
showError("目的地无法到达!");
time = getTime(startTime);
return { path: [], time: time };
if (arr_map[startNodeNum][0] == 0) {
showError("起始点不可用!");
time = getTime(startTime);
return { path: [], time: time };
if (arr_map[targetNodeNum] && arr_map[targetNodeNum][0] * arr_map[startNodeNum][0] == 1) {
arr_map[startNodeNum] = [arr_map[startNodeNum][0], startNodeNum, arr_map[startNodeNum][2], arr_map[startNodeNum][3], arr_map[startNodeNum][4]];//起始点的父节点为自己
setH(targetNodeNum);
setOpenList(startNodeNum); //把开始节点加入到openlist中
//就要开始那个令人发指的循环了,==!!A*算法主体
while (open_list.length != 0) {
var bestNodeNum = selectFmin(open_list);
stopn = 0;
open_list.shift();
setCloseList(bestNodeNum);
if (bestNodeNum == targetNodeNum) {
showPath(close_list, arr_path);
var i = 0, j = 0;
//当目标为孤岛时的判断
var tmp0 = new Array();
tmp0 = setSuccessorNode(targetNodeNum, map_w);
for (j; j & 9; j++) {
if (j == 8) {
if (tmp0[j][0] == 1) {
//当目标为孤岛时的判断语句结束
if (k == 0) {
showError("目标成孤岛!");
time = getTime(startTime);
return { path: [], time: time };
tmp = setSuccessorNode(bestNodeNum, map_w);
for (i; i & 8; i++) {
if ((tmp[i][0] == 0) || (findInCloseList(tmp[i][4]))) continue;
if (findInOpenList(tmp[i][4]) == 1) {
if (tmp[i][2] &= (arr_map[bestNodeNum][2] + cost(tmp[i], bestNodeNum))) {
setG(tmp[i][4], bestNodeNum); //算g值,修改arr_map中[2]的值
arr_map[tmp[i][4]] = tmp[i] = [arr_map[tmp[i][4]][0], bestNodeNum, arr_map[tmp[i][4]][2], arr_map[tmp[i][4]][3], arr_map[tmp[i][4]][4]]; //修改tmp和arr_map中父节点的值,并修改tmp中g值,是之和arr_map中对应节点的值统一
if (findInOpenList(tmp[i][4]) == 0) {
setG(tmp[i][4], bestNodeNum); //算g值,修改arr_map中[2]的值
arr_map[tmp[i][4]] = tmp[i] = [arr_map[tmp[i][4]][0], bestNodeNum, arr_map[tmp[i][4]][2], arr_map[tmp[i][4]][3], arr_map[tmp[i][4]][4]]; //修改tmp和arr_map中父节点的值,并修改tmp中g值,是之和arr_map中对应节点的值统一
setOpenList(tmp[i][4]); //存进openlist中
//if (stopn == map_w * map_w - 1) {
if (stopn == map_w * map_w * 1000) {
showError("找不到路径!");
time = getTime(startTime);
return { path: [], time: time };
if (open_list.length == 0 && bestNodeNum != targetNodeNum) {
showError("没有找到路径!!");
//对于那种找不到路径的点的处理
time = getTime(startTime);
return { path: [], time: time };
time = getTime(startTime);
return { path: arr_path_out, time: time };
function getTime(startTime) {
/***显示运行时间********/
var endTime = new Date().getTime();
return (endTime - startTime) / 1000;
function showError(error) {
console.log(error);
/**********************************************************************
*function setMap(n)
*功能:把外部的地图数据抽象成该算法中可操作数组的形式来输入算法
*参数:n为地图的宽度,生成方阵地图
************************************************************************/
function setMap(mapData) {
map_w = mapData.
var m = map_w * map_w;
var arr_map0 = new Array(); //该函数对地图数据转换的操作数组
var a = m - 1;
for (a; a &= 0; a--) {
var xTmp = tile_x(a); //把ID 编号值转换为x坐标值,用来对应读入地图数据
var yTmp = map_w - 1 - tile_y(a); //把ID 编号值转换为y坐标值,用来对应读入地图数据,对应数组标号和我自定义xoy坐标位置
//[cost,parent,g,h,id]
if (mapData[yTmp][xTmp] == pass)
arr_map0[a] = [1, 0, 0, 0, a];
arr_map0[a] = [0, 0, 0, 0, a];
return arr_map0;
/*********************************************************************
*以下三个函数是地图上的编号与数组索引转换
*function tile_num(x,y)
*功能:将 x,y 坐标转换为地图上块的编号
*function tile_x(n)
*功能:由块编号得出 x 坐标
*function tile_y(n)
*功能:由块编号得出 y 坐标
******************************************************************/
function tile_num(x, y) {
return ((y) * map_w + (x));
function tile_x(n) {
return (parseInt((n) % map_w));
function tile_y(n) {
return (parseInt((n) / map_w));
/*********************************************************************
*function setH(targetNode)
*功能:初始化所有点H的值
*参数:targetNode目标节点
**********************************************************************/
function setH(targetNode) {
var x0 = tile_x(targetNode);
var y0 = tile_y(targetNode);
var i = 0;
for (i; i & arr_map. i++) {
var x1 = tile_x(i);
var y1 = tile_y(i);
/*****欧几里德距离********************************/
// var h = (Math.sqrt((parseInt(x0) - parseInt(x1)) * (parseInt(x0) - parseInt(x1))) + Math.sqrt((parseInt(y0) - parseInt(y1)) * (parseInt(y0) - parseInt(y1))));
/*****对角线距离********************************/
var h = Math.max(Math.abs(parseInt(x0) - parseInt(x1)), Math.abs(parseInt(y0) - parseInt(y1)));
/*****曼哈顿距离********************************/
// var h=Math.abs(parseInt(x0) - parseInt(x1))+Math.abs(parseInt(y0) - parseInt(y1));
arr_map[i][3] = h * parseInt(10);
/*********************************************************************
*function setG(nowNode,bestNode)
*功能:计算现节点G的值
*参数:nowNode现节点,bestNode其父节点
**********************************************************************/
function setG(nowNode, bestNode) {
var x0 = tile_x(bestNode);
var y0 = tile_y(bestNode);
var x1 = tile_x(nowNode);
var y1 = tile_y(nowNode);
if (((x0 - x1) == 0) || ((y0 - y1) == 0)) {
arr_map[nowNode] = [arr_map[nowNode][0], arr_map[nowNode][1], arr_map[nowNode][2] + parseInt(10), arr_map[nowNode][3], arr_map[nowNode][4]];
arr_map[nowNode] = [arr_map[nowNode][0], arr_map[nowNode][1], arr_map[nowNode][2] + parseInt(14), arr_map[nowNode][3], arr_map[nowNode][4]];
/*********************************************************************
*function selectFmin(open_list)
*功能:在openlist中对f值进行排序(冒泡排序),并选择一个f值最小的节点返回
*参数:openlist
***********************************************************************/
function selectFmin(open_list) {
var i, j, min,
for (i = 0; i & open_list. i++) {
for (j = i + 1; j & open_list. j++) {
if ((open_list[i][2] + open_list[i][3]) & (open_list[j][2] + open_list[j][3])) {
temp = open_list[i];
open_list[i] = open_list[j];
open_list[j] =
var min = open_list[0];
return min[4];
/***********************************************************************
*function setOpenList(NodeNum)
*功能:把节点加入open表中
*参数:待加入openlist的节点的编号
************************************************************************/
function setOpenList(NodeNum) {
var n = open_list.
open_list[n] = [arr_map[NodeNum][0], arr_map[NodeNum][1], arr_map[NodeNum][2], arr_map[NodeNum][3], arr_map[NodeNum][4]];
/***********************************************************************
*function setCloseList(NodeNum)
*功能:把节点加入close表中
*参数:待加入closelist的节点的编号
************************************************************************/
function setCloseList(NodeNum) {
var n = close_list.
close_list[n] = [arr_map[NodeNum][0], arr_map[NodeNum][1], arr_map[NodeNum][2], arr_map[NodeNum][3], arr_map[NodeNum][4]];
/***********************************************************************
*function findInOpenList(nowNodeNum)
*功能:查询当前节点是否在openlist中,找到返回1,找不到返回0
*参数:待查询的节点的编号
************************************************************************/
function findInOpenList(nowNodeNum) {
for (i = 0; i & open_list. i++) {
if (open_list[i][4] == nowNodeNum)
/***********************************************************************
*function findInCloseList(nowNodeNum)
*功能:查询当前节点是否在closelist中,找到返回1,找不到返回0
*参数:待查询的节点的编号
************************************************************************/
function findInCloseList(nowNodeNum) {
for (i = 0; i & close_list. i++) {
if (close_list[i][4] == nowNodeNum)
else return 0;
/***********************************************************************
*function cost(SuccessorNodeNum,bestNodeNum)
*功能:现节点到达周围节点的代价
*参数:SuccessorNodeNum周围节点编号,bestNodeNum现节点
************************************************************************/
function cost(SuccessorNodeNum, bestNodeNum) {
var x0 = tile_x(bestNodeNum);
var y0 = tile_y(bestNodeNum);
var x1 = tile_x(SuccessorNodeNum);
var y1 = tile_y(SuccessorNodeNum);
if (((x0 - x1) == 0) || ((y0 - y1) == 0)) {
return 10;
return 14;
/**********************************************************************
*function setSuccessorNode(bestNodeNum,map_w)
*功能:把现节点的周围8个节点放入预先准备好的临时arr中以备检察
*参数:现节点的编号
周围八个点的排序
***********************************************************************/
function setSuccessorNode(bestNodeNum, n) {
var x0 = tile_x(bestNodeNum);
var y0 = tile_y(bestNodeNum);
var m = n - 1;
if ((x0 - 1) &= 0 && (y0) &= 0 && (x0 - 1) &= m && (y0) &= m) tmp[1] = [arr_map[tile_num(x0 - 1, y0)][0], arr_map[tile_num(x0 - 1, y0)][1], arr_map[tile_num(x0 - 1, y0)][2], arr_map[tile_num(x0 - 1, y0)][3], arr_map[tile_num(x0 - 1, y0)][4]]; else {
tmp[1] = [0, 0, 0, 0, 0];
if ((x0) &= 0 && (y0 + 1) &= 0 && (x0) &= m && (y0 + 1) &= m) tmp[3] = [arr_map[tile_num(x0, y0 + 1)][0], arr_map[tile_num(x0, y0 + 1)][1], arr_map[tile_num(x0, y0 + 1)][2], arr_map[tile_num(x0, y0 + 1)][3], arr_map[tile_num(x0, y0 + 1)][4]]; else {
tmp[3] = [0, 0, 0, 0, 0];
if ((x0) &= 0 && (y0 - 1) &= 0 && (x0) &= m && (y0 - 1) &= m) tmp[4] = [arr_map[tile_num(x0, y0 - 1)][0], arr_map[tile_num(x0, y0 - 1)][1], arr_map[tile_num(x0, y0 - 1)][2], arr_map[tile_num(x0, y0 - 1)][3], arr_map[tile_num(x0, y0 - 1)][4]]; else {
tmp[4] = [0, 0, 0, 0, 0];
if ((x0 + 1) &= 0 && (y0) &= 0 && (x0 + 1) &= m && (y0) &= m) tmp[6] = [arr_map[tile_num(x0 + 1, y0)][0], arr_map[tile_num(x0 + 1, y0)][1], arr_map[tile_num(x0 + 1, y0)][2], arr_map[tile_num(x0 + 1, y0)][3], arr_map[tile_num(x0 + 1, y0)][4]]; else {
tmp[6] = [0, 0, 0, 0, 0];
if (bomberConfig.algorithm.DIRECTION == 8) {
if ((x0 - 1) &= 0 && (y0 + 1) &= 0 && (x0 - 1) &= m && (y0 + 1) &= m) tmp[0] = [arr_map[tile_num(x0 - 1, y0 + 1)][0], arr_map[tile_num(x0 - 1, y0 + 1)][1], arr_map[tile_num(x0 - 1, y0 + 1)][2], arr_map[tile_num(x0 - 1, y0 + 1)][3], arr_map[tile_num(x0 - 1, y0 + 1)][4]]; else {
tmp[0] = [0, 0, 0, 0, 0];
if ((x0 - 1) &= 0 && (y0 - 1) &= 0 && (x0 - 1) &= m && (y0 - 1) &= m) tmp[2] = [arr_map[tile_num(x0 - 1, y0 - 1)][0], arr_map[tile_num(x0 - 1, y0 - 1)][1], arr_map[tile_num(x0 - 1, y0 - 1)][2], arr_map[tile_num(x0 - 1, y0 - 1)][3], arr_map[tile_num(x0 - 1, y0 - 1)][4]]; else {
tmp[2] = [0, 0, 0, 0, 0];
if ((x0 + 1) &= 0 && (y0 + 1) &= 0 && (x0 + 1) &= m && (y0 + 1) &= m) tmp[5] = [arr_map[tile_num(x0 + 1, y0 + 1)][0], arr_map[tile_num(x0 + 1, y0 + 1)][1], arr_map[tile_num(x0 + 1, y0 + 1)][2], arr_map[tile_num(x0 + 1, y0 + 1)][3], arr_map[tile_num(x0 + 1, y0 + 1)][4]]; else {
tmp[5] = [0, 0, 0, 0, 0];
if ((x0 + 1) &= 0 && (y0 - 1) &= 0 && (x0 + 1) &= m && (y0 - 1) &= m) tmp[7] = [arr_map[tile_num(x0 + 1, y0 - 1)][0], arr_map[tile_num(x0 + 1, y0 - 1)][1], arr_map[tile_num(x0 + 1, y0 - 1)][2], arr_map[tile_num(x0 + 1, y0 - 1)][3], arr_map[tile_num(x0 + 1, y0 - 1)][4]]; else {
tmp[7] = [0, 0, 0, 0, 0];
if (bomberConfig.algorithm.DIRECTION == 4) {
tmp[0] = [0, 0, 0, 0, 0];
tmp[2] = [0, 0, 0, 0, 0];
tmp[5] = [0, 0, 0, 0, 0];
tmp[7] = [0, 0, 0, 0, 0];
/*******************************************************************
*function showPath(close_list)
*功能:把结果路径存入arr_path输出
*参数:close_list
********************************************************************/
function showPath(close_list, arr_path) {
var n = close_list.
var i = n - 1;
var ii = 0;
var nn = 0;
var mm = 0;
var arr_path_tmp = new Array();
var target = null;
/**********把close_list中有用的点存入arr_path_tmp中*************/
for ( ; ii++) {
arr_path_tmp[ii] = close_list[n - 1][4];
if (close_list[n - 1][1] == close_list[i][4]) {
for (i = n - 1; i &= 0; i--) {
if (close_list[i][4] == close_list[n - 1][1]) {
n = i + 1;
var w = arr_path_tmp.length - 1;
var j = 0;
for (var i = i &= 0; i--) {
arr_path[j] = arr_path_tmp[i];
for (var k = 0; k &= k++) {
target = {
x: tile_x(arr_path[k]),
y: map_w - 1 - tile_y(arr_path[k])
arr_path_out.push(target);
arr_path_out.shift();
function _reset() {
arr_path_out = new Array();
arr_map = new Array();
arr_map_tmp = window.mapD
map_w = arr_map_tmp.
open_list = new Array(); //创建OpenList
close_list = new Array(); //创建CloseList
tmp = new Array(); //存放当前节点的八个方向的节点
var findPath = {
aCompute: function (terrainData, begin, end) {
return aCompute(terrainData, begin, end);
window.findPath = findP
重构状态模式Context类
(function () {
var Context = YYC.Class({
Init: function (sprite) {
this.sprite =
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
删除Context的静态实例,改为在构造函数中创建具体状态类实例
因为EnemySprite和PlayerSprite都要使用Context的实例。如果为静态实例的话,EnemySprite中的Context类实例与PlayerSprite中的Context类实例会共享静态实例(具体状态类实例)!会造成互相干扰!
(function () {
var Context = YYC.Class({
Init: function (sprite) {
this.sprite =
this.walkLeftState = new WalkLeftState();
this.walkRightState = new WalkRightState();
this.walkUpState = new WalkUpState();
this.walkDownState = new WalkDownState();
this.standLeftState = new StandLeftState();
this.standRightState = new StandRightState();
this.standUpState = new StandUpState();
this.standDownState = new StandDownState();
window.Context = C
提出基类Sprite
为什么要提出
EnemySprite与PlayerSpite有很多相同的代码
从概念上来说,玩家精灵类与敌人精灵类都属于精灵类的概念
因此,提出EnemySprite、PlayerSprite基类Sprite。
修改碰撞检测
Sprite 增加getCollideRect获得碰撞面积。EnemySprite增加collideWidthOther。
相关的代码
(function () {
var Sprite = YYC.AClass({
Init: function (data) {
this.x = data.x;
this.y = data.y;
this.speedX = data.speedX;
this.speedY = data.speedY;
= data.minX;
= data.maxX;
= data.minY;
= data.maxY;
this.defaultAnimId = data.defaultAnimId;
this.anims = data.
Private: {
//更新帧动画
_updateFrame: function (deltaTime) {
if (this.currentAnim) {
this.currentAnim.update(deltaTime);
//在一个移动步长中已经移动的次数
moveIndex: 0,
//精灵的坐标
//精灵的速度
speedX: 0,
speedY: 0,
//精灵的坐标区间
maxX: 9999,
maxY: 9999,
//精灵包含的所有 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 () {
if (this.currentAnim) {
var f = this.currentAnim.getCurrentFrame();
x1: this.x,
y1: this.y,
x2: this.x + f.imgWidth,
y2: this.y + f.imgHeight
Virtual: {
//初始化方法
init: function () {
this.setAnim(this.defaultAnimId);
// 更新精灵当前状态.
update: function (deltaTime) {
this._updateFrame(deltaTime);
Abstract: {
draw: function (context) { },
clear: function (context) { },
move: function () { },
setDir: function () { }
window.Sprite = S
EnemySprite
(function () {
var EnemySprite = YYC.Class(Sprite, {
Init: function (data) {
this.base(data);
this.walkSpeed = data.walkS
this.speedX = data.walkS
this.speedY = data.walkS
this.__context = new Context(this);
Private: {
//状态模式上下文类
__context: null,
__getCurrentState: function () {
var currentState = null;
switch (this.defaultAnimId) {
case "stand_right":
currentState = new StandRightState();
case "stand_left":
currentState = new StandLeftS
case "stand_down":
currentState = new StandDownS
case "stand_up":
currentState = new StandUpS
case "walk_down":
currentState = new WalkDownS
case "walk_up":
currentState = new WalkUpS
case "walk_right":
currentState = new WalkRightS
case "walk_left":
currentState = new WalkLeftS
throw new Error("未知的状态");
return currentS
//计算移动次数
__computeStep: function () {
this.stepX = Math.ceil(bomberConfig.WIDTH / this.speedX);
this.stepY = Math.ceil(bomberConfig.HEIGHT / this.speedY);
//精灵的方向系数:
//往下走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,
//寻找的路径
playerSprite: null,
init: function () {
this.__context.setPlayerState(this.__getCurrentState());
this.__computeStep();
this.base();
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);
//判断是否和另外一个精灵碰撞
collideWidthOther: function (sprite2) {
var rect1 = this.getCollideRect();
var rect2 = sprite2.getCollideRect();
//如果碰撞,则抛出异常
if (rect1 && rect2 && !(rect1.x1 &= rect2.x2 || rect1.y1 &= rect2.y2 || rect1.x2 &= rect2.x1 || rect1.y2 &= rect2.y1)) {
throw new Error();
setPlayerSprite: function (sprite) {
this.playerSprite =
__computePath: function () {
//playerSprite的坐标要向下取整
var x = (this.playerSprite.x - this.playerSprite.x % window.bomberConfig.WIDTH) / window.bomberConfig.WIDTH;
var y = (this.playerSprite.y - this.playerSprite.y % window.bomberConfig.HEIGHT) / window.bomberConfig.HEIGHT;
window.findPath.aCompute(window.terrainData,
{ x: this.x / window.bomberConfig.WIDTH, y: this.y / window.bomberConfig.HEIGHT },
{ x: x, y: y }).path
move: function () {
this.__context.move();
setDir: function () {
var target,
if (this.moving) {
//特殊情况,如寻找不到路径
if (this.path === false) {
if (this.path.length == 0) {
this.path = this.__computePath();
//返回并移除要移动到的坐标
target = this.path.shift();
//当前坐标
x: self.x / bomberConfig.WIDTH,
y: self.y / bomberConfig.HEIGHT
//判断要移动的方向,调用相应的方法
if (target.x & now.x) {
this.__context.walkRight();
else if (target.x & now.x) {
this.__context.walkLeft();
else if (target.y & now.y) {
this.__context.walkDown();
else if (target.y & now.y) {
this.__context.walkUp();
this.__context.stand();
window.EnemySprite = EnemyS
PlayerSprite
(function () {
var PlayerSprite = YYC.Class(Sprite, {
Init: function (data) {
this.base(data);
this.walkSpeed = data.walkS
this.speedX = data.walkS
this.speedY = data.walkS
this.__context = new Context(this);
Private: {
//状态模式上下文类
__context: null,
__getCurrentState: function () {
var currentState = null;
switch (this.defaultAnimId) {
case "stand_right":
currentState = new StandRightState();
case "stand_left":
currentState = new StandLeftS
case "stand_down":
currentState = new StandDownS
case "stand_up":
currentState = new StandUpS
case "walk_down":
currentState = new WalkDownS
case "walk_up":
currentState = new WalkUpS
case "walk_right":
currentState = new WalkRightS
case "walk_left":
currentState = new 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;
__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();
//精灵的方向系数:
//往下走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,
init: function () {
this.__context.setPlayerState(this.__getCurrentState());
this.__computeStep();
this.base();
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
增加CharacterLayer类
PlayerLayer、EnemyLayer有相似的模式
从语义上来看,PlayerLayer、EnemyLayer都是属于&人物&的语义
因此,提出CharacterLayer父类
(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();
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 () { },
//游戏主线程调用的函数
render: function () { }
window.Layer = L
CharacterLayer
(function () {
var CharacterLayer = YYC.AClass(Layer, {
Init: function (deltaTime) {
this.___deltaTime = deltaT
Private: {
___deltaTime: 0,
___update: function (deltaTime) {
this.P__iterator("update", deltaTime);
___setDir: function () {
this.P__iterator("setDir");
___move: function () {
this.P__iterator("move");
draw: function () {
this.P__iterator("draw", this.P__context);
clear: function () {
this.P__iterator("clear", this.P__context);
render: function () {
this.___setDir();
this.___move();
if (this.P__isChange()) {
this.clear();
this.___update(this.___deltaTime);
this.draw();
this.P__setStateNormal();
window.CharacterLayer = CharacterL
PlayerLayer
(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;
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": 1
change: function () {
if (this.___keyDown() || this.___spriteMoving() || this.___spriteStand()) {
this.base();
window.PlayerLayer = PlayerL
EnemyLayer
(function () {
var EnemyLayer = YYC.Class(CharacterLayer, {
Init: function (deltaTime) {
this.base(deltaTime);
Private: {
__getPath: function () {
this.P__iterator("getPath");
playerLayer: null,
init: function () {
this.base();
setCanvas: function () {
this.P__canvas = document.getElementById("enemyLayerCanvas");
$("#enemyLayerCanvas").css({
"position": "absolute",
"top": bomberConfig.canvas.TOP,
"left": bomberConfig.canvas.LEFT,
"border": "1px solid black",
"z-index": 1
getPlayer: function (playerLayer) {
this.playerLayer = playerL
this.P__iterator("setPlayerSprite", playerLayer.getChildAt(0));
collideWidthPlayer: function () {
this.P__iterator("collideWidthPlayer", this.playerLayer.getChildAt(0));
return false;
return true;
render: function () {
this.__getPath();
this.base();
window.EnemyLayer = EnemyL
提出父类MoveSprite
PlayerSprite、EnemySprite有相似的模式
从语义上来看,PlayerSprite、EnemySprite都是能够移动的精灵类
因此,提出父类MoveSprite
为什么不把PlayerSprite、EnemySprite的相似的模式直接提到Sprite中?
抽象层次不同
因为我提取的语义是&移动的精灵类&,而Sprite的语义是&精灵类&,属于更抽象的概念
为什么不叫CharacterSprite?
因为关注的语义不同。
在提取CharacterLayer类时,关注的是PlayerLayer、EnemyLayer中&人物&语义;而在提取MoveSprite类时,关注的是PlayerSprite、EnemySprite中&移动&语义。因此,凡是属于&人物&这个语义的Layer类,都可以考虑继承于CharacterLayer;而凡是有&移动&这个特点的Sprite类,都可以考虑继承于MoveSprite。
(function () {
var Sprite = YYC.AClass({
Init: function (data, bitmap) {
this.bitmap =
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,
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);
Abstract: {
draw: function (context) { },
clear: function (context) { }
window.Sprite = S
MoveSprite
(function () {
var MoveSprite = YYC.AClass(Sprite, {
Init: function (data, bitmap) {
this.base(data, bitmap);
this.minX = data.minX;
this.maxX = data.maxX;
this.minY = data.minY;
this.maxY = data.maxY;
this.walkSpeed = data.walkS
this.speedX = data.walkS
this.speedY = data.walkS
Protected: {
//状态模式上下文类
P__context: null
Private: {
__getCurrentState: function () {
var currentState = null;
switch (this.defaultAnimId) {
case "stand_right":
currentState = new StandRightState();
case "stand_left":
currentState = new StandLeftS
case "stand_down":
currentState = new StandDownS
case "stand_up":
currentState = new StandUpS
case "walk_down":
currentState = new WalkDownS
case "walk_up":
currentState = new WalkUpS
case "walk_right":
currentState = new WalkRightS
case "walk_left":
currentState = new WalkLeftS
throw new Error("未知的状态");
return currentS
//计算移动次数
__computeStep: function () {
this.stepX = Math.ceil(bomberConfig.WIDTH / this.speedX);
this.stepY = Math.ceil(bomberConfig.HEIGHT / this.speedY);
__isMoving: function(){
return this.x % bomberConfig.WIDTH !== 0 || this.y % bomberConfig.HEIGHT !== 0
//精灵的速度
speedX: 0,
speedY: 0,
//精灵的坐标区间
maxX: 9999,
maxY: 9999,
//精灵的方向系数:
//往下走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,
init: function () {
this.P__context.setPlayerState(this.__getCurrentState());
this.__computeStep();
this.base();
draw: function (context) {
var frame = null;
if (this.currentAnim) {
frame = this.currentAnim.getCurrentFrame();
context.drawImage(this.bitmap.img, frame.x, frame.y, frame.width, frame.height, this.x, this.y, this.bitmap.width, this.bitmap.height);
clear: function (context) {
var frame = null;
if (this.currentAnim) {
frame = this.currentAnim.getCurrentFrame();
//直接清空画布区域
context.clearRect(0, 0, bomberConfig.canvas.WIDTH, bomberConfig.canvas.HEIGHT);
//获得当前坐标对应的方格坐标
getCurrentCellPosition: function () {
if (this.__isMoving()) {
throw new Error("精灵正在移动且未完成一个移动步长");
x: this.x / bomberConfig.WIDTH,
y: this.y / bomberConfig.HEIGHT
Abstract: {
move: function () { },
setDir: function () { }
window.MoveSprite = MoveS
PlayerSprite
(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();
move: function () {
this.P__context.move();
setDir: function () {
if (this.moving) {
if (this.__allKeyUp()) {
this.P__context.stand();
this.__judgeAndSetDir();
window.PlayerSprite = PlayerS
EnemySprite
(function () {
var EnemySprite = YYC.Class(MoveSprite, {
Init: function (data, bitmap) {
this.base(data, bitmap);
this.P__context = new Context(this);
Private: {
___findPath: function () {
return window.findPath.aCompute(window.terrainData, this.___computeCurrentCoordinate(),
this.___computePlayerCoordinate()).path
___computeCurrentCoordinate: function () {
if (this.x % window.bomberConfig.WIDTH || this.y % window.bomberConfig.HEIGHT) {
throw new Error("当前坐标应该为方格尺寸的整数倍!");
x: this.x / window.bomberConfig.WIDTH,
y: this.y / window.bomberConfig.HEIGHT
___computePlayerCoordinate: function () {
x: Math.floor(this.playerSprite.x / window.bomberConfig.WIDTH),
y: Math.floor(this.playerSprite.y / window.bomberConfig.HEIGHT)
___getAndRemoveTarget: function () {
return this.path.shift();
___judgeAndSetDir: function (target) {
//当前坐标
var current = this.___computeCurrentCoordinate();
//判断要移动的方向,调用相应的方法
if (target.x & current.x) {
this.P__context.walkRight();
else if (target.x & current.x) {
this.P__context.walkLeft();
else if (target.y & current.y) {
this.P__context.walkDown();
else if (target.y & current.y) {
this.P__context.walkUp();
this.P__context.stand();
//寻找的路径
playerSprite: null,
collideWidthPlayer : function(sprite2){
var rect1=this.getCollideRect();
var rect2=sprite2.getCollideRect();
//如果碰撞,则抛出异常
if (rect1 && rect2 && !(rect1.x1 &= rect2.x2 || rect1.y1 &= rect2.y2 || rect1.x2 &= rect2.x1 || rect1.y2 &= rect2.y1)) {
throw new Error();
setPlayerSprite: function (sprite) {
this.playerSprite =
move: function () {
this.P__context.move();
setDir: function () {
//如果正在移动或者找不到路径,则返回
if (this.moving || this.path === false) {
this.___judgeAndSetDir(this.___getAndRemoveTarget());
getPath: function () {
if (this.moving) {
if (this.path.length == 0) {
this.path = this.___findPath();
window.EnemySprite = EnemyS
将Bitmap注入到Sprite中
反思Sprite类,发现在getCollideRect方法中,使用了图片的宽度和高度:
getCollideRect: function () {
if (this.currentAnim) {
var f = this.currentAnim.getCurrentFrame();
x1: this.x,
y1: this.y,
x2: this.x + f.imgWidth,
y2: this.y + f.imgHeight
此处图片的宽度和高度是从FrameData中读取的:
var getFrames = (function () {
imgWidth = bomberConfig.player.IMGWIDTH,
imgHeight = bomberConfig.player.IMGHEIGHT;
图片的宽度和高度属于图片信息,应该都放到Bitmap类中!
在创建精灵实例时,将图片的宽度和高度包装到Bitmap中,并注入到精灵类中:
SpriteFactory
(function () {
var spriteFactory = {
createPlayer: function () {
return new PlayerSprite(getSpriteData("player"), bitmapFactory.createBitmap({ img: window.imgLoader.get("player"), width: bomberConfig.player.IMGWIDTH, height: bomberConfig.player.IMGHEIGHT }));
createEnemy: function () {
return new EnemySprite(getSpriteData("enemy"), bitmapFactory.createBitmap({ img: window.imgLoader.get("enemy"), width: bomberConfig.player.IMGWIDTH, height: bomberConfig.player.IMGHEIGHT }));
window.spriteFactory = spriteF
然后在getCollideRect方法中改为读取Bitmap实例引用的宽度和高度:
Init: function (data, bitmap) {
this.bitmap =
bitmap: null,
getCollideRect: function () {
x1: this.x,
y1: this.y,
x2: this.x + this.bitmap.width,
y2: this.y + this.bitmap.height
删除data -& frames.js中的imgWidth、imgHeight
现在FrameData中的imgWidth、imgHeight是多余的了,应该将其删除。
增加MapElementSprite
增加地图元素精灵类,它拥有图片Bitmap的实例。其中,地图的一个单元格就是一个地图元素精灵类。
为什么增加?
1、可以在创建MapLayer元素时,元素由bitmap改为精灵类,这样x、y属性就可以从bitmap移到精灵类中了.2、精灵类包含动画,方便后期增加动态地图。
它的父类为Sprite还是MoveSprite?
因为MapElementSprite不属于&移动&语义,且它与MoveSprite没有相同的模式,所以它应该继承于Sprite。
MapElementSprite
(function () {
var MapElementSprite = YYC.Class(Sprite, {
Init: function (data, bitmap) {
this.base(data, bitmap);
Protected: {
Private: {
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.MapElementSprite = MapElementS
(function () {
var MapLayer = YYC.Class(Layer, {
Init: function () {
Private: {
___canvasBuffer: null,
___contextBuffer: null,
___getCanvasBuffer: function () {
//缓冲的canvas也要在html中创建并设置width、height!
this.___canvasBuffer = document.getElementById("mapLayerCanvas_buffer");
___getContextBuffer: function () {
this.___contextBuffer = this.___canvasBuffer.getContext("2d");
___drawBuffer: function () {
this.P__iterator("draw", this.___contextBuffer);
setCanvas: function () {
this.P__canvas = document.getElementById("mapLayerCanvas");
var css = {
"position": "absolute",
"top": bomberConfig.canvas.TOP,
"left": bomberConfig.canvas.LEFT,
"border": "1px solid blue",
"z-index": 0
$("#mapLayerCanvas").css(css);
//缓冲canvas的css也要设置!
$("#mapLayerCanvas_buffer").css(css);
init: function(){
//获得缓冲canvas
this.___getCanvasBuffer();
//获得缓冲context
this.___getContextBuffer();
this.base();
draw: function () {
this.___drawBuffer();
this.P__context.drawImage(this.___canvasBuffer, 0, 0);
clear: function () {
this.___contextBuffer.clearRect(0, 0, bomberConfig.canvas.WIDTH, bomberConfig.canvas.HEIGHT);
this.P__context.clearRect(0, 0, bomberConfig.canvas.WIDTH, bomberConfig.canvas.HEIGHT);
render: function () {
if (this.P__isChange()) {
this.clear();
this.draw();
this.P__setStateNormal();
window.MapLayer = MapL
重构Bitmap
删除Bitmap的x、y属性。
现在Bitmap的x、y属性用于保存地图图片的坐标。现在坐标保存在地图精灵类中了,故Bitmap中多余的x、y属性。
(function () {
var Bitmap = YYC.Class({
Init: function (data) {
this.img = data.
this.width = data.
this.height = data.
Private: {
img: null,
window.Bitmap = B
重构LayerManager
LayerManager本来的职责为&负责层的逻辑&,但是我认为LayerManager的职责应该为&负责层的统一操作&,它应该为一个键-值集合类,它的元素应该为Layer的实例。
因此对LayerManager进行重构:
将change的判断移到具体的Layer中
由Layer类应该负责自己状态的维护。
将createElement放到Game中
创建层内元素createElement这个职责应该放到调用LayerManager的客户端,即Game类中。在Game中还要负责创建LayerManager、创建Layer。
增加一个Hash类,它实现键-值集合的通用操作,然后让LayerManager继承于Hash,使之成为集合类。
为什么使用Hash类,而不是使用Collection类(数组集合类)
功能上分析:
因为LayerManager集合的元素为Layer实例,而每一个层的实例都是唯一的,即如PlayerLayer实例只有一个,不会有二个PlayerLayer实例。因此,使用Hash结构,可以通过key获得LayerManager集合中的每个元素。
Hash的优势:
Hash结构不需要知道LayerManager集合装入Layer实例的顺序,通过key值就可以唯一获得元素;而Collection结构(数组结构)需要知道装入顺序。
LayerManager、PlayerLayerManager、EnemyLayerManager、MapLayerManager重构前:
(function () {
var LayerManager = YYC.AClass({
Init: function (layer) {
this.layer =
Private: {
layer: null,
addElement: function (element) {
var i = 0,
for (i = 0, len = element. i & i++) {
this.layer.appendChild(element[i]);
render: function () {
this.layer.render();
Virtual: {
initLayer: function () {
this.layer.setCanvas();
this.layer.init();
this.layer.change();
Abstract: {
createElement: function () { },
change: function () { }
var MapLayerManager = YYC.Class(LayerManager, {
Init: function (layer) {
this.base(layer);
Private: {
__getMapImg: function (i, j, mapData) {
var img = null;
switch (mapData[i][j]) {
img = window.imgLoader.get("ground");
img = window.imgLoader.get("wall");
//创建并设置每个地图单元bitmap,加入到元素数组中并返回。
createElement: function () {
var i = 0,
row = bomberConfig.map.ROW,
col = bomberConfig.map.COL,
element = [],
mapData = mapDataOperate.getMapData(),
img = null;
for (i = 0; i & i++) {
//y为纵向height,x为横向width
y = i * bomberConfig.HEIGHT;
for (j = 0; j & j++) {
x = j * bomberConfig.WIDTH;
img = this.__getMapImg(i, j, mapData);
element.push(spriteFactory.createMapElement({ x: x, y: y }, bitmapFactory.createBitmap({ img: img, width: bomberConfig.WIDTH, height: bomberConfig.HEIGHT })));
change: function () {
var PlayerLayerManager = YYC.Class(LayerManager, {
Init: function (layer) {
this.base(layer);
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.layer.getChildAt(0).moving
__spriteStand: function () {
if (this.layer.getChildAt(0).stand) {
this.layer.getChildAt(0).stand = false;
return true;
return false;
createElement: function () {
var element = [],
player = spriteFactory.createPlayer();
player.init();
element.push(player);
change: function () {
if (this.__keyDown() || this.__spriteMoving() || this.__spriteStand()) {
this.layer.change();
var EnemyLayerManager = YYC.Class(LayerManager, {
Init: function (layer) {
this.base(layer);
Private: {
initLayer: function (playerLayerManager) {
this.layer.setCanvas();
this.layer.init();
this.layer.getPlayer(playerLayerManager.layer);
this.layer.change();
createElement: function () {
var element = [],
enemy = spriteFactory.createEnemy();
enemy.init();
element.push(enemy);
collideWidthPlayer: function () {
return this.layer.collideWidthPlayer();
change: function () {
this.layer.change();
window.LayerManager = LayerM
//用于测试
window.MapLayerManager = MapLayerM
window.PlayerLayerManager = PlayerLayerM
window.EnemyLayerManager = EnemyLayerM
LayerManager重构后:
(function () {
var LayerManager = YYC.Class(Hash, {
Private: {
__iterator: function (handler) {
var args = Array.prototype.slice.call(arguments, 1),
layers = this.getChilds();
for (i in layers) {
if (layers.hasOwnProperty(i)) {
layers[i][handler].apply(layers[i], args);
addLayer: function (name, layer) {
this.add(name, layer);
return this;
getLayer: function (name) {
return this.getValue(name);
initLayer: function () {
this.__iterator("setCanvas");
this.__iterator("init");
render: function () {
this.__iterator("render");
change: function () {
this.__iterator("change");
window.LayerManager = LayerM
(function () {
var Hash = YYC.AClass({
Private: {
_childs: {}
getChilds: function () {
return YYC.Tool.extend.extend({}, this._childs);
getValue: function (key) {
return this._childs[key];
add: function (key, value) {
this._childs[key] =
return this;
window.Hash = H
(function () {
var Game = YYC.Class({
Init: function () {
Private: {
_pattern: null,
_ground: null,
_layerManager: null,
_createLayerManager: function () {
this._layerManager = new LayerManager();
this._layerManager.addLayer("mapLayer", layerFactory.createMap());
this._layerManager.addLayer("playerLayer", layerFactory.createPlayer(this.sleep));
this._layerManager.addLayer("enemyLayer", layerFactory.createEnemy(this.sleep));
_addElements: function () {
var mapLayerElements = this._createMapLayerElement(),
playerLayerElements = this._createPlayerLayerElement(),
enemyLayerElements = this._createEnemyLayerElement();
this._layerManager.addElements("mapLayer", mapLayerElements);
this._layerManager.addElements("playerLayer", playerLayerElements);
this._layerManager.addElements("enemyLayer", enemyLayerElements);
//创建并设置每个地图方格精灵,加入到元素数组中并返回。
_createMapLayerElement: function () {
var i = 0,
row = bomberConfig.map.ROW,
col = bomberConfig.map.COL,
element = [],
mapData = mapDataOperate.getMapData(),
img = null;
for (i = 0; i & i++) {
//y为纵向height,x为横向width
y = i * bomberConfig.HEIGHT;
for (j = 0; j & j++) {
x = j * bomberConfig.WIDTH;
img = this._getMapImg(i, j, mapData);
element.push(spriteFactory.createMapElement({ x: x, y: y }, bitmapFactory.createBitmap({ img: img, width: bomberConfig.WIDTH, height: bomberConfig.HEIGHT })));
_getMapImg: function (i, j, mapData) {
var img = null;
switch (mapData[i][j]) {
img = window.imgLoader.get("ground");
img = window.imgLoader.get("wall");
_createPlayerLayerElement: function () {
var element = [],
player = spriteFactory.createPlayer();
player.init();
element.push(player);
_createEnemyLayerElement: function () {
var element = [],
enemy = spriteFactory.createEnemy();
enemy.init();
element.push(enemy);
_initLayer: function () {
this._layerManager.initLayer();
this._layerManager.getLayer("enemyLayer").getPlayer(this._layerManager.getLayer("playerLayer"));
_initEvent: function () {
//监听整个document的keydown,keyup事件
keyEventManager.addKeyDown();
keyEventManager.addKeyUp();
context: null,
mainLoop: null,
init: function () {
this.sleep = Math.floor(1000 / bomberConfig.FPS);
this._createLayerManager();
this._addElements();
this._initLayer();
this._initEvent();
start: function () {
var self = this;
this.mainLoop = window.setInterval(function () {
self.run();
}, this.sleep);
run: function () {
if (this._layerManager.getLayer("enemyLayer").collideWidthPlayer()) {
clearInterval(this.mainLoop);
alert("Game Over!");
this._layerManager.render();
this._layerManager.change();
window.Game = G
本文最终领域模型
算法包FindPath
精灵抽象包Sprite
哈希集合包Hash
层管理实现包经过本文重构后,去掉了PlayerLayerManager等子类,只保留了LayerManager类。因此去掉层管理实现包。
将集合包重命名为数组集合包
&层&这个层级的集合包的命名太广泛了,应该具体化为数组集合包,这样才不至于与哈希集合包混淆。
将哈希集合包和数组集合包合并为集合包
哈希集合包与数组集合包都属于集合,因此应该合并为集合包,然后将集合包放到辅助逻辑层。
提出

我要回帖

更多关于 类似死或生的买肉游戏 的文章

 

随机推荐