search
HomeWeb Front-endJS TutorialJavaScript writing pushing box game_javascript skills

The push box game is an old game. There are various versions on the Internet. Let me talk about the simple implementation of the push box game, as well as some reference videos and examples I found;

The following is the rendering:

This box dragging game has been adapted to the mobile terminal. I used zepto’s touch module to control the turtle to go in different directions by sliding the screen with your finger;

Because the Sokoban game is relatively simple, the code is written directly in a procedural way. The module is two Views and Model, and the rest is the user's event Controller. Every time the user presses the direction key on the keyboard, the data will be changed. Model data, then regenerate the static HTML of the game, and then insert it into the interface using innerHTML to automatically generate DOM nodes;

The level model of the game is data. I divided the data of each level into three pieces:

Map data, two-dimensional array (map data includes tiles, target location of the box, and blank location)
Box data, one-dimensional array (initial position of the box)
Data of little turtle, json object
Each level has corresponding game level data. The simulated data is as follows:

      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}
          ]
        }
      ]

One very important thing is the main logic of the push box game: because the place where the little turtle can only walk is a blank area, and if there is a wall in front of the turtle, it cannot walk, or if there is a box in front of the turtle, then judge the front of the box. Is there a wall? If there is no wall, both the turtle and the box can move forward. If there is a wall, they cannot move. Every time the little turtle walks away, the map data is changed, and then the interface is regenerated. In this cycle, after every little turtle walks, it will check whether the box data in the map data are all aligned. If they are aligned, the user will be prompted and entered. Next level;

The game’s template engine uses handlebarsJS. You can go to the official website to see the API. This is a blog I wrote, Handlebars usage documentation (Handlebars.js): Open, template content:

  <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>

Several helpers are defined for Handlebars, including initY, getClass, getY, calc,,,,. The template engine is mainly used as an auxiliary function. It is not very wise to use Handlebars here. The readability of the code becomes a bit worse. , closures are also used to save variables to avoid pollution of global variables:

    (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;
        }
      }
    })();

Because it needs to be compatible with mobile devices, we need to check whether it is a mobile phone or tablet. If so, I will add the corresponding DOM element (direction key DOM element), and then bind the corresponding event. zeptoJS provides the touch module, we Go to the official website to find it, then add additional references, open the address, and then you can use the swipeLeft, swipeUp, swipeDown, swipeRight events:

        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)
          });
        };

Because we need to save the user’s current level, we also use the jQuery-cookies plug-in. Every time a level is successfully cleared, we will save the current level record. When the user does not want to play or closes the browser for other reasons, the browser will be closed after a few days. You can continue playing when you want to play again;

            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 ;
            };

All the codes are here:

<!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>

There are 6 levels in the game. Successfully passing each level will unlock the next level. You can actually find more maps, haha;

Online DEMO of Sokoban game: Open

The above is the entire content of this article, I hope you all like it.

Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
es6数组怎么去掉重复并且重新排序es6数组怎么去掉重复并且重新排序May 05, 2022 pm 07:08 PM

去掉重复并排序的方法:1、使用“Array.from(new Set(arr))”或者“[…new Set(arr)]”语句,去掉数组中的重复元素,返回去重后的新数组;2、利用sort()对去重数组进行排序,语法“去重数组.sort()”。

JavaScript的Symbol类型、隐藏属性及全局注册表详解JavaScript的Symbol类型、隐藏属性及全局注册表详解Jun 02, 2022 am 11:50 AM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于Symbol类型、隐藏属性及全局注册表的相关问题,包括了Symbol类型的描述、Symbol不会隐式转字符串等问题,下面一起来看一下,希望对大家有帮助。

原来利用纯CSS也能实现文字轮播与图片轮播!原来利用纯CSS也能实现文字轮播与图片轮播!Jun 10, 2022 pm 01:00 PM

怎么制作文字轮播与图片轮播?大家第一想到的是不是利用js,其实利用纯CSS也能实现文字轮播与图片轮播,下面来看看实现方法,希望对大家有所帮助!

JavaScript对象的构造函数和new操作符(实例详解)JavaScript对象的构造函数和new操作符(实例详解)May 10, 2022 pm 06:16 PM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于对象的构造函数和new操作符,构造函数是所有对象的成员方法中,最早被调用的那个,下面一起来看一下吧,希望对大家有帮助。

JavaScript面向对象详细解析之属性描述符JavaScript面向对象详细解析之属性描述符May 27, 2022 pm 05:29 PM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于面向对象的相关问题,包括了属性描述符、数据描述符、存取描述符等等内容,下面一起来看一下,希望对大家有帮助。

javascript怎么移除元素点击事件javascript怎么移除元素点击事件Apr 11, 2022 pm 04:51 PM

方法:1、利用“点击元素对象.unbind("click");”方法,该方法可以移除被选元素的事件处理程序;2、利用“点击元素对象.off("click");”方法,该方法可以移除通过on()方法添加的事件处理程序。

整理总结JavaScript常见的BOM操作整理总结JavaScript常见的BOM操作Jun 01, 2022 am 11:43 AM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于BOM操作的相关问题,包括了window对象的常见事件、JavaScript执行机制等等相关内容,下面一起来看一下,希望对大家有帮助。

foreach是es6里的吗foreach是es6里的吗May 05, 2022 pm 05:59 PM

foreach不是es6的方法。foreach是es3中一个遍历数组的方法,可以调用数组的每个元素,并将元素传给回调函数进行处理,语法“array.forEach(function(当前元素,索引,数组){...})”;该方法不处理空数组。

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Tools

mPDF

mPDF

mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

MantisBT

MantisBT

Mantis is an easy-to-deploy web-based defect tracking tool designed to aid in product defect tracking. It requires PHP, MySQL and a web server. Check out our demo and hosting services.

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

Integrate Eclipse with SAP NetWeaver application server.

Atom editor mac version download

Atom editor mac version download

The most popular open source editor

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

This project is in the process of being migrated to osdn.net/projects/mingw, you can continue to follow us there. MinGW: A native Windows port of the GNU Compiler Collection (GCC), freely distributable import libraries and header files for building native Windows applications; includes extensions to the MSVC runtime to support C99 functionality. All MinGW software can run on 64-bit Windows platforms.