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 相关文章推荐
window.dialogArguments 使用说明
Apr 11 Javascript
JavaScript 盒模型 尺寸深入理解
Dec 31 Javascript
js去空格技巧分别去字符串前后、左右空格
Oct 21 Javascript
让html页面不缓存js的实现方法
Oct 31 Javascript
jquery实现左右滑动菜单效果代码
Aug 27 Javascript
AngularJS Module方法详解
Dec 08 Javascript
JavaScript计算值然后把值嵌入到html中的实现方法
Oct 29 Javascript
JavaScript实现弹出广告功能
Mar 30 Javascript
详解微信小程序审核不通过的解决方法
Jan 17 Javascript
详解node child_process模块学习笔记
Jan 24 Javascript
js 图片转base64的方式(两种)
Apr 24 Javascript
在vue中使用公共过滤器filter的方法
Jun 26 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
smarty静态实验表明,网络上是错的~呵呵
2006/11/25 PHP
php str_pad 函数使用详解
2009/01/13 PHP
腾讯QQ php程序员面试题目整理
2010/06/08 PHP
PHP中引用类型和值类型功能与用法示例
2019/02/26 PHP
jQuery 1.2.x 升? 1.3.x 注意事项
2009/05/06 Javascript
jQuery html()等方法介绍
2009/11/18 Javascript
jquery入门—访问DOM对象方法
2013/01/07 Javascript
js读取配置文件自写
2014/02/11 Javascript
js用typeof方法判断undefined类型
2014/07/15 Javascript
Javascript的表单验证-提交表单
2016/03/18 Javascript
Javascript 5种方法实现过滤删除前后所有空格
2016/06/22 Javascript
Bootstrap选项卡与Masonry插件的完美结合
2016/07/06 Javascript
jQuery实现的选择商品飞入文本框动画效果完整实例
2016/08/10 Javascript
利用jQuery插件imgAreaSelect实现图片上传裁剪(放大缩小)
2016/12/02 Javascript
微信公众号开发 实现点击返回按钮就返回到聊天界面
2016/12/15 Javascript
JavaScript数组和对象的复制
2017/03/21 Javascript
深入浅析JavaScript中的RegExp对象
2017/09/18 Javascript
使用vux实现上拉刷新功能遇到的坑
2018/02/08 Javascript
uni-app微信小程序登录并使用vuex存储登录状态的思路详解
2019/11/04 Javascript
python切换hosts文件代码示例
2013/12/31 Python
pycharm中连接mysql数据库的步骤详解
2017/05/02 Python
python中requests和https使用简单示例
2018/01/18 Python
Python产生Gnuplot绘图数据的方法
2018/11/09 Python
python生成器推导式用法简单示例
2019/10/08 Python
python标准库OS模块详解
2020/03/10 Python
django 数据库返回queryset实现封装为字典
2020/05/19 Python
关于python scrapy中添加cookie踩坑记录
2020/11/17 Python
英国100%防污和防水的靴子:Muck Boot Company
2020/09/08 全球购物
一套软件测试笔试题
2014/07/25 面试题
2014年学前班工作总结
2014/12/08 职场文书
培训简讯范文
2015/07/20 职场文书
2016小学新学期寄语
2015/12/04 职场文书
导游词之香港-太平山顶
2019/10/18 职场文书
话题作文之财富(600字)
2019/12/03 职场文书
python如何做代码性能分析
2021/04/26 Python
html+css实现文字折叠特效实例
2021/06/02 HTML / CSS