lufy 发表于 2013-2-25 20:16:31

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

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

这是我用最新版lufylegend.js引擎开发的,想挑战一下的朋友,可以点击下面的游戏链接试一下自己能通过几关。http://lufylegend.com/demo/box游戏一共6关,我在游戏里加入了排名系统,每过一关可以上传自己的成绩,跟大家比拼一下,或者您也可以将自己的过关方法心得等回复到文章下面。
制作开始好了,废话说完了,现在来看看如何来制作这款游戏。一,首先,你需要下载lufylegend.js引擎下面是我在博客的lufylegend-1.6.0发布帖http://blog.csdn.net/lufy_legend/article/details/8593968

下面一步步来进入开发正题。二,绘制背景和箱子我们先来准备一张图,http://lufy.netne.net/lufylegend_blog_img/png/1.png
图2如果我们将上面的图平均分割成5份,那么他们的序号分别为0,1,2,3,4。我们利用上面5个小图片就可以拼接任意的房间以及房间内箱子的摆放。比如,我博客一开始的示例图1,它是游戏第一关的截图,要绘制这个房间,首先要知道这些图片应该摆放的位置,我们预先准备一个数组。var stage01 = [
[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
[-1,-1, 1, 1, 1, 1, 1, 1,-1,-1,-1],
[-1,-1, 1, 0, 0, 0, 0, 1, 1,-1,-1],
[-1,-1, 1, 0, 0, 4, 0, 0, 1,-1,-1],
[-1,-1, 1, 4, 4, 0, 4, 4, 1,-1,-1],
[-1,-1, 1, 0, 0, 4, 0, 0, 1,-1,-1],
[-1,-1, 1, 1, 0, 0, 0, 0, 1,-1,-1],
[-1,-1,-1, 1, 1, 1, 1, 1, 1,-1,-1],
[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1]
];上面的-1表示不摆放,然后0,1,4等分别对应着图2中的序号。
按照这个数组来绘制房间就简单了,看下面的函数function drawFloor(x,y){
        if(list < 0)return;
        var bitmap = new LBitmap(bitmapDataList]);
        bitmap.x = x*STEP;
        bitmap.y = y*STEP;
        boxLayer.addChild(bitmap);
}这个list数组就是上面的stage01数组,参数x,y分别是数组的列和行的序号,STEP是每个小图片的长度,绘制一个小图块,其实我就是建立了一个LBitmap对象。
关于LSprite,LBitmap等对象是lufylegend.js引擎中很常用的对象,关于它们的用法,请大家参照官方的API文档,或者看我以前的一些文章来了解一下,这里不加赘述了。当然,一开始,房间里要有箱子,箱子一开始的位置也是需要预先设置好的,我们同样建立一个数组。var box01 = [
{x:3,y:3},
{x:4,y:3},
{x:5,y:3},
{x:5,y:5},
{x:6,y:5},
{x:7,y:5}
];绘制箱子的函数如下function drawBox(){
        var bitmap;
        for(var i=0;i<boxList.length;i++){
                bitmap = new LBitmap(bitmapDataList);
                bitmap.x = boxList.x*STEP;
                bitmap.y = boxList.y*STEP;
                boxLayer.addChild(bitmap);
                nowBoxList.push(bitmap);
        }
}上面,我同样利用LBitmap对象来显示这些箱子,nowBoxList数组用来储存这些已经加载到游戏界面上的箱子对象,之后用来判断游戏过关。因为stage01数组中的4就表示箱子需要还原的位置,所以在判断游戏是否过关的时候,只需要判断下这些位置是否与所有箱子的位置重合,判断方法如下。function checkBox(){
        var bitmap,x,y,win=true;
        list = [];
        for(var i=0;i<stageList.length;i++){
                list.push(stageList.join(",").split(","));
        }
       
        for(var i=0;i<nowBoxList.length;i++){
                bitmap = nowBoxList;
                x = bitmap.x / STEP;
                y = bitmap.y / STEP;
                if(list == 4){
                        bitmap.bitmapData = bitmapDataList;
                }else{
                        bitmap.bitmapData = bitmapDataList;
                        win = false;
                }
                list += 10;
        }
        if(win)gameClearShow();
}代码我是直接从程序中截取的,所以出现了些新的数组和对象。
stageList储存了所有的关卡信息,stageIndex是当前关卡的序号,stageList就可以获取当前关卡的信息,bitmapDataList数组储存了图1中小图片的LBitmapData对象,这些暂且不说,关键是下面。                if(list == 4){
                        bitmap.bitmapData = bitmapDataList;
                }else{
                        bitmap.bitmapData = bitmapDataList;
                        win = false;
                }函数中循环了所有箱子的位置,如果他们的位置的序号为4,则表示该箱子已经归位,全部归位表示过关,否则游戏继续,返回false。
三,主人公登场,推动箱子。同样准备一张图片,如下http://lufy.netne.net/lufylegend_blog_img/png/2.png
图3人物走动动画,当然就需要lufylegend引擎中另一个重要的对象LAnimation,它专门用来顺序播放图片以形成动画,具体用法请参照官方API文档。下面是主人公的构造器/**
* 循环事件
* @param data 图片数据
* @param row 图片分割行数
* @param col 图片分割列数
**/
function Character(data,row,col){
        base(this,LSprite,[]);
        var self = this;
        //设定人物动作速度
        self.speed = 2;
        self.speedIndex = 0;
        //设定人物大小
        data.setProperties(0,0,data.image.width/col,data.image.height/row);
        //得到人物图片拆分数组
        var list = LGlobal.divideCoordinate(data.image.width,data.image.height,row,col);
        //设定人物动画
        self.anime = new LAnimation(this,data,list);
        //设定不移动
        self.move = false;
        //在一个移动步长中的移动次数设定
        self.moveIndex = 0;
};主人公如何推动箱子,看下面的onmove函数/**
* 开始移动
**/
Character.prototype.onmove = function (){
        var self = this;
        //设定一个移动步长中的移动次数
        var ml_cnt = 4;
        //计算一次移动的长度
        var ml = STEP/ml_cnt;
        //根据移动方向,开始移动
        switch (self.direction){
                case UP:
                        self.y -= ml;
                        if(box)box.y -= ml;
                        break;
                case LEFT:
                        self.x -= ml;
                        if(box)box.x -= ml;
                        break;
                case RIGHT:
                        self.x += ml;
                        if(box)box.x += ml;
                        break;
                case DOWN:
                        self.y += ml;
                        if(box)box.y += ml;
                        break;
        }
        self.moveIndex++;
        //当移动次数等于设定的次数,开始判断是否继续移动
        if(self.moveIndex >= ml_cnt){
                self.moveIndex = 0;
                box = null;
                self.move = false;
                checkBox();
        }
};可以看到,箱子是不是跟着主人公一起走,关键是要看box这个变量,这个变量的值是在下面的checkRoad函数中设置的。Character.prototype.checkRoad = function (dir){
        var self = this;
        var tox,toy;
        //开始计算移动目的地的坐标
        switch (dir){
                case UP:
                        tox = 0;
                        toy = -1;
                        break;
                case LEFT:
                        tox = -1;
                        toy = 0;
                        break;
                case RIGHT:
                        tox = 1;
                        toy = 0;
                        break;
                case DOWN:
                        tox = 0;
                        toy = 1;
                        break;
        }
        if(list==1)return false;
        if(list>4){
                if(list==1 || list>4)return false;
                box = getBox(self.x + tox*STEP,self.y + toy*STEP);
        }
        return true;
};其实,就是判断一下主人公要走的方向的前方是不是有障碍物,如果障碍物是墙则不可移动,如果是箱子的话,就要看看箱子的后面是不是有障碍物,如果有则不可移动,否则箱子就需要跟着主人公一起移动,将box设置为主人公前方的箱子即可。
上面这个函数,是在人物即将发生移动的时候被调用的,如下。/**
* 改变人物方向,并判断是否可移动
**/
Character.prototype.changeDir = function (dir){
        var self = this;
        if(self.move)return;
        self.direction = dir;
        self.anime.setAction(dir);
        if(!self.checkRoad(dir))return;
        self.move = true;
        steps.text = parseInt(steps.text) + 1;
};当图1中的方向图标被按下的时候,根据点击的方向,来实现人物的移动。
点击方向图标的方法当然是鼠标事件ctrlLayer.addEventListener(LMouseEvent.MOUSE_UP,onCtrl);然后在onCtrl函数中根据点击的位置,来进行相应的移动。function onCtrl(event){
        var ctrlSize = 60;
        if(event.selfX >= ctrlSize && event.selfX <= ctrlSize*2){
                if(event.selfY >= 0 && event.selfY <= ctrlSize){
                        player.changeDir(UP);
                }else if(event.selfY >= ctrlSize*2 && event.selfY <= ctrlSize*3){
                        player.changeDir(DOWN);
                }
        }else if(event.selfY >= ctrlSize && event.selfY <= ctrlSize*2){
                if(event.selfX >= 0 && event.selfX <= ctrlSize){
                        player.changeDir(LEFT);
                }else if(event.selfX >= ctrlSize*2 && event.selfX <= ctrlSize*3){
                        player.changeDir(RIGHT);
                }
        }
}这样,游戏的主要功能部分,就介绍完了。
四,建一个开始画面如下。
http://lufy.netne.net/lufylegend_blog_img/jpg/3.jpg
图4
使用lufylegend.js引擎做个界面,可以说毫无难度,代码如下。function GameLogo(){
        base(this,LSprite,[]);
        var self = this;
       
        var logolist = [,,,];
        var bitmap,logoLayer;
       
        logoLayer = new LSprite();
        logoLayer.graphics.drawRect(6,"#FF7F50",,true,"#FFDAB9");
        self.addChild(logoLayer);
       
        logoLayer = new LSprite();
        logoLayer.x = 50;
        logoLayer.y = 50;
        for(var i=0;i<logolist.length;i++){
                for(var j=0;j<logolist.length;j++){
                        bitmap = new LBitmap(bitmapDataList]);
                        bitmap.x = j*STEP;
                        bitmap.y = i*STEP;
                        logoLayer.addChild(bitmap);
                }
        }
        bitmap = new LBitmap(new LBitmapData(imglist["player"],0,0,STEP,STEP));
        bitmap.x = STEP;
        bitmap.y = 2*STEP;
        logoLayer.addChild(bitmap);
        self.addChild(logoLayer);
       
        labelText = new LTextField();
        labelText.rotate = -20;
        labelText.color = "#4B0082";
        labelText.font = "HG行書体";
        labelText.size = 100;
        labelText.x = 300;
        labelText.y = 50;
        labelText.stroke = true;
        labelText.lineWidth = 4;
        labelText.text = "推";
        self.addChild(labelText);
       
        labelText = new LTextField();
        labelText.color = "#4B0082";
        labelText.font = "HG行書体";
        labelText.size = 100;
        labelText.x = 450;
        labelText.y = 60;
        labelText.stroke = true;
        labelText.lineWidth = 4;
        labelText.text = "箱";
        self.addChild(labelText);
       
        labelText = new LTextField();
        labelText.rotate = 20;
        labelText.color = "#4B0082";
        labelText.font = "HG行書体";
        labelText.size = 100;
        labelText.x = 600;
        labelText.y = 60;
        labelText.stroke = true;
        labelText.lineWidth = 4;
        labelText.text = "子";
        self.addChild(labelText);
       
        labelText = new LTextField();
        labelText.color = "#B22222";
        labelText.font = "HG行書体";
        labelText.size = 40;
        labelText.x = 100;
        labelText.y = 250;
        labelText.stroke = true;
        labelText.lineWidth = 4;
        labelText.text = "Click to Start Game !!";
        self.addChild(labelText);
       
        var social = new Social();
        social.x = 220;
        social.y = 330;
        self.addChild(social);
       
        labelText = new LTextField();
        labelText.font = "HG行書体";
        labelText.size = 14;
        labelText.x = 400;
        labelText.y = 390;
        labelText.text = "- Html5 Game Engine lufylegend.js";
        self.addChild(labelText);
        labelText = new LTextField();
        labelText.color = "#006400";
        labelText.font = "HG行書体";
        labelText.size = 14;
        labelText.x = 400;
        labelText.y = 410;
        labelText.text = "http://www.lufylegend.com/lufylegend";
        self.addChild(labelText);
       
        self.addEventListener(LMouseEvent.MOUSE_UP,menuShow);
};就是显示几张图片,以及添加一些文字,LTextField对象的使用方法请参考官方API文档。
五,建一个选择画面如下。
http://lufy.netne.net/lufylegend_blog_img/jpg/4.jpg
图5
代码如下。function GameMenu(){
        base(this,LSprite,[]);
        var self = this;
       
        var menuLayer;
        menuLayer = new LSprite();
        menuLayer.graphics.drawRect(6,"#ADD8E6",,true,"#E6E6FA");
        self.addChild(menuLayer);
       
        labelText = new LTextField();
        labelText.color = "#B22222";
        labelText.font = "HG行書体";
        labelText.size = 40;
        labelText.x = 200;
        labelText.y = 30;
        labelText.stroke = true;
        labelText.lineWidth = 4;
        labelText.text = "Please select !!";
        menuLayer.addChild(labelText);
        for(var i=0;i<stageMenu.length;i++){
                self.stageVsMenu(stageMenu);
        }
};
GameMenu.prototype.stageVsMenu = function(obj){
        var self = this;
       
        var menuButton,btn_up;
        if(obj.open){
                btn_up = new LSprite();
                btn_up.graphics.drawRect(2,"#000",,true,"#191970");
                labelText = new LTextField();
                labelText.color = "#ffffff";
                labelText.font = "HG行書体";
                labelText.size = 20;
                labelText.x = 40;
                labelText.y = 5;
                btn_up.addChild(labelText)
                labelText.text = "第"+(obj.index+1)+"关";
               
                labelText = new LTextField();
                labelText.color = "#ffffff";
                labelText.font = "HG行書体";
                labelText.size = 12;
                labelText.x = 10;
                labelText.y = 40;
                btn_up.addChild(labelText)
                labelText.text = "step:"+obj.step;
                labelText = new LTextField();
                labelText.color = "#ffffff";
                labelText.font = "HG行書体";
                labelText.size = 12;
                labelText.x = 10;
                labelText.y = 60;
                btn_up.addChild(labelText)
                labelText.text = "times:"+obj.times;
               
               
                var btn_down = new LSprite();
                btn_down.graphics.drawRect(2,"#000",,true,"#2F4F4F");
                labelText = new LTextField();
                labelText.color = "#ffffff";
                labelText.font = "HG行書体";
                labelText.size = 20;
                labelText.x = 40;
                labelText.y = 5;
                labelText.text = "第"+(obj.index+1)+"关";
                btn_down.addChild(labelText);
               
                labelText = new LTextField();
                labelText.color = "#ffffff";
                labelText.font = "HG行書体";
                labelText.size = 12;
                labelText.x = 10;
                labelText.y = 40;
                btn_down.addChild(labelText)
                labelText.text = "step:"+obj.step;
                labelText = new LTextField();
                labelText.color = "#ffffff";
                labelText.font = "HG行書体";
                labelText.size = 12;
                labelText.x = 10;
                labelText.y = 60;
                btn_down.addChild(labelText)
                labelText.text = "times:"+obj.times;
               
                menuButton = new LButton(btn_up,btn_down);
                menuButton.obj = obj;
                menuButton.addEventListener(LMouseEvent.MOUSE_UP,function(event,self){
                        gameStart(self.obj.index);
                });
        }else{
                btn_up = new LSprite();
                btn_up.graphics.drawRect(2,"#000",,true,"#708090");
                labelText = new LTextField();
                labelText.color = "#ffffff";
                labelText.font = "HG行書体";
                labelText.size = 20;
                labelText.x = 40;
                labelText.y = 5;
                btn_up.addChild(labelText)
                labelText.text = "???";
                menuButton = btn_up;
        };
        self.addChild(menuButton);
        menuButton.x = obj.x * 220 + 100;
        menuButton.y = obj.y * 140 + 130;
}好了,游戏主要的代码已经都贴出来了。
源码由于上面的代码比较零碎,而且我也只是挑中心部分说了一下,下面提供完整游戏源代码,想研究一下的朋友可以点击下面的连接下载。
http://lufy.netne.net/lufylegend_download/box.rar

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

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

li_star 发表于 2013-3-4 16:09:47

顶个 {:3_46:}

wangcheng800718 发表于 2013-3-13 13:17:30

很好,以前很喜欢这样的游戏.

qqqwwezwt 发表于 2013-4-4 14:36:44

{:3_41:} 小时候常玩   现在回顾一下

信服了啊 发表于 2014-2-15 19:14:57



  这是什么?
页: [1]
查看完整版本: [HTML5游戏开发]推箱子小游戏