关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

HTML5 游戏开发实战 | 推箱子

发布时间:2023-06-26 09:57:38
经典的推箱子是一个来自日本的古老游戏,目的是在训练玩家的逻辑思考能力。在一个狭小的仓库中,要求把木箱放到指定的位置,稍不小心就会出现箱子无法移动或者通道被堵住的情况,所以需要巧妙地利用有限的空间和通道,合理安排移动的次序和位置,才能顺利地完成任务。 推箱子游戏功能如下: 游戏运行载入相应的地图,屏幕中出现一个推箱子的工人,其周围是围墙、人可以走的通道、几个可以移动的箱子和箱子放置的目的地。让玩家通过按上、下、左、右键控制工人推箱子,当箱子都推到了目的地后出现过关信息,并显示下一关。推错了玩家可以撤销移动或者重新玩这关,直到通过全部关卡。 推箱子游戏的运行界面如上图所示。 本游戏使用的图片元素的含义如图9-2所示。 01、箱子游戏设计的思路 先来确定一下开发难点。对工人的操作很简单,就是4个方向移动。注意在工人移动时箱子也移动,此效果对按键处理的要求也比较简单。当箱子到达目的地位置时,需会产生游戏过关事件,需要一个逻辑判断。那么仔细想一下,这些所有的事件都发生在一张地图中。这张地图包括了箱子的初始化位置、箱子最终放置的位置,以及围墙障碍等。每一关地图都要更换,这些位置也要变。所以每一关的地图数据是最关键的,它决定了每一关的不同场景和物体位置。那么下面就重点分析一下地图。 假设把地图想象成一个网格,每个格子就是工人每次移动的步长,也是箱子移动的距离,这样问题就简化多了。首先设计一个16×16的二维数组curMap。按照这样的框架来思考。对于格子的X,Y两个屏幕像素坐标,可以由二维列表下标换算。 每个格子状态值分别用值(0)代表通道Block,(1)代表墙Wall,(2)代表目的地Ball,(3)代表箱子Box,(4)代表工人CurMan,(5)代表放到目的地的箱子redBox。文件中存储的原始地图中格子的状态值采用相应的整数形式存放。 在玩家通过键盘控制工人推箱子的过程中,需要按游戏规则进行判断是否响应该按键指示。下面分析一下工人将会遇到什么情况,以便归纳出所有的规则和对应算法。为了描述方便,可以假设工人移动趋势方向为向右,其他方向原理是一致的。如图9-4所示,P1、P2分别代表工人移动趋势方向的前两个方格。 ■ 图9-4工人移动趋势(向右) 游戏规则判断如下。 (1) 判断P1是否出界,出界则退出规则判断,布局不做任何改变。 if(p1.x< 0) return false;if(pl.y< 0) return false;if(pl.y>= curMap.length) return false;if(p1.x>= curMap[0].length) return false;(2) 前方P1是围墙。 如果工人前方是围墙(即阻挡工人的路线) { 退出规则判断,布局不做任何改变; } if(curMap[p1.y][p1.x] == 1return false; //如果是墙,不能通行(3) 前方P1是箱子,如图9-5所示。■ 图9-5工人前方是箱子 (3) 前方P1是箱子,如图9-5所示。 在前面的情况中,只要根据前方P1处的物体就可以判断出工人是否可以移动,而在第3种情况中,需要判断箱子前方P2处的物体才能判断出工人是否可以移动。此时有以下几种可能。 ① P1处为箱子或者放到目的地的箱子,P2处为墙或箱子。 如果工人前方P1处为箱子或者放到目的地的箱子,P2处为墙或箱子,退出规则判断,布局不做任何改变。 if(curMap[pl.y][p1.x]== 3 curMap[p1.y][p1.x]== 5) //如果是箱子,继续判断前一格 if(curMap[p2.y][p2.x] == 1 curMap[ p2.y][p2.x]== 3 curMap[p2.y][p2.x]== 5) return false; //前一格如果是墙或箱子,则不能前进 ② P1处为箱子或者放到目的地的箱子,P2处为通道。 如果工人前方P1处为箱子,P2处为通道,工人可以进到P1方格,P2方格状态为箱子。修改相关位置格子的状态值。 ③ P1处为箱子或者放到目的地的箱子,P2处为目的地。 如果工人前方P1处为箱子,P2处为目的地,工人可以进到P1方格,P2方格状态为放置好的箱子。修改相关位置格子的状态值。 //如果是箱子,继续判断前一格 if(curMap[pl.y][p1.x]== 3 curMap[p1.y][p1.x]== 5)if(curMap[p2.y][p2.x]==0 curMap[p2.y][p2.x]== 2) //如果 P2 为通道或者目的地 //记录现在的地图 oldMap = copyArray(curMap);//箱子前进一格 curMap[p2.y][ p2.x]= 3;//如果原始地图是目的地或者是放到目的地的箱子if(CurLevel[p2.y][p2.x] == 2 CurLevel[p2.y][p2.x] == 5)curMap[p2.y][p2.x] = 5; canReDo = true;//工人前进一格 curMap[ p1.y][p1.x] = 4; //4 代表工人 //处理工人原来位置是显示目的地还是通道平地//获取工人原来位置原始地图信息var v= CurLevel[per position.y][per position.x];if(v== 2 v== 5)[ //如果原来位置是目的地或者放到目的地的箱子curMap[per_position.y][per position.x]=2; //显示目的地 else //显示通道平地 curMap[per position.y][per position.x]=0; 综合前面的分析,可以设计出整个游戏的实现流程。 02、推箱子游戏设计的步骤游戏页面pushbox.html < head > 推箱子游戏 < meta http - equiv = content - type content = "text/html; charset = utf - 8"> 浏览器还不支持哦
< img id ="wall"src ="img/wall.gif"style = "display:none;">< img id= "redbox”src ="img/redbox.gif"style ="display:none;">< img id="pleft"src = "img/left.png"style ="display:none;'< img id="pdown”src ="img/down.pngstyle ="display:none;">< input type ="button"value ="下一关"onclick ="NextLevel(1)"> < input type="button"value ="撤销移动”onclick ="Redo()"> < input type="button"value ="游戏说明”onclick ="DoHelp()">< script type = "text/javascript"src = "mapdata100.js"> 游戏页面主要设置图片素材对应的id 。例如,箱子图片的id是“box" ,目的地图片的id是“ball" ,通道图片的id是“block" ,已在目的地的箱子id是“redbox" ,墙图片的id 是“wall" 。人物的上下左右方向图片的id分别是“pleft" 、“pright" 、“pup" 、“pdown" 。 界面上添加 5 个功能按钮,实现“上一关”“下一关”“撤销移动”“重玩本关”“游戏说明”功能。 设计脚本( pushbox1.js )1. 设计游戏地图整个游戏在16×16区域中,使用二维数组curMap存储游戏的状态。其中,方格状态值0代表通道,1代表墙,2代表目的地,3代表箱子,4代表工人,5代表放到目的地的箱子。例如图9-1所示推箱子游戏界面的对应数据如下:每关地图方格状态值采用levels数组存储,如levels[0]存储第一关,levels[1]存储第二关,以此类推。本游戏存储100关信息,所以把数组levels单独放置在"mapdata100.js"脚本文件中。 第一关如下: var levels =[]; levels[0]=[ [00,00,000000000000 [00,000000000000001 [0,0,0,0,0,0,00000000001 0.000.0000000000001 [0.0.0.0.0.0.11.0.00.0.0.001 0000000,0,0,0,0.0.01 0000001,0,0,0,01 [0,0,0,01.1.0.0.0.07 [0,0,0,0,1,2,03,4,1,1,10000] [0.0,0,0,1,1,11,3,1,0000001 [0,0,0,0,0,0,0,1,2,1,0,0,00001 [0,0,0,0,0,0,01,1,1,0000001 [0,0,0,0,0,0,000,0,0,0,00001 [0,0,0,0,0,0,00,0,0,0,0,0,0001 [0,0,00,0,0,0,0,0,0,0,0,0,000] [0,0,0,0,0,0,0,00,0,0,0000 第二关如下: [0,0,0,0,1,4,0,0,1,0,0,0,0,0,0,01[0,0,0,0,1,0,3,3,1,0,1,1,1,0,0,0][0,0,0,0,1,0,3,0,1,0,1,2,1,0,0,0][0,0,0,0,1,1,1,0,1,1,1,2,1,0,0,01[0,0,0,0,0,1,1,0,0,0,0,2,1,0,0,0] [0,0,0,0,0,1,0,0,0,1,00,10,001 [0,0,0,0,0,10,0,0,1,1,110001 0,0,0,0,0,1,1,1,1,1,00,00001 0.0.0.0.0.0.0.0.0.00000001 00.0.0.0.00.0.00000001 0000000000000000 00,0000,0,00,0,00,00001l; 程序初始时,获取对应的图片,并将本关iCurLevel的地图信息levels[iCurLevel]复制到当前游戏地图数据数组curMap和CurLevel。curMap初始与CurLevel相同,游戏中记录不断改变游戏状态。CurLevel是当前关游戏地图数据,游戏中不变,主要用来获取箱子目的地和判断游戏是否结束。 var w= 32; var h = 32; var curMap; var oldMap; var CurLevel; var iCurLevel=0; var curMan; var UseTime = 0; var MoveTimes = 0; //当前游戏地图数据数组,初始与 CurLevel 相同,游戏中改变//保存上次人物移动前地图数据数组 //当前关游戏地图数据,游戏中不变,用来判断游戏是否结束 //当前是第几关//当前小人图片 //当前关用时,单位为秒 //移动次数 var mycanvas = document.getElementById('myCanvas'); var context = mycanvas.getContext(2d'); var block = document.getElementById("block"); var box = document.getElementById("box"); var wall= document.getElementById("wall") var ball = document.getElementById("ball"); var redbox = document.getElementById("redbox"); var pdown = document.getElementById("pdown"); var pup = document.getElementById("pup”); var pleft = document.getElementById("pleft"); var pright = document.getElementById("pright");var msg = document.getElementById("msg”); function init() initLevel(); showMoveInfo(); initLevel()函数将本关地图信息复制到当前游戏地图数据数组curMap和CurLevel,并在屏幕上画出通道、箱子、墙、人物、目的地信息。 function initLevel() curMap = copyArray(levels[iCurLevel]); oldMap = copyArray(curMap); CurLevel = copyArray(levels[ iCurLevel]); curMan = pdown; DrawMap(curMap) function copyArray(arr) //画出通道、箱子、墙、人物、目的地信息 //复制二维数组 var b =[]; for(i= 0;i= curMap.length) return false; if(pl.x>= curMap[0].length) return false; //如果是墙,不能通行if(curMap[pl.y][p1.x]== 1)return false;if(curMap[pl.y][p1.x]==3 curMap[pl.y][p1.x]==5) //如果是箱子,继续判断前一格 if(curMap[p2.y][p2.x]== 1 curMap[p2.y][p2.x] == 3curMap[p2.y][p2.x] == 5)//前一格如果是墙或箱子,则不能前进return false;if(curMap[p2.y][p2.x]== 0 curMap[p2.y][p2.x]== 2) //如果 P2 为通道或者目的地 oldMap = copyArray(curMap);//记录现在地图//箱子前进一格curMap[p2.y][p2.x] = 3;//如果原始地图是目的地或者是放到目的地的箱子if(CurLevel[p2.y][p2.x] == 2CurLevel[ p2.y][p2.x]== 5)curMap[ p2.y][p2.x]= 5; canReDo = true; //工人前进一格 curMap[p1.y][p1.x] = 4; //以下处理工人原来位置是显示目的地还是通道平地var v= CurLevel[per_position.y][per_position.x]; if(v== 2v== 5)curMap[per_position.y][per_position.x]=2else curMap[per_position.y][per_position.x]=0; per_position=pl; //获取工人原来位置原始地图信息//如果原来是目的地 //显示通道平地 //记录位置 return true; CheckFinish()函数用于判断是否完成本关。如果原始地图目标位置上没放箱子(也就是此位置不是放到目的地的箱子curMap[i][j]!=5),则表明有没放好的箱子,游戏还未过关,反之游戏过关。 function CheckFinish( for(var i= 0;i< curMap.length;i++) //验证是否过关 //行号 [j]!= 5) for(var j= 0;j< curMap[ i].length;j++)//如果原始地图的目标位置上没放箱子,则还没结束if(CurLevel[i][j]== 2 && curMap[i]lj]!= 5 CurLevel[i][j]== 5 && curMap[i //列号 return false; return true; 显示帮助信息var showHelp = false; function DoHelp() showHelp =!showHelp; if(showHelp) msg.innerHTML="用键盘的上、下、左右键移动小人,把箱子全部推到小球的位置即可关.箱子只可向前推,不能往后拉,并且小人一次只能推动一个箱子."; else showMoveInfo(); function showMoveInfo() msq.innerHTML="第"+(CurLevel + 1)+"关移动次数:”+ MoveTimes;showHelp = false; 5. 撤销功能游戏中oldMap用于保存每次移动前的地图信息,执行撤销就是把oldMap恢复到当前地图curMap中。同时根据地图中记录的信息找到工人位置,修改per_position记录的工人位置信息,最后重新绘制整个游戏屏幕就可以恢复到上一步的状态。var canReDo = false; function Redo()if (canReDo == false) //撤销功能 //不能撤销 return; //恢复上次地图 curMap = copyArray(oldMap); for (var i=0;i< curMap.length; i++) //行号 //列号for (var j= 0;j< curMap[i].length; j++) if (curMap[i][j]== 4)per_position = new Point(j,i); //如果此处是工人 this.MoveTimes -- canReDo = false; showMoveInfo(); DrawMap(curMap); //次数减 1 //显示移动次数信息 //画箱子、墙、人物、目的地信息 6. 选关功能游戏中有“上一关”“下一关”“重玩本关”这3个选关功能,这3个选关功能实现方法是一样的。参数i如果是1,则是“下一关”;参数i如果是-1,则是“上一关”;参数i如果是0,则是“重玩本关”。主要根据关卡号iCurLevel,调用initLevel()函数初始化本关地图,并在屏幕上画出箱子、墙、人物、目的地信息。function NextLevel(i) //初始化 i iCurLevel=iCurLevel + i;if(iCurLevel<0) iCurLevel= 0; return; var len = levels.length;if(iCurLevel> len - 1) iCurLevel = len - 1; return; initLevel();UseTime = 0;MoveTimes = 0:showMoveInfo(); 至此,完成经典的推箱子游戏。

/template/Home/leiyu/PC/Static