lufy's legend

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
楼主: lufy

[HTML5游戏开发]推箱子小游戏

  [复制链接]

37

主题

8

好友

9305

积分

诸侯王

Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15

发表于 2013-2-25 20:16:31 |显示全部楼层
开言:
lufylegend.js引擎已经更新到1.6以上了,虽然我陆陆续续发布了一些教程,也提供了一些简单的游戏示例,但是一直以来也没有制作几款完整的作品来,实在也是自己一个人时间太有限了,接下来的时间,我会尽可能的使用lufylegend.js引擎开发几款完整的作品,来增加一下这个引擎的说服力,希望喜欢html5,喜欢游戏开发的朋友多提些意见。
这一次先来看一个经典的推箱子游戏,相信大家也都知道这款游戏,推箱子游戏最早源于日本,是一款极其锻炼逻辑思考能力的游戏,箱子只能推不能拉,玩家必须在一个有限的空间里,将所有的箱子归位,如下图所示。

图1


这是我用最新版lufylegend.js引擎开发的,想挑战一下的朋友,可以点击下面的游戏链接试一下自己能通过几关。
游戏一共6关,我在游戏里加入了排名系统,每过一关可以上传自己的成绩,跟大家比拼一下,或者您也可以将自己的过关方法心得等回复到文章下面。

制作开始
好了,废话说完了,现在来看看如何来制作这款游戏。
一,首先,你需要下载lufylegend.js引擎
下面是我在博客的lufylegend-1.6.0发布帖

下面一步步来进入开发正题。
二,绘制背景和箱子
我们先来准备一张图,

图2
如果我们将上面的图平均分割成5份,那么他们的序号分别为0,1,2,3,4。
我们利用上面5个小图片就可以拼接任意的房间以及房间内箱子的摆放。
比如,我博客一开始的示例图1,它是游戏第一关的截图,要绘制这个房间,首先要知道这些图片应该摆放的位置,我们预先准备一个数组。
  1. var stage01 = [
  2. [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
  3. [-1,-1, 1, 1, 1, 1, 1, 1,-1,-1,-1],
  4. [-1,-1, 1, 0, 0, 0, 0, 1, 1,-1,-1],
  5. [-1,-1, 1, 0, 0, 4, 0, 0, 1,-1,-1],
  6. [-1,-1, 1, 4, 4, 0, 4, 4, 1,-1,-1],
  7. [-1,-1, 1, 0, 0, 4, 0, 0, 1,-1,-1],
  8. [-1,-1, 1, 1, 0, 0, 0, 0, 1,-1,-1],
  9. [-1,-1,-1, 1, 1, 1, 1, 1, 1,-1,-1],
  10. [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1]
  11. ];
复制代码
上面的-1表示不摆放,然后0,1,4等分别对应着图2中的序号。
按照这个数组来绘制房间就简单了,看下面的函数
  1. function drawFloor(x,y){
  2.         if(list[y][x] < 0)return;
  3.         var bitmap = new LBitmap(bitmapDataList[list[y][x]]);
  4.         bitmap.x = x*STEP;
  5.         bitmap.y = y*STEP;
  6.         boxLayer.addChild(bitmap);
  7. }
复制代码
这个list数组就是上面的stage01数组,参数x,y分别是数组的列和行的序号,STEP是每个小图片的长度,绘制一个小图块,其实我就是建立了一个LBitmap对象。
关于LSprite,LBitmap等对象是lufylegend.js引擎中很常用的对象,关于它们的用法,请大家参照官方的API文档,或者看我以前的一些文章来了解一下,这里不加赘述了。
当然,一开始,房间里要有箱子,箱子一开始的位置也是需要预先设置好的,我们同样建立一个数组。
  1. var box01 = [
  2. {x:3,y:3},
  3. {x:4,y:3},
  4. {x:5,y:3},
  5. {x:5,y:5},
  6. {x:6,y:5},
  7. {x:7,y:5}
  8. ];
复制代码
绘制箱子的函数如下
  1. function drawBox(){
  2.         var bitmap;
  3.         for(var i=0;i<boxList[stageIndex].length;i++){
  4.                 bitmap = new LBitmap(bitmapDataList[2]);
  5.                 bitmap.x = boxList[stageIndex][i].x*STEP;
  6.                 bitmap.y = boxList[stageIndex][i].y*STEP;
  7.                 boxLayer.addChild(bitmap);
  8.                 nowBoxList.push(bitmap);
  9.         }
  10. }
复制代码
上面,我同样利用LBitmap对象来显示这些箱子,nowBoxList数组用来储存这些已经加载到游戏界面上的箱子对象,之后用来判断游戏过关。
因为stage01数组中的4就表示箱子需要还原的位置,所以在判断游戏是否过关的时候,只需要判断下这些位置是否与所有箱子的位置重合,判断方法如下。
  1. function checkBox(){
  2.         var bitmap,x,y,win=true;
  3.         list = [];
  4.         for(var i=0;i<stageList[stageIndex].length;i++){
  5.                 list.push(stageList[stageIndex][i].join(",").split(","));
  6.         }
  7.        
  8.         for(var i=0;i<nowBoxList.length;i++){
  9.                 bitmap = nowBoxList[i];
  10.                 x = bitmap.x / STEP;
  11.                 y = bitmap.y / STEP;
  12.                 if(list[y][x] == 4){
  13.                         bitmap.bitmapData = bitmapDataList[3];
  14.                 }else{
  15.                         bitmap.bitmapData = bitmapDataList[2];
  16.                         win = false;
  17.                 }
  18.                 list[y][x] += 10;
  19.         }
  20.         if(win)gameClearShow();
  21. }
复制代码
代码我是直接从程序中截取的,所以出现了些新的数组和对象。
stageList储存了所有的关卡信息,stageIndex是当前关卡的序号,stageList[stageIndex]就可以获取当前关卡的信息,bitmapDataList数组储存了图1中小图片的LBitmapData对象,这些暂且不说,关键是下面。
  1.                 if(list[y][x] == 4){
  2.                         bitmap.bitmapData = bitmapDataList[3];
  3.                 }else{
  4.                         bitmap.bitmapData = bitmapDataList[2];
  5.                         win = false;
  6.                 }
复制代码
函数中循环了所有箱子的位置,如果他们的位置的序号为4,则表示该箱子已经归位,全部归位表示过关,否则游戏继续,返回false。
三,主人公登场,推动箱子。
同样准备一张图片,如下

图3
人物走动动画,当然就需要lufylegend引擎中另一个重要的对象LAnimation,它专门用来顺序播放图片以形成动画,具体用法请参照官方API文档。
下面是主人公的构造器
  1. /**
  2. * 循环事件
  3. * @param data 图片数据
  4. * @param row 图片分割行数
  5. * @param col 图片分割列数
  6. **/
  7. function Character(data,row,col){
  8.         base(this,LSprite,[]);
  9.         var self = this;
  10.         //设定人物动作速度
  11.         self.speed = 2;
  12.         self.speedIndex = 0;
  13.         //设定人物大小
  14.         data.setProperties(0,0,data.image.width/col,data.image.height/row);
  15.         //得到人物图片拆分数组
  16.         var list = LGlobal.divideCoordinate(data.image.width,data.image.height,row,col);
  17.         //设定人物动画
  18.         self.anime = new LAnimation(this,data,list);
  19.         //设定不移动
  20.         self.move = false;
  21.         //在一个移动步长中的移动次数设定
  22.         self.moveIndex = 0;
  23. };
复制代码
主人公如何推动箱子,看下面的onmove函数
  1. /**
  2. * 开始移动
  3. **/
  4. Character.prototype.onmove = function (){
  5.         var self = this;
  6.         //设定一个移动步长中的移动次数
  7.         var ml_cnt = 4;
  8.         //计算一次移动的长度
  9.         var ml = STEP/ml_cnt;
  10.         //根据移动方向,开始移动
  11.         switch (self.direction){
  12.                 case UP:
  13.                         self.y -= ml;
  14.                         if(box)box.y -= ml;
  15.                         break;
  16.                 case LEFT:
  17.                         self.x -= ml;
  18.                         if(box)box.x -= ml;
  19.                         break;
  20.                 case RIGHT:
  21.                         self.x += ml;
  22.                         if(box)box.x += ml;
  23.                         break;
  24.                 case DOWN:
  25.                         self.y += ml;
  26.                         if(box)box.y += ml;
  27.                         break;
  28.         }
  29.         self.moveIndex++;
  30.         //当移动次数等于设定的次数,开始判断是否继续移动
  31.         if(self.moveIndex >= ml_cnt){
  32.                 self.moveIndex = 0;
  33.                 box = null;
  34.                 self.move = false;
  35.                 checkBox();
  36.         }
  37. };
复制代码
可以看到,箱子是不是跟着主人公一起走,关键是要看box这个变量,这个变量的值是在下面的checkRoad函数中设置的。
  1. Character.prototype.checkRoad = function (dir){
  2.         var self = this;
  3.         var tox,toy;
  4.         //开始计算移动目的地的坐标
  5.         switch (dir){
  6.                 case UP:
  7.                         tox = 0;
  8.                         toy = -1;
  9.                         break;
  10.                 case LEFT:
  11.                         tox = -1;
  12.                         toy = 0;
  13.                         break;
  14.                 case RIGHT:
  15.                         tox = 1;
  16.                         toy = 0;
  17.                         break;
  18.                 case DOWN:
  19.                         tox = 0;
  20.                         toy = 1;
  21.                         break;
  22.         }
  23.         if(list[self.y/STEP + toy][self.x/STEP + tox]==1)return false;
  24.         if(list[self.y/STEP + toy][self.x/STEP + tox]>4){
  25.                 if(list[self.y/STEP + toy*2][self.x/STEP + tox*2]==1 || list[self.y/STEP + toy*2][self.x/STEP + tox*2]>4)return false;
  26.                 box = getBox(self.x + tox*STEP,self.y + toy*STEP);
  27.         }
  28.         return true;
  29. };
复制代码
其实,就是判断一下主人公要走的方向的前方是不是有障碍物,如果障碍物是墙则不可移动,如果是箱子的话,就要看看箱子的后面是不是有障碍物,如果有则不可移动,否则箱子就需要跟着主人公一起移动,将box设置为主人公前方的箱子即可。
上面这个函数,是在人物即将发生移动的时候被调用的,如下。
  1. /**
  2. * 改变人物方向,并判断是否可移动
  3. **/
  4. Character.prototype.changeDir = function (dir){
  5.         var self = this;
  6.         if(self.move)return;
  7.         self.direction = dir;
  8.         self.anime.setAction(dir);
  9.         if(!self.checkRoad(dir))return;
  10.         self.move = true;
  11.         steps.text = parseInt(steps.text) + 1;
  12. };
复制代码
当图1中的方向图标被按下的时候,根据点击的方向,来实现人物的移动。
点击方向图标的方法当然是鼠标事件
  1. ctrlLayer.addEventListener(LMouseEvent.MOUSE_UP,onCtrl);
复制代码
然后在onCtrl函数中根据点击的位置,来进行相应的移动。
  1. function onCtrl(event){
  2.         var ctrlSize = 60;
  3.         if(event.selfX >= ctrlSize && event.selfX <= ctrlSize*2){
  4.                 if(event.selfY >= 0 && event.selfY <= ctrlSize){
  5.                         player.changeDir(UP);
  6.                 }else if(event.selfY >= ctrlSize*2 && event.selfY <= ctrlSize*3){
  7.                         player.changeDir(DOWN);
  8.                 }
  9.         }else if(event.selfY >= ctrlSize && event.selfY <= ctrlSize*2){
  10.                 if(event.selfX >= 0 && event.selfX <= ctrlSize){
  11.                         player.changeDir(LEFT);
  12.                 }else if(event.selfX >= ctrlSize*2 && event.selfX <= ctrlSize*3){
  13.                         player.changeDir(RIGHT);
  14.                 }
  15.         }
  16. }
复制代码
这样,游戏的主要功能部分,就介绍完了。
四,建一个开始画面
如下。


图4

使用lufylegend.js引擎做个界面,可以说毫无难度,代码如下。
  1. function GameLogo(){
  2.         base(this,LSprite,[]);
  3.         var self = this;
  4.        
  5.         var logolist = [[1,1,1,1],[1,2,4,1],[1,4,2,1],[1,1,1,1]];
  6.         var bitmap,logoLayer;
  7.        
  8.         logoLayer = new LSprite();
  9.         logoLayer.graphics.drawRect(6,"#FF7F50",[0,0,LGlobal.width,LGlobal.height],true,"#FFDAB9");
  10.         self.addChild(logoLayer);
  11.        
  12.         logoLayer = new LSprite();
  13.         logoLayer.x = 50;
  14.         logoLayer.y = 50;
  15.         for(var i=0;i<logolist.length;i++){
  16.                 for(var j=0;j<logolist.length;j++){
  17.                         bitmap = new LBitmap(bitmapDataList[logolist[i][j]]);
  18.                         bitmap.x = j*STEP;
  19.                         bitmap.y = i*STEP;
  20.                         logoLayer.addChild(bitmap);
  21.                 }
  22.         }
  23.         bitmap = new LBitmap(new LBitmapData(imglist["player"],0,0,STEP,STEP));
  24.         bitmap.x = STEP;
  25.         bitmap.y = 2*STEP;
  26.         logoLayer.addChild(bitmap);
  27.         self.addChild(logoLayer);
  28.        
  29.         labelText = new LTextField();
  30.         labelText.rotate = -20;
  31.         labelText.color = "#4B0082";
  32.         labelText.font = "HG行書体";
  33.         labelText.size = 100;
  34.         labelText.x = 300;
  35.         labelText.y = 50;
  36.         labelText.stroke = true;
  37.         labelText.lineWidth = 4;
  38.         labelText.text = "推";
  39.         self.addChild(labelText);
  40.        
  41.         labelText = new LTextField();
  42.         labelText.color = "#4B0082";
  43.         labelText.font = "HG行書体";
  44.         labelText.size = 100;
  45.         labelText.x = 450;
  46.         labelText.y = 60;
  47.         labelText.stroke = true;
  48.         labelText.lineWidth = 4;
  49.         labelText.text = "箱";
  50.         self.addChild(labelText);
  51.        
  52.         labelText = new LTextField();
  53.         labelText.rotate = 20;
  54.         labelText.color = "#4B0082";
  55.         labelText.font = "HG行書体";
  56.         labelText.size = 100;
  57.         labelText.x = 600;
  58.         labelText.y = 60;
  59.         labelText.stroke = true;
  60.         labelText.lineWidth = 4;
  61.         labelText.text = "子";
  62.         self.addChild(labelText);
  63.        
  64.         labelText = new LTextField();
  65.         labelText.color = "#B22222";
  66.         labelText.font = "HG行書体";
  67.         labelText.size = 40;
  68.         labelText.x = 100;
  69.         labelText.y = 250;
  70.         labelText.stroke = true;
  71.         labelText.lineWidth = 4;
  72.         labelText.text = "Click to Start Game !!";
  73.         self.addChild(labelText);
  74.        
  75.         var social = new Social();
  76.         social.x = 220;
  77.         social.y = 330;
  78.         self.addChild(social);
  79.        
  80.         labelText = new LTextField();
  81.         labelText.font = "HG行書体";
  82.         labelText.size = 14;
  83.         labelText.x = 400;
  84.         labelText.y = 390;
  85.         labelText.text = "- Html5 Game Engine lufylegend.js";
  86.         self.addChild(labelText);
  87.         labelText = new LTextField();
  88.         labelText.color = "#006400";
  89.         labelText.font = "HG行書体";
  90.         labelText.size = 14;
  91.         labelText.x = 400;
  92.         labelText.y = 410;
  93.         labelText.text = "http://www.lufylegend.com/lufylegend";
  94.         self.addChild(labelText);
  95.        
  96.         self.addEventListener(LMouseEvent.MOUSE_UP,menuShow);
  97. };
复制代码
就是显示几张图片,以及添加一些文字,LTextField对象的使用方法请参考官方API文档。
五,建一个选择画面
如下。


图5

代码如下。
  1. function GameMenu(){
  2.         base(this,LSprite,[]);
  3.         var self = this;
  4.        
  5.         var menuLayer;
  6.         menuLayer = new LSprite();
  7.         menuLayer.graphics.drawRect(6,"#ADD8E6",[0,0,LGlobal.width,LGlobal.height],true,"#E6E6FA");
  8.         self.addChild(menuLayer);
  9.        
  10.         labelText = new LTextField();
  11.         labelText.color = "#B22222";
  12.         labelText.font = "HG行書体";
  13.         labelText.size = 40;
  14.         labelText.x = 200;
  15.         labelText.y = 30;
  16.         labelText.stroke = true;
  17.         labelText.lineWidth = 4;
  18.         labelText.text = "Please select !!";
  19.         menuLayer.addChild(labelText);
  20.         for(var i=0;i<stageMenu.length;i++){
  21.                 self.stageVsMenu(stageMenu[i]);
  22.         }
  23. };
  24. GameMenu.prototype.stageVsMenu = function(obj){
  25.         var self = this;
  26.        
  27.         var menuButton,btn_up;
  28.         if(obj.open){
  29.                 btn_up = new LSprite();
  30.                 btn_up.graphics.drawRect(2,"#000",[0,0,150,90],true,"#191970");
  31.                 labelText = new LTextField();
  32.                 labelText.color = "#ffffff";
  33.                 labelText.font = "HG行書体";
  34.                 labelText.size = 20;
  35.                 labelText.x = 40;
  36.                 labelText.y = 5;
  37.                 btn_up.addChild(labelText)
  38.                 labelText.text = "第"+(obj.index+1)+"关";
  39.                
  40.                 labelText = new LTextField();
  41.                 labelText.color = "#ffffff";
  42.                 labelText.font = "HG行書体";
  43.                 labelText.size = 12;
  44.                 labelText.x = 10;
  45.                 labelText.y = 40;
  46.                 btn_up.addChild(labelText)
  47.                 labelText.text = "step:"+obj.step;
  48.                 labelText = new LTextField();
  49.                 labelText.color = "#ffffff";
  50.                 labelText.font = "HG行書体";
  51.                 labelText.size = 12;
  52.                 labelText.x = 10;
  53.                 labelText.y = 60;
  54.                 btn_up.addChild(labelText)
  55.                 labelText.text = "times:"+obj.times;
  56.                
  57.                
  58.                 var btn_down = new LSprite();
  59.                 btn_down.graphics.drawRect(2,"#000",[0,0,150,90],true,"#2F4F4F");
  60.                 labelText = new LTextField();
  61.                 labelText.color = "#ffffff";
  62.                 labelText.font = "HG行書体";
  63.                 labelText.size = 20;
  64.                 labelText.x = 40;
  65.                 labelText.y = 5;
  66.                 labelText.text = "第"+(obj.index+1)+"关";
  67.                 btn_down.addChild(labelText);
  68.                
  69.                 labelText = new LTextField();
  70.                 labelText.color = "#ffffff";
  71.                 labelText.font = "HG行書体";
  72.                 labelText.size = 12;
  73.                 labelText.x = 10;
  74.                 labelText.y = 40;
  75.                 btn_down.addChild(labelText)
  76.                 labelText.text = "step:"+obj.step;
  77.                 labelText = new LTextField();
  78.                 labelText.color = "#ffffff";
  79.                 labelText.font = "HG行書体";
  80.                 labelText.size = 12;
  81.                 labelText.x = 10;
  82.                 labelText.y = 60;
  83.                 btn_down.addChild(labelText)
  84.                 labelText.text = "times:"+obj.times;
  85.                
  86.                 menuButton = new LButton(btn_up,btn_down);
  87.                 menuButton.obj = obj;
  88.                 menuButton.addEventListener(LMouseEvent.MOUSE_UP,function(event,self){
  89.                         gameStart(self.obj.index);
  90.                 });
  91.         }else{
  92.                 btn_up = new LSprite();
  93.                 btn_up.graphics.drawRect(2,"#000",[0,0,150,90],true,"#708090");
  94.                 labelText = new LTextField();
  95.                 labelText.color = "#ffffff";
  96.                 labelText.font = "HG行書体";
  97.                 labelText.size = 20;
  98.                 labelText.x = 40;
  99.                 labelText.y = 5;
  100.                 btn_up.addChild(labelText)
  101.                 labelText.text = "???";
  102.                 menuButton = btn_up;
  103.         };
  104.         self.addChild(menuButton);
  105.         menuButton.x = obj.x * 220 + 100;
  106.         menuButton.y = obj.y * 140 + 130;
  107. }
复制代码
好了,游戏主要的代码已经都贴出来了。

源码
由于上面的代码比较零碎,而且我也只是挑中心部分说了一下,下面提供完整游戏源代码,想研究一下的朋友可以点击下面的连接下载。


注意:该附件只包含本次文章源码,lufylegend.js引擎请到http://lufylegend.com/lufylegend进行下载。


最后,转载请注明出处,谢谢。

不回答与技术和引擎不相关的问题
回复

使用道具 举报

0

主题

0

好友

4

积分

士兵

Rank: 1

发表于 2013-3-4 16:09:47 |显示全部楼层
顶个
回复

使用道具 举报

0

主题

0

好友

10

积分

士兵

Rank: 1

发表于 2013-3-13 13:17:30 |显示全部楼层
很好,以前很喜欢这样的游戏.
回复

使用道具 举报

1

主题

0

好友

19

积分

士兵

Rank: 1

发表于 2013-4-4 14:36:44 |显示全部楼层
小时候常玩   现在回顾一下
回复

使用道具 举报

0

主题

0

好友

8

积分

士兵

Rank: 1

发表于 2014-2-15 19:14:57 |显示全部楼层


  这是什么?
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

防止垃圾广告,请填写任意字符

Archiver|lufy's legend

GMT+8, 2024-3-29 13:57 , Processed in 0.066048 second(s), 27 queries .

Powered by Discuz! X2.5

© 2001-2012 Comsenz Inc.

回顶部