JavaScript编写推箱子游戏


Posted in Javascript onJuly 07, 2015

推箱子游戏是老游戏了, 网上有各种各样的版本, 说下推箱子游戏的简单实现,以及我找到的一些参考视频和实例;

如下是效果图:

JavaScript编写推箱子游戏

这个拖箱子游戏做了移动端的适配, 我使用了zepto的touch模块, 通过手指滑动屏幕就可以控制乌龟走不同的方向;

因为推箱子这个游戏比较简单, 直接用了过程式的方式写代码, 模块也就是两个View 和 Model, 剩下就是用户的事件Controller, 用户每一次按下键盘的方向键都会改变数据模型的数据,然后重新生成游戏的静态html, 然后用innerHTML方式插入到界面, 自动生成DOM节点;

游戏的关卡模型就是数据, 我把每一关的数据分为三块:

地图数据,二维数组(地图数据包括板砖, 箱子要去的目标位置, 空白的位置)

箱子数据,一维数组(箱子的初始位置)

小乌龟的数据,json对象

每一个关卡都有对应的游戏关卡数据, 模拟的数据如下:

level: [
        {
          //0是空的地图
          //1是板砖
          //3是目标点
          state:[
            [0,0,1,1,1,0,0,0,0],
            [0,1,1,3,3,1,0,0,0],
            [0,1,0,0,0,0,1,0,0],
            [0,1,0,0,0,0,1,0,0],
            [0,1,1,1,1,1,1,0,0]
          ],
          person: {x : 2, y : 2},
          box: [{x:3, y : 2},{x:4,y:2}]
        },
        //第二关
        {
          //0是空的地图
          //1是板砖
          //3是目标点
          state:[
            [0,1,1,1,1,1,0,0],
            [0,1,0,0,1,1,1,0],
            [0,1,0,0,0,0,1,0],
            [1,1,1,0,1,0,1,1],
            [1,3,1,0,1,0,0,1],
            [1,3,0,0,0,1,0,1],
            [1,3,0,0,0,0,0,1],
            [1,1,1,1,1,1,1,1]
          ],
          person: {x : 2, y : 2},
          box: [{x:3, y : 2}, {x:2,y:5} ,{x:5, y:6}]
          /*
          box : [
            {x:3, y : 1},
            {x:4, y : 1},
            {x:4, y : 2},
            {x:5, y : 5}
          ]
          */
        },
        //第三关
        {
          //0是空的地图
          //1是板砖
          //3是目标点
          state:[
            [0,0,0,1,1,1,1,1,1,0],
            [0,1,1,1,0,0,0,0,1,0],
            [1,1,3,0,0,1,1,0,1,1],
            [1,3,3,0,0,0,0,0,0,1],
            [1,3,3,0,0,0,0,0,1,1],
            [1,1,1,1,1,1,0,0,1,0],
            [0,0,0,0,0,1,1,1,1,0]
          ],
          person: {x : 8, y : 3},
          box: [{x:4, y : 2}, {x:3,y:3} ,{x:4, y:4},{x:5, y:3},{x:6, y:4}]
        },
        //第四关
        {
          //0是空的地图
          //1是板砖
          //3是目标点
          state:[
            [0,1,1,1,1,1,1,1,0,0],
            [0,1,0,0,0,0,0,1,1,1],
            [1,1,0,1,1,1,0,0,0,1],
            [1,0,0,0,0,0,0,0,0,1],
            [1,0,3,3,1,0,0,0,1,1],
            [1,1,3,3,1,0,0,0,1,0],
            [0,1,1,1,1,1,1,1,1,0]
          ],
          person: {x : 2, y : 3},
          box: [{x:2, y : 2}, {x:4,y:3} ,{x:6, y:4},{x:7, y:3},{x:6, y:4}]
        },
        //第五关
        {
          //0是空的地图
          //1是板砖
          //3是目标点
          state:[
            [0,0,1,1,1,1,0,0],
            [0,0,1,3,3,1,0,0],
            [0,1,1,0,3,1,1,0],
            [0,1,0,0,0,3,1,0],
            [1,1,0,0,0,0,1,1],
            [1,0,0,1,0,0,0,1],
            [1,0,0,0,0,0,0,1],
            [1,1,1,1,1,1,1,1]
          ],
          person: {x : 4, y : 6},
          box: [{x:4, y : 3}, {x:3,y:4} ,{x:4, y:5}, {x:5,y:5}]
          /*
           box : [
           {x:3, y : 1},
           {x:4, y : 1},
           {x:4, y : 2},
           {x:5, y : 5}
           ]
           */
        },
          //第六关
        {
          //0是空的地图
          //1是板砖
          //3是目标点
          state:[
            [0,0,0,0,1,1,1,1,1,1,1,0],
            [0,0,0,0,1,0,0,1,0,0,1,0],
            [0,0,0,0,1,0,0,0,0,0,1,0],
            [1,1,1,1,1,0,0,1,0,0,1,0],
            [3,3,3,1,1,0,0,0,0,0,1,1],
            [3,0,0,1,0,0,0,0,1,0,0,1],
            [3,0,0,0,0,0,0,0,0,0,0,1],
            [3,0,0,1,0,0,0,0,1,0,0,1],
            [3,3,3,1,1,1,0,1,0,0,1,1],
            [1,1,1,1,1,0,0,0,0,0,1,0],
            [0,0,0,0,1,0,0,1,0,0,1,0],
            [0,0,0,0,1,1,1,1,1,1,1,0]
          ],
          person: {x : 5, y : 10},
          box: [
            {x:5, y:6},
            {x:6, y:3},
            {x:6, y:5},
            {x:6, y:7},
            {x:6, y:9},
            {x:7, y:2},
            {x:8, y:2},
            {x:9, y:6}
          ]
        }
      ]

有一个很重要的东西就是推箱子游戏的主要逻辑:因为小乌龟走的地方只能是空白的区域,而且乌龟前面有墙就不能走, 或者乌龟前面是箱子,就再判断箱子前面是否有墙, 如果没有墙乌龟和箱子都可以走往前走一步,如果有墙就不能走。每一次小乌龟走了都改变地图数据,然后重新生成界面,如此循环, 每一小乌龟走完都要检测地图数据中的箱子数据是否全对上了,对上了就给用户提示, 并进入下一关;

游戏的模板引擎用了handlebarsJS, 可以去官网看API 。 这个是写过的一篇博客,Handlebars的使用方法文档整理(Handlebars.js):打开, 模板内容:

<script id="tpl" type="text/x-handlebars-template">
    {{#initY}}{{/initY}}
    {{#each this}}
      {{#each this}}
        <div class="{{#getClass this}}{{/getClass}}" data-x="{{@index}}" data-y="{{#getY}}{{/getY}}" style="left:{{#calc @index}}{{/calc}};top:{{#calc 1111}}{{/calc}}">
          <!--{{@index}}
          {{#getY}}{{/getY}}
          -->
        </div>
      {{/each}}
      {{#addY}}{{/addY}}
    {{/each}}
  </script>

为Handlebars定了几个helper,包括initY, getClass, getY,calc 、、、、,模板引擎主要是辅助的作用, 这边用Handlebars不是很明智啊, 代码的可读性变差了点, 这里面也利用了闭包保存变量, 避免全局变量的污染:

(function() {
      var y = 0;
      Handlebars.registerHelper("initY", function() {
        y = 0;
      });
      Handlebars.registerHelper("addY", function() {
        y++;
      });
      Handlebars.registerHelper("getY", function() {
        return y;
      });
      Handlebars.registerHelper("calc", function(arg) {
        //console.log(arg)
        if(arg!==1111) {
          return 50*arg + "px";
        }else{
          return 50*y + "px";
        };
      });
      Handlebars.registerHelper("getClass", function(arg) {
        switch( arg ) {
          case 0 :
            return "bg"
          case 1 :
            return "block"
          case 2 :
            return "box"
          case 3 :
            return "target"
        };
      });
      window.util = {
        isMobile : function() {
          return navigator.userAgent.toLowerCase().indexOf("mobile") !== -1 || navigator.userAgent.toLowerCase().indexOf("android") !== -1 || navigator.userAgent.toLowerCase().indexOf("pad") !== -1;
        }
      }
    })();

因为要兼容移动端, 我们要检查是否是手机或者平板,如果是的话,我就添加对应的DOM元素(方向键DOM元素),然后绑定对应的事件, zeptoJS提供了touch模块,我们要去官网去找,然后额外引用进来,打开地址 , 然后就可以使用swipeLeft,swipeUp,swipeDown, swipeRight 这几个事件:

if( window.util.isMobile() ) {
          $(window).on("swipeLeft",function() {
            _this.step("left");
          }).on("swipeRight",function() {
            _this.step("right");
          }).on("swipeUp",function() {
            _this.step("top");
          }).on("swipeDown",function() {
            _this.step("bottom");
          });
          mobileDOM();

          $(".arrow-up").tap(function() {
            _this.step("top");
          });
          $(".arrow-down").tap(function() {
            _this.step("bottom");
          });
          $(".arrow-left").tap(function() {
            _this.step("left");
          });
          $(".arrow-right").tap(function() {
            _this.step("right");
          });
        }else{
          $(window).on("keydown", function(ev) {
            var state = "";
            switch( ev.keyCode ) {
              case 37 :
                state = "left";
              break;
              case 39 :
                state = "right";
              break;
              case 38 :
                state = "top";
              break;
              case 40 :
                state = "bottom";
              break;
            };
            _this.step(state)
          });
        };

因为要保存用户的当前关卡, 也额外引用了jQuery-cookies插件, 每一次闯关成功,我们就保存一次当前的闯关记录, 当用户不想玩或者别的原因关闭了浏览器, 过几天想重新玩的时候可以继续玩;

if( G.now+1 > G.level.length-1 ) {
              alert("闯关成功");
              return ;
            }else{
              //如果可用的等级大于当前的等级,就把level设置进去;
              if( G.now+1 > parseInt( $.cookie('level') || 0 )) {
                $.cookie('level' , G.now+1 , { expires: 7 });
              };
              start( G.now+1 );
              return ;
            };

所有的代码在这里:

<!DOCTYPE html>
<html>
<head lang="en">
  <meta charset="UTF-8">
  <title></title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css">
  <link rel="stylesheet" href="http://sqqihao.github.io/games/rusBlock/libs/Tiny-Alert/css/zepto.alert.css"/>
  <script src="libs/jquery-1.9.1.min.js"></script>
  <script src="libs/handlebars.js"></script>
  <script src="libs/jquery-cookie.js"></script>
  <script src="http://sqqihao.github.io/games/rusBlock/libs/Tiny-Alert/js/zepto.alert.js"></script>
  <script id="tpl" type="text/x-handlebars-template">
    {{#initY}}{{/initY}}
    {{#each this}}
      {{#each this}}
        <div class="{{#getClass this}}{{/getClass}}" data-x="{{@index}}" data-y="{{#getY}}{{/getY}}" style="left:{{#calc @index}}{{/calc}};top:{{#calc 1111}}{{/calc}}">
          <!--{{@index}}
          {{#getY}}{{/getY}}
          -->
        </div>
      {{/each}}
      {{#addY}}{{/addY}}
    {{/each}}
  </script>
  <script>
    (function() {
      var y = 0;
      Handlebars.registerHelper("initY", function() {
        y = 0;
      });
      Handlebars.registerHelper("addY", function() {
        y++;
      });
      Handlebars.registerHelper("getY", function() {
        return y;
      });
      Handlebars.registerHelper("calc", function(arg) {
        //console.log(arg)
        if(arg!==1111) {
          return 50*arg + "px";
        }else{
          return 50*y + "px";
        };
      });
      Handlebars.registerHelper("getClass", function(arg) {
        switch( arg ) {
          case 0 :
            return "bg"
          case 1 :
            return "block"
          case 2 :
            return "box"
          case 3 :
            return "target"
        };
      });
      window.util = {
        isMobile : function() {
          return navigator.userAgent.toLowerCase().indexOf("mobile") !== -1 || navigator.userAgent.toLowerCase().indexOf("android") !== -1 || navigator.userAgent.toLowerCase().indexOf("pad") !== -1;
        }
      }
    })();
  </script>
</head>
<style>
  #game{
    display: none;
  }
  #house{
    position: relative;
  }
  .bg{
    position: absolute;
    width:50px;
    height:50px;
    box-sizing: border-box;
  }
  .block{
    position: absolute;
    background-image: url(imgs/wall.png);
    width:50px;
    height:50px;
    box-sizing: border-box;
  }
  .box{
    position: absolute;
    background: #fbd500;
    width:50px;
    height:50px;
    background-image: url(imgs/box.png);
  }
  .target{
    position: absolute;
    background: url(imgs/target.jpg);
    background-size: 50px 50px;;
    width:50px;
    height:50px;
    box-sizing: border-box;
  }
  #person{
    background-image: url(imgs/person.png);
    width:50px;
    height:50px;
    position: absolute;
  }
  #person.up{
    background-position: 0 0;
  }
  #person.right{
    background-position:-50px 0 ;
  }
  #person.bottom{
    background-position:-100px 0 ;
  }
  #person.left{
    background-position:-150px 0 ;
  }
  /*移动端的DOM*/
  .operate-bar{
    font-size:30px;
  }
  .height20percent{
    height:30%;
  }
  .height30percent{
    height:30%;
  }
  .height40percent{
    height:40%;
  }
  .height100percent{
    height:100%;
  }
  .font30{
    font-size:30px;
    color:#34495e;
  }
</style>
<body>
  <div id="select">
    <div class="container">
      <div class="row">
        <p class="text-info">
          已经解锁的关卡:
        <p id="level">
        </p>
        </p>
        <button id="start" class="btn btn-default">
          开始游戏
        </button>
      </div>
    </div>
  </div>
  <div id="game" class="container">
    <div class="row">
      <button onclick="location.reload()" class="btn btn-info" >
        返回选择关卡重新
      </button>
      <div id="house">
      </div>
    </div>
  </div>

  <script>
    G = {
      level: [
        {
          //0是空的地图
          //1是板砖
          //3是目标点
          state:[
            [0,0,1,1,1,0,0,0,0],
            [0,1,1,3,3,1,0,0,0],
            [0,1,0,0,0,0,1,0,0],
            [0,1,0,0,0,0,1,0,0],
            [0,1,1,1,1,1,1,0,0]
          ],
          person: {x : 2, y : 2},
          box: [{x:3, y : 2},{x:4,y:2}]
        },
        //第二关
        {
          //0是空的地图
          //1是板砖
          //3是目标点
          state:[
            [0,1,1,1,1,1,0,0],
            [0,1,0,0,1,1,1,0],
            [0,1,0,0,0,0,1,0],
            [1,1,1,0,1,0,1,1],
            [1,3,1,0,1,0,0,1],
            [1,3,0,0,0,1,0,1],
            [1,3,0,0,0,0,0,1],
            [1,1,1,1,1,1,1,1]
          ],
          person: {x : 2, y : 2},
          box: [{x:3, y : 2}, {x:2,y:5} ,{x:5, y:6}]
          /*
          box : [
            {x:3, y : 1},
            {x:4, y : 1},
            {x:4, y : 2},
            {x:5, y : 5}
          ]
          */
        },
        //第三关
        {
          //0是空的地图
          //1是板砖
          //3是目标点
          state:[
            [0,0,0,1,1,1,1,1,1,0],
            [0,1,1,1,0,0,0,0,1,0],
            [1,1,3,0,0,1,1,0,1,1],
            [1,3,3,0,0,0,0,0,0,1],
            [1,3,3,0,0,0,0,0,1,1],
            [1,1,1,1,1,1,0,0,1,0],
            [0,0,0,0,0,1,1,1,1,0]
          ],
          person: {x : 8, y : 3},
          box: [{x:4, y : 2}, {x:3,y:3} ,{x:4, y:4},{x:5, y:3},{x:6, y:4}]
        },
        //第四关
        {
          //0是空的地图
          //1是板砖
          //3是目标点
          state:[
            [0,1,1,1,1,1,1,1,0,0],
            [0,1,0,0,0,0,0,1,1,1],
            [1,1,0,1,1,1,0,0,0,1],
            [1,0,0,0,0,0,0,0,0,1],
            [1,0,3,3,1,0,0,0,1,1],
            [1,1,3,3,1,0,0,0,1,0],
            [0,1,1,1,1,1,1,1,1,0]
          ],
          person: {x : 2, y : 3},
          box: [{x:2, y : 2}, {x:4,y:3} ,{x:6, y:4},{x:7, y:3},{x:6, y:4}]
        },
        //第五关
        {
          //0是空的地图
          //1是板砖
          //3是目标点
          state:[
            [0,0,1,1,1,1,0,0],
            [0,0,1,3,3,1,0,0],
            [0,1,1,0,3,1,1,0],
            [0,1,0,0,0,3,1,0],
            [1,1,0,0,0,0,1,1],
            [1,0,0,1,0,0,0,1],
            [1,0,0,0,0,0,0,1],
            [1,1,1,1,1,1,1,1]
          ],
          person: {x : 4, y : 6},
          box: [{x:4, y : 3}, {x:3,y:4} ,{x:4, y:5}, {x:5,y:5}]
          /*
           box : [
           {x:3, y : 1},
           {x:4, y : 1},
           {x:4, y : 2},
           {x:5, y : 5}
           ]
           */
        },
          //第六关
        {
          //0是空的地图
          //1是板砖
          //3是目标点
          state:[
            [0,0,0,0,1,1,1,1,1,1,1,0],
            [0,0,0,0,1,0,0,1,0,0,1,0],
            [0,0,0,0,1,0,0,0,0,0,1,0],
            [1,1,1,1,1,0,0,1,0,0,1,0],
            [3,3,3,1,1,0,0,0,0,0,1,1],
            [3,0,0,1,0,0,0,0,1,0,0,1],
            [3,0,0,0,0,0,0,0,0,0,0,1],
            [3,0,0,1,0,0,0,0,1,0,0,1],
            [3,3,3,1,1,1,0,1,0,0,1,1],
            [1,1,1,1,1,0,0,0,0,0,1,0],
            [0,0,0,0,1,0,0,1,0,0,1,0],
            [0,0,0,0,1,1,1,1,1,1,1,0]
          ],
          person: {x : 5, y : 10},
          box: [
            {x:5, y:6},
            {x:6, y:3},
            {x:6, y:5},
            {x:6, y:7},
            {x:6, y:9},
            {x:7, y:2},
            {x:8, y:2},
            {x:9, y:6}
          ]
        }
      ],
      //map data
      mapData : (function() {
        var data = {};
        return {
          get: function () {
            return data;
          },
          set: function (arg) {
            data = arg;
          },
          //穿进来的数据在界面中是否存在;
          collision: function (x, y) {
            if( data.state[y][x] === 1)return true;
            return false;
          },
          collisionBox : function(x,y) {
            for(var i= 0, len= data.box.length; i< len; i++) {
              if( data.box[i].x === x&& data.box[i].y === y)return data.box[i];
            };
            return false;
          }
        }
      })(),
      view : {
        initMap : function(map) {
          document.getElementById("house").innerHTML = Handlebars.compile( document.getElementById("tpl").innerHTML )( map );
        },
        initPerson : function(personXY) {
          var per = document.createElement("div");
          per.id = "person";
          G.per = per;
          document.getElementById("house").appendChild(per);
          per.style.left = 50* personXY.x+"px";
          per.style.top = 50* personXY.y+"px";
        },
        initBox : function(boxs) {
          for(var i=0;i<boxs.length; i++) {
            var box = document.createElement("div");
            box.className = "box";
            G.box = box;
            document.getElementById("house").appendChild(box);
            box.style.left = boxs[i].x*50 + "px";
            box.style.top = boxs[i].y*50 + "px";
          };
        },
        deleteBox : function() {
          var eBoxs = document.getElementsByClassName("box");
          var len = eBoxs.length;
          while( len-- ) {
            eBoxs[len].parentNode.removeChild( eBoxs[len] );
          };
        }
      },
      /*
      * 0;向上
      * 1:向右
      * 2:向下
      * 3:向左
      * */
      direction : 0,
      step : function(xy) {
        //这里面要做很多判断
        /*包括:
         用户当前的方向和以前是否一样,如果不一样要先转头;
         如果一样的话,判断前面是否有石头, 是否有箱子;
           如果前面有墙壁或者
           前面有箱子,而且箱子前面有墙壁就return
         把人物往前移动
         如果人物的位置上有一个箱子,把箱子也移动一下;
         */
        var mapData = this.mapData.get();
        //对参数进行处理;
        if ( typeof xy === "string" ) {
          var x = 0, y = 0, xx = 0, yy = 0;
          switch( xy ) {
            case "left" :
                if(this.direction==0){
                  x = -1;
                  xx = -2;
                }else{
                  x = 0;
                };
              this.direction = 0;
              break;
            case "top" :
                if(this.direction===1){
                  y = -1;
                  yy = -2
                }else{
                  y = 0;
                };
                this.direction = 1;
              break;
            case "right" :
                if(this.direction === 2) {
                  x = 1;
                  xx = 2;
                }else{
                  x = 0;
                };
              this.direction = 2;
              break;
            case "bottom" :
                if(this.direction ===3 ) {
                  y = 1;
                  yy = 2;
                }else{
                  y = 0;
                };
              this.direction = 3;
          };
          //如果是墙壁就不能走
          if( this.mapData.collision(mapData.person.x + x, mapData.person.y+y) ) {
            return;
          };
          //如果碰到的是箱子, 而且箱子前面是墙壁, 就return
          if( this.mapData.collisionBox(mapData.person.x+x, mapData.person.y+y) && this.mapData.collision(mapData.person.x+xx, mapData.person.y+yy)) {
            return;
          };
          if( this.mapData.collisionBox(mapData.person.x+x, mapData.person.y+y) && this.mapData.collisionBox(mapData.person.x+xx, mapData.person.y+yy)) {
            return
          }
          //mapData.x+xx, mapData.y+yy
          mapData.person.x = mapData.person.x + x;
          mapData.person.y = mapData.person.y + y;

          this.per.style.left = 50* mapData.person.x+"px";
          this.per.style.top = 50* mapData.person.y+"px";
          this.per.className = {
            0:"up",
            1:"right",
            2:"bottom",
            3:"left"
          }[this.direction];
          var theBox = {};
          if(theBox = this.mapData.collisionBox(mapData.person.x, mapData.person.y)) {
            theBox.x = mapData.person.x+x;
            theBox.y = mapData.person.y+y;
            this.view.deleteBox();
            this.view.initBox(mapData.box);
            this.testSuccess();
          };
          //如果碰到了箱子,而且箱子前面不能走就return, 否则就走箱子和人物;
        };
      },
      /*
      * return Boolean;
      * */
      //遍历所有的box,如果在box中的所有x,y在地图中对应的值为3,全部通过就返回true
      testSuccess : function() {
        var mapData = this.mapData.get();
        for(var i=0; i<mapData.box.length; i++) {
          if(mapData.state[mapData.box[i].y][mapData.box[i].x] != 3) {
            return false;
          };
        };
        $.dialog({
          content : '游戏成功, 进入下一关!',
          title : 'alert',
          ok : function() {
            if( G.now+1 > G.level.length-1 ) {
              alert("闯关成功");
              return ;
            }else{
              //如果可用的等级大于当前的等级,就把level设置进去;
              if( G.now+1 > parseInt( $.cookie('level') || 0 )) {
                $.cookie('level' , G.now+1 , { expires: 7 });
              };
              start( G.now+1 );
              return ;
            };
          },
          cancel : function(){
            location.reload();
          },
          lock : true
        });
      },
      //这里面需要处理 map, 人物数据, box数据
      init : function() {
        //更新地图;
        //this.level[0].state
        this.view.initMap( this.mapData.get().state );
        this.view.initPerson( this.mapData.get().person );
        this.view.initBox( this.mapData.get().box );
        //this.person = this.factory.Person(0,0);
        //this.box = this.factory.Box([{x:0,y:1},{x:1,y:1},{x:0,y:2},{x:1,y:2}]);
        if( this.hasBind ) {
          return
        };
        this.hasBind = true;
        this.controller();
      },
      controller : function() {
        function mobileDOM() {
          var mobileDOMString = '\
            <div class="navbar-fixed-bottom height20percent operate-bar" >\
              <div class="container height100percent">\
                <div class="row text-center height100percent">\
                  <div class="height40percent arrow-up">\
                    <span class="glyphicon glyphicon-arrow-up" aria-hidden="true"></span>\
                  </div>\
                  <div class="height30percent">\
                    <div class="col-xs-6 arrow-left">\
                      <span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span>\
                    </div>\
                    <div class="col-xs-6 arrow-right">\
                      <span class="glyphicon glyphicon-arrow-right" aria-hidden="true"></span>\
                    </div>\
                  </div>\
                  <div class="height30percent arrow-down">\
                    <span class="glyphicon glyphicon-arrow-down" aria-hidden="true"></span>\
                  </div>\
                </div>\
              </div>\
            </div>\
            ';
            +function addDOM() {
              $("#game").append( mobileDOMString );
            }();
        };
        var _this = this;
        if( window.util.isMobile() ) {
          $(window).on("swipeLeft",function() {
            _this.step("left");
          }).on("swipeRight",function() {
            _this.step("right");
          }).on("swipeUp",function() {
            _this.step("top");
          }).on("swipeDown",function() {
            _this.step("bottom");
          });
          mobileDOM();

          $(".arrow-up").tap(function() {
            _this.step("top");
          });
          $(".arrow-down").tap(function() {
            _this.step("bottom");
          });
          $(".arrow-left").tap(function() {
            _this.step("left");
          });
          $(".arrow-right").tap(function() {
            _this.step("right");
          });
        }else{
          $(window).on("keydown", function(ev) {
            var state = "";
            switch( ev.keyCode ) {
              case 37 :
                state = "left";
              break;
              case 39 :
                state = "right";
              break;
              case 38 :
                state = "top";
              break;
              case 40 :
                state = "bottom";
              break;
            };
            _this.step(state)
          });
        };
      }
    };

    function start( level ) {
      G.now = level;
      G.mapData.set(G.level[level] );
      G.init();
      $("#game").show();
      $("#select").hide();
    };

    function init() {
      var cookieLevel = $.cookie('level') || 0;
      start( cookieLevel );
    };
    $("#start").click(function() {
      init();
    });
    String.prototype.repeat = String.prototype.repeat || function(num) {
      return (new Array(num+1)).join( this.toString() );
    };

    window.onload = function() {
      var cookieLevel = $.cookie('level') || 0;
      $("#level").html( function() {
        var index = 0;
        return "<a href='###' class='btn btn-info' onclick='start({{i}})'>关卡</a>    ".repeat((parseInt($.cookie('level')) || 0)+1).replace(/{{i}}/gi, function() {
          return index++;
        })
      });
    }
  </script>
</body>
</html>

游戏一共有6关, 每一关成功通过即可解锁下一关, 地图的话其实可以多找些的,哈哈;

推箱子游戏的在线DEMO : 打开

以上所述就是本文的全部内容了,希望大家能够喜欢。

Javascript 相关文章推荐
IE6下CSS图片缓存问题解决方法
Dec 09 Javascript
jQuery EasyUI API 中文文档 - TimeSpinner时间微调器
Oct 23 Javascript
Extjs4 Treegrid 使用心得分享(经验篇)
Jul 01 Javascript
深入理解JS中的变量及作用域、undefined与null
Mar 04 Javascript
php中给js数组赋值方法
Mar 10 Javascript
JS 在指定数组中随机取出N个不重复的数据
Jun 10 Javascript
使用 bootstrap modal遇到的问题小结
Nov 09 Javascript
微信小程序开发教程-手势解锁实例
Jan 06 Javascript
es6系列教程_ Map详解以及常用api介绍
Sep 25 Javascript
vue绑定事件后获取绑定事件中的this方法
Sep 15 Javascript
微信小程序模板template简单用法示例
Dec 04 Javascript
基于mpvue搭建微信小程序项目框架的教程详解
Apr 10 Javascript
使用JavaScript实现连续滚动字幕效果的方法
Jul 07 #Javascript
理解JavaScript的变量的入门教程
Jul 07 #Javascript
Javascript编写俄罗斯方块思路及实例
Jul 07 #Javascript
javascript实现控制div颜色
Jul 07 #Javascript
浅谈JavaScript中的字符编码转换问题
Jul 07 #Javascript
JavaScript中判断两个字符串是否相等的方法
Jul 07 #Javascript
javascript中数组方法汇总
Jul 07 #Javascript
You might like
example2.php
2006/10/09 PHP
PHP输出一个等腰三角形的方法
2015/05/12 PHP
php similar_text()函数的定义和用法
2016/05/12 PHP
Yii2主题(Theme)用法详解
2016/07/23 PHP
php多进程模拟并发事务产生的问题小结
2018/12/07 PHP
php中错误处理操作实例分析
2019/08/23 PHP
Thinkphp5框架实现图片、音频和视频文件的上传功能详解
2019/08/27 PHP
静态的动态续篇之来点XML
2006/12/23 Javascript
ImageFlow可鼠标控制图片滚动
2008/01/30 Javascript
Jquery 的扩展方法总结
2011/10/01 Javascript
jQuery中size()方法用法实例
2014/12/27 Javascript
jQuery模仿阿里云购买服务器选择购买时间长度的代码
2016/04/29 Javascript
BootStrap中的table实现数据填充与分页应用小结
2016/05/26 Javascript
浅谈javascript中的Function和Arguments
2016/08/30 Javascript
JavaScript-html标题滚动效果的简单实现
2016/09/08 Javascript
基于jQuery实现数字滚动效果
2017/01/16 Javascript
使用Vue自定义指令实现Select组件
2018/05/24 Javascript
微信小程序表单验证form提交错误提示效果
2020/06/19 Javascript
如何将Node.js中的回调转换为Promise
2020/11/10 Javascript
Python中的anydbm模版和shelve模版使用指南
2015/07/09 Python
学习python之编写简单乘法口诀表实现代码
2016/02/27 Python
python 计算文件的md5值实例
2017/01/13 Python
Python统计单词出现的次数
2018/04/04 Python
python实现从pdf文件中提取文本,并自动翻译的方法
2018/11/28 Python
python实现图片插入文字
2019/11/26 Python
解决Python import docx出错DLL load failed的问题
2020/02/13 Python
什么是python的自省
2020/06/21 Python
python matplotlib工具栏源码探析二之添加、删除内置工具项的案例
2021/02/25 Python
bonprix匈牙利:女士、男士和儿童服装
2019/07/19 全球购物
水电工岗位职责
2014/02/12 职场文书
十佳中学生事迹材料
2014/06/02 职场文书
鲁滨孙漂流记读书笔记
2015/06/30 职场文书
幼儿教师远程研修感悟
2015/11/18 职场文书
暑假开始了,你的暑假学习计划写好了吗?
2019/07/04 职场文书
奇妙的 CSS shapes(CSS图形)
2021/04/05 HTML / CSS
CSS实现章节添加自增序号的方法
2021/06/23 HTML / CSS