JavaScript写一个小乌龟推箱子游戏

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

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

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

如下是效果图:

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

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

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

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

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

  3. 小乌龟的数据,json对象

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

  1. level: [                {                    //0是空的地图 
  2.                     //1是板砖 
  3.                     //3是目标点 
  4.                     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}]                },                //第二关 
  5.                 {                    //0是空的地图 
  6.                     //1是板砖 
  7.                     //3是目标点 
  8.                     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}                    ]                    */                },                //第三关 
  9.                 {                    //0是空的地图 
  10.                     //1是板砖 
  11.                     //3是目标点 
  12.                     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}]                },                //第四关 
  13.                 {                    //0是空的地图 
  14.                     //1是板砖 
  15.                     //3是目标点 
  16.                     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}]                },                //第五关 
  17.                 {                    //0是空的地图 
  18.                     //1是板砖 
  19.                     //3是目标点 
  20.                     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}                     ]                     */                },                    //第六关 
  21.                 {                    //0是空的地图 
  22.                     //1是板砖 
  23.                     //3是目标点 
  24.                     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)打开, 模板内容:

  1. <script id="tpl" type="text/x-handlebars-template"
  2.         {{#initY}}{{/initY}} 
  3.         {{#each this}} 
  4.             {{#each this}} 
  5.                 <div class="{{#getClass this}}{{/getClass}}" data-x="{{@index}}" data-y="{{#getY}}{{/getY}}" style="left:{{#calc @index}}{{/calc}};top:{{#calc 1111}}{{/calc}}"
  6.                     <!--{{@index}} 
  7.                     {{#getY}}{{/getY}} 
  8.                     --> 
  9.                 </div> 
  10.             {{/each}} 
  11.             {{#addY}}{{/addY}} 
  12.         {{/each}} 
  13.     </script> 

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

  1. (function() { 
  2.             var y = 0
  3.             Handlebars.registerHelper("initY", function() { 
  4.                 y = 0
  5.             }); 
  6.             Handlebars.registerHelper("addY", function() { 
  7.                 y++; 
  8.             }); 
  9.             Handlebars.registerHelper("getY", function() { 
  10.                 return y; 
  11.             }); 
  12.             Handlebars.registerHelper("calc", function(arg) { 
  13.                 //console.log(arg) 
  14.                 if(arg!==1111) { 
  15.                     return 50*arg + "px"
  16.                 }else
  17.                     return 50*y + "px"
  18.                 }; 
  19.             }); 
  20.             Handlebars.registerHelper("getClass", function(arg) { 
  21.                 switch( arg ) { 
  22.                     case 0 : 
  23.                         return "bg" 
  24.                     case 1 : 
  25.                         return "block" 
  26.                     case 2 : 
  27.                         return "box" 
  28.                     case 3 : 
  29.                         return "target" 
  30.                 }; 
  31.             }); 
  32.             window.util = { 
  33.                 isMobile : function() { 
  34.                     return navigator.userAgent.toLowerCase().indexOf("mobile") !== -1 || navigator.userAgent.toLowerCase().indexOf("android") !== -1  || navigator.userAgent.toLowerCase().indexOf("pad") !== -1
  35.                 } 
  36.             } 
  37.         })(); 

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

  1. if( window.util.isMobile() ) { 
  2.                     $(window).on("swipeLeft",function() { 
  3.                         _this.step("left"); 
  4.                     }).on("swipeRight",function() { 
  5.                         _this.step("right"); 
  6.                     }).on("swipeUp",function() { 
  7.                         _this.step("top"); 
  8.                     }).on("swipeDown",function() { 
  9.                         _this.step("bottom"); 
  10.                     }); 
  11.                     mobileDOM(); 
  12.  
  13.                     $(".arrow-up").tap(function() { 
  14.                         _this.step("top"); 
  15.                     }); 
  16.                     $(".arrow-down").tap(function() { 
  17.                         _this.step("bottom"); 
  18.                     }); 
  19.                     $(".arrow-left").tap(function() { 
  20.                         _this.step("left"); 
  21.                     }); 
  22.                     $(".arrow-right").tap(function() { 
  23.                         _this.step("right"); 
  24.                     }); 
  25.                 }else
  26.                     $(window).on("keydown", function(ev) { 
  27.                         var state = ""
  28.                         switch( ev.keyCode ) { 
  29.                             case 37 : 
  30.                                 state = "left"
  31.                             break
  32.                             case 39 : 
  33.                                 state = "right"
  34.                             break
  35.                             case 38 : 
  36.                                 state = "top"
  37.                             break
  38.                             case 40 : 
  39.                                 state = "bottom"
  40.                             break
  41.                         }; 
  42.                         _this.step(state) 
  43.                     }); 
  44.                 }; 

 

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

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

所有的代码在这里:

  1. <!DOCTYPE html> 
  2. <html> 
  3. <head lang="en"
  4.     <meta charset="UTF-8"
  5.     <title></title> 
  6.     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
  7.     <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css"
  8.     <link rel="stylesheet" href="http://sqqihao.github.io/games/rusBlock/libs/Tiny-Alert/css/zepto.alert.css"/> 
  9.     <script src="libs/jquery-1.9.1.min.js"></script> 
  10.     <script src="libs/handlebars.js"></script> 
  11.     <script src="libs/jquery-cookie.js"></script> 
  12.     <script src="http://sqqihao.github.io/games/rusBlock/libs/Tiny-Alert/js/zepto.alert.js"></script> 
  13.     <script id="tpl" type="text/x-handlebars-template"
  14.         {{#initY}}{{/initY}} 
  15.         {{#each this}} 
  16.             {{#each this}} 
  17.                 <div class="{{#getClass this}}{{/getClass}}" data-x="{{@index}}" data-y="{{#getY}}{{/getY}}" style="left:{{#calc @index}}{{/calc}};top:{{#calc 1111}}{{/calc}}"
  18.                     <!--{{@index}} 
  19.                     {{#getY}}{{/getY}} 
  20.                     --> 
  21.                 </div> 
  22.             {{/each}} 
  23.             {{#addY}}{{/addY}} 
  24.         {{/each}} 
  25.     </script> 
  26.     <script> 
  27.         (function() { 
  28.             var y = 0
  29.             Handlebars.registerHelper("initY", function() { 
  30.                 y = 0
  31.             }); 
  32.             Handlebars.registerHelper("addY", function() { 
  33.                 y++; 
  34.             }); 
  35.             Handlebars.registerHelper("getY", function() { 
  36.                 return y; 
  37.             }); 
  38.             Handlebars.registerHelper("calc", function(arg) { 
  39.                 //console.log(arg) 
  40.                 if(arg!==1111) { 
  41.                     return 50*arg + "px"
  42.                 }else
  43.                     return 50*y + "px"
  44.                 }; 
  45.             }); 
  46.             Handlebars.registerHelper("getClass", function(arg) { 
  47.                 switch( arg ) { 
  48.                     case 0 : 
  49.                         return "bg" 
  50.                     case 1 : 
  51.                         return "block" 
  52.                     case 2 : 
  53.                         return "box" 
  54.                     case 3 : 
  55.                         return "target" 
  56.                 }; 
  57.             }); 
  58.             window.util = { 
  59.                 isMobile : function() { 
  60.                     return navigator.userAgent.toLowerCase().indexOf("mobile") !== -1 || navigator.userAgent.toLowerCase().indexOf("android") !== -1  || navigator.userAgent.toLowerCase().indexOf("pad") !== -1
  61.                 } 
  62.             } 
  63.         })(); 
  64.     </script> 
  65. </head> 
  66. <style> 
  67.     #game{ 
  68.         display: none; 
  69.     } 
  70.     #house{ 
  71.         position: relative; 
  72.     } 
  73.     .bg{ 
  74.         position: absolute; 
  75.         width:50px; 
  76.         height:50px; 
  77.         box-sizing: border-box; 
  78.     } 
  79.     .block{ 
  80.         position: absolute; 
  81.         background-image: url(imgs/wall.png); 
  82.         width:50px; 
  83.         height:50px; 
  84.         box-sizing: border-box; 
  85.     } 
  86.     .box{ 
  87.         position: absolute; 
  88.         background: #fbd500; 
  89.         width:50px; 
  90.         height:50px; 
  91.         background-image: url(imgs/box.png); 
  92.     } 
  93.     .target{ 
  94.         position: absolute; 
  95.         background: url(imgs/target.jpg); 
  96.         background-size: 50px 50px;; 
  97.         width:50px; 
  98.         height:50px; 
  99.         box-sizing: border-box; 
  100.     } 
  101.     #person{ 
  102.         background-image: url(imgs/person.png); 
  103.         width:50px; 
  104.         height:50px; 
  105.         position: absolute; 
  106.     } 
  107.     #person.up{ 
  108.         background-position: 0 0
  109.     } 
  110.     #person.right{ 
  111.         background-position:-50px  0 ; 
  112.     } 
  113.     #person.bottom{ 
  114.         background-position:-100px 0 ; 
  115.     } 
  116.     #person.left{ 
  117.         background-position:-150px 0 ; 
  118.     } 
  119.     /*移动端的DOM*/ 
  120.     .operate-bar{ 
  121.         font-size:30px; 
  122.     } 
  123.     .height20percent{ 
  124.         height:30%; 
  125.     } 
  126.     .height30percent{ 
  127.         height:30%; 
  128.     } 
  129.     .height40percent{ 
  130.         height:40%; 
  131.     } 
  132.     .height100percent{ 
  133.         height:100%; 
  134.     } 
  135.     .font30{ 
  136.         font-size:30px; 
  137.         color:#34495e; 
  138.     } 
  139. </style> 
  140. <body> 
  141.     <div id="select"
  142.         <div class="container"
  143.             <div class="row"
  144.                 <p class="text-info"
  145.                     已经解锁的关卡: 
  146.                 <p id="level"
  147.                 </p> 
  148.                 </p> 
  149.                 <button id="start" class="btn btn-default"
  150.                     开始游戏 
  151.                 </button> 
  152.             </div> 
  153.         </div> 
  154.     </div> 
  155.     <div id="game" class="container"
  156.         <div class="row"
  157.             <button onclick="location.reload()" class="btn btn-info" > 
  158.                 返回选择关卡重新 
  159.             </button> 
  160.             <div id="house"
  161.             </div> 
  162.         </div> 
  163.     </div> 
  164.  
  165.     <script> 
  166.         G = { 
  167.             level: [ 
  168.                 { 
  169.                     //0是空的地图 
  170.                     //1是板砖 
  171.                     //3是目标点 
  172.                     state:[ 
  173.                         [0,0,1,1,1,0,0,0,0], 
  174.                         [0,1,1,3,3,1,0,0,0], 
  175.                         [0,1,0,0,0,0,1,0,0], 
  176.                         [0,1,0,0,0,0,1,0,0], 
  177.                         [0,1,1,1,1,1,1,0,0
  178.                     ], 
  179.                     person: {x : 2, y : 2}, 
  180.                     box: [{x:3, y : 2},{x:4,y:2}] 
  181.                 }, 
  182.                 //第二关 
  183.                 { 
  184.                     //0是空的地图 
  185.                     //1是板砖 
  186.                     //3是目标点 
  187.                     state:[ 
  188.                         [0,1,1,1,1,1,0,0], 
  189.                         [0,1,0,0,1,1,1,0], 
  190.                         [0,1,0,0,0,0,1,0], 
  191.                         [1,1,1,0,1,0,1,1], 
  192.                         [1,3,1,0,1,0,0,1], 
  193.                         [1,3,0,0,0,1,0,1], 
  194.                         [1,3,0,0,0,0,0,1], 
  195.                         [1,1,1,1,1,1,1,1
  196.                     ], 
  197.                     person: {x : 2, y : 2}, 
  198.                     box: [{x:3, y : 2}, {x:2,y:5} ,{x:5, y:6}] 
  199.                     /* 
  200.                     box : [ 
  201.                         {x:3, y : 1}, 
  202.                         {x:4, y : 1}, 
  203.                         {x:4, y : 2}, 
  204.                         {x:5, y : 5} 
  205.                     ] 
  206.                     */ 
  207.                 }, 
  208.                 //第三关 
  209.                 { 
  210.                     //0是空的地图 
  211.                     //1是板砖 
  212.                     //3是目标点 
  213.                     state:[ 
  214.                         [0,0,0,1,1,1,1,1,1,0], 
  215.                         [0,1,1,1,0,0,0,0,1,0], 
  216.                         [1,1,3,0,0,1,1,0,1,1], 
  217.                         [1,3,3,0,0,0,0,0,0,1], 
  218.                         [1,3,3,0,0,0,0,0,1,1], 
  219.                         [1,1,1,1,1,1,0,0,1,0], 
  220.                         [0,0,0,0,0,1,1,1,1,0
  221.                     ], 
  222.                     person: {x : 8, y : 3}, 
  223.                     box: [{x:4, y : 2}, {x:3,y:3} ,{x:4, y:4},{x:5, y:3},{x:6, y:4}] 
  224.                 }, 
  225.                 //第四关 
  226.                 { 
  227.                     //0是空的地图 
  228.                     //1是板砖 
  229.                     //3是目标点 
  230.                     state:[ 
  231.                         [0,1,1,1,1,1,1,1,0,0], 
  232.                         [0,1,0,0,0,0,0,1,1,1], 
  233.                         [1,1,0,1,1,1,0,0,0,1], 
  234.                         [1,0,0,0,0,0,0,0,0,1], 
  235.                         [1,0,3,3,1,0,0,0,1,1], 
  236.                         [1,1,3,3,1,0,0,0,1,0], 
  237.                         [0,1,1,1,1,1,1,1,1,0
  238.                     ], 
  239.                     person: {x : 2, y : 3}, 
  240.                     box: [{x:2, y : 2}, {x:4,y:3} ,{x:6, y:4},{x:7, y:3},{x:6, y:4}] 
  241.                 }, 
  242.                 //第五关 
  243.                 { 
  244.                     //0是空的地图 
  245.                     //1是板砖 
  246.                     //3是目标点 
  247.                     state:[ 
  248.                         [0,0,1,1,1,1,0,0], 
  249.                         [0,0,1,3,3,1,0,0], 
  250.                         [0,1,1,0,3,1,1,0], 
  251.                         [0,1,0,0,0,3,1,0], 
  252.                         [1,1,0,0,0,0,1,1], 
  253.                         [1,0,0,1,0,0,0,1], 
  254.                         [1,0,0,0,0,0,0,1], 
  255.                         [1,1,1,1,1,1,1,1
  256.                     ], 
  257.                     person: {x : 4, y : 6}, 
  258.                     box: [{x:4, y : 3}, {x:3,y:4} ,{x:4, y:5}, {x:5,y:5}] 
  259.                     /* 
  260.                      box : [ 
  261.                      {x:3, y : 1}, 
  262.                      {x:4, y : 1}, 
  263.                      {x:4, y : 2}, 
  264.                      {x:5, y : 5} 
  265.                      ] 
  266.                      */ 
  267.                 }, 
  268.                     //第六关 
  269.                 { 
  270.                     //0是空的地图 
  271.                     //1是板砖 
  272.                     //3是目标点 
  273.                     state:[ 
  274.                         [0,0,0,0,1,1,1,1,1,1,1,0], 
  275.                         [0,0,0,0,1,0,0,1,0,0,1,0], 
  276.                         [0,0,0,0,1,0,0,0,0,0,1,0], 
  277.                         [1,1,1,1,1,0,0,1,0,0,1,0], 
  278.                         [3,3,3,1,1,0,0,0,0,0,1,1], 
  279.                         [3,0,0,1,0,0,0,0,1,0,0,1], 
  280.                         [3,0,0,0,0,0,0,0,0,0,0,1], 
  281.                         [3,0,0,1,0,0,0,0,1,0,0,1], 
  282.                         [3,3,3,1,1,1,0,1,0,0,1,1], 
  283.                         [1,1,1,1,1,0,0,0,0,0,1,0], 
  284.                         [0,0,0,0,1,0,0,1,0,0,1,0], 
  285.                         [0,0,0,0,1,1,1,1,1,1,1,0
  286.                     ], 
  287.                     person: {x : 5, y : 10}, 
  288.                     box: [ 
  289.                         {x:5,  y:6}, 
  290.                         {x:6,  y:3}, 
  291.                         {x:6,  y:5}, 
  292.                         {x:6,  y:7}, 
  293.                         {x:6,  y:9}, 
  294.                         {x:7,  y:2}, 
  295.                         {x:8,  y:2}, 
  296.                         {x:9,  y:6
  297.                     ] 
  298.                 } 
  299.             ], 
  300.             //map data 
  301.             mapData : (function() { 
  302.                 var data = {}; 
  303.                 return { 
  304.                     get: function () { 
  305.                         return data; 
  306.                     }, 
  307.                     set: function (arg) { 
  308.                         data = arg; 
  309.                     }, 
  310.                     //穿进来的数据在界面中是否存在; 
  311.                     collision: function (x, y) { 
  312.                         if( data.state[y][x] === 1)return true
  313.                         return false
  314.                     }, 
  315.                     collisionBox : function(x,y) { 
  316.                         for(var i= 0, len= data.box.length; i< len; i++) { 
  317.                             if( data.box[i].x === x&& data.box[i].y === y)return data.box[i]; 
  318.                         }; 
  319.                         return false
  320.                     } 
  321.                 } 
  322.             })(), 
  323.             view : { 
  324.                 initMap : function(map) { 
  325.                     document.getElementById("house").innerHTML = Handlebars.compile( document.getElementById("tpl").innerHTML )( map ); 
  326.                 }, 
  327.                 initPerson : function(personXY) { 
  328.                     var per = document.createElement("div"); 
  329.                     per.id = "person"
  330.                     G.per = per; 
  331.                     document.getElementById("house").appendChild(per); 
  332.                     per.style.left = 50* personXY.x+"px"
  333.                     per.style.top = 50* personXY.y+"px"
  334.                 }, 
  335.                 initBox : function(boxs) { 
  336.                     for(var i=0;i<boxs.length; i++) { 
  337.                         var box = document.createElement("div"); 
  338.                         box.className = "box"
  339.                         G.box = box; 
  340.                         document.getElementById("house").appendChild(box); 
  341.                         box.style.left = boxs[i].x*50 + "px"
  342.                         box.style.top = boxs[i].y*50 + "px"
  343.                     }; 
  344.                 }, 
  345.                 deleteBox : function() { 
  346.                     var eBoxs = document.getElementsByClassName("box"); 
  347.                     var len = eBoxs.length; 
  348.                     while( len-- ) { 
  349.                         eBoxs[len].parentNode.removeChild( eBoxs[len] ); 
  350.                     }; 
  351.                 } 
  352.             }, 
  353.             /* 
  354.             * 0;向上 
  355.             * 1:向右 
  356.             * 2:向下 
  357.             * 3:向左 
  358.             * */ 
  359.             direction : 0
  360.             step : function(xy) { 
  361.                 //这里面要做很多判断 
  362.                 /*包括: 
  363.                  用户当前的方向和以前是否一样,如果不一样要先转头; 
  364.                  如果一样的话,判断前面是否有石头, 是否有箱子; 
  365.                      如果前面有墙壁或者 
  366.                      前面有箱子,而且箱子前面有墙壁就return 
  367.                  把人物往前移动 
  368.                  如果人物的位置上有一个箱子,把箱子也移动一下; 
  369.                  */ 
  370.                 var mapData = this.mapData.get(); 
  371.                 //对参数进行处理; 
  372.                 if ( typeof xy === "string" ) { 
  373.                     var x = 0, y = 0, xx = 0, yy = 0
  374.                     switch( xy ) { 
  375.                         case "left" : 
  376.                                 if(this.direction==0){ 
  377.                                     x = -1
  378.                                     xx = -2
  379.                                 }else
  380.                                     x = 0
  381.                                 }; 
  382.                             this.direction = 0
  383.                             break
  384.                         case "top" : 
  385.                                 if(this.direction===1){ 
  386.                                     y = -1
  387.                                     yy = -2 
  388.                                 }else
  389.                                     y = 0
  390.                                 }; 
  391.                                 this.direction = 1
  392.                             break
  393.                         case "right" : 
  394.                                 if(this.direction === 2) { 
  395.                                     x = 1
  396.                                     xx = 2
  397.                                 }else
  398.                                     x = 0
  399.                                 }; 
  400.                             this.direction = 2
  401.                             break
  402.                         case "bottom" : 
  403.                                 if(this.direction ===3 ) { 
  404.                                     y = 1
  405.                                     yy = 2
  406.                                 }else
  407.                                     y = 0
  408.                                 }; 
  409.                             this.direction = 3
  410.                     }; 
  411.                     //如果是墙壁就不能走 
  412.                     ifthis.mapData.collision(mapData.person.x + x, mapData.person.y+y) ) { 
  413.                         return
  414.                     }; 
  415.                     //如果碰到的是箱子, 而且箱子前面是墙壁, 就return 
  416.                     ifthis.mapData.collisionBox(mapData.person.x+x, mapData.person.y+y) &&  this.mapData.collision(mapData.person.x+xx, mapData.person.y+yy)) { 
  417.                         return
  418.                     }; 
  419.                     ifthis.mapData.collisionBox(mapData.person.x+x, mapData.person.y+y) &&  this.mapData.collisionBox(mapData.person.x+xx, mapData.person.y+yy)) { 
  420.                         return 
  421.                     } 
  422.                     //mapData.x+xx, mapData.y+yy 
  423.                     mapData.person.x = mapData.person.x + x; 
  424.                     mapData.person.y = mapData.person.y + y; 
  425.  
  426.                     this.per.style.left = 50* mapData.person.x+"px"
  427.                     this.per.style.top = 50* mapData.person.y+"px"
  428.                     this.per.className = { 
  429.                         0:"up"
  430.                         1:"right"
  431.                         2:"bottom"
  432.                         3:"left" 
  433.                     }[this.direction]; 
  434.                     var theBox = {}; 
  435.                     if(theBox = this.mapData.collisionBox(mapData.person.x, mapData.person.y)) { 
  436.                         theBox.x = mapData.person.x+x; 
  437.                         theBox.y = mapData.person.y+y; 
  438.                         this.view.deleteBox(); 
  439.                         this.view.initBox(mapData.box); 
  440.                         this.testSuccess(); 
  441.                     }; 
  442.                     //如果碰到了箱子,而且箱子前面不能走就return, 否则就走箱子和人物; 
  443.                 }; 
  444.             }, 
  445.             /* 
  446.             * return Boolean; 
  447.             * */ 
  448.             //遍历所有的box,如果在box中的所有x,y在地图中对应的值为3,全部通过就返回true 
  449.             testSuccess : function() { 
  450.                 var mapData = this.mapData.get(); 
  451.                 for(var i=0; i<mapData.box.length; i++) { 
  452.                     if(mapData.state[mapData.box[i].y][mapData.box[i].x] != 3) { 
  453.                         return false
  454.                     }; 
  455.                 }; 
  456.                 $.dialog({ 
  457.                     content : '游戏成功, 进入下一关!'
  458.                     title : 'alert'
  459.                     ok : function() { 
  460.                         if( G.now+1 > G.level.length-1 ) { 
  461.                             alert("闯关成功"); 
  462.                             return ; 
  463.                         }else
  464.                             //如果可用的等级大于当前的等级,就把level设置进去; 
  465.                             if( G.now+1 > parseInt( $.cookie('level') || 0 )) { 
  466.                                 $.cookie('level' , G.now+1 , { expires: 7 }); 
  467.                             }; 
  468.                             start( G.now+1 ); 
  469.                             return ; 
  470.                         }; 
  471.                     }, 
  472.                     cancel : function(){ 
  473.                         location.reload(); 
  474.                     }, 
  475.                     lock : true 
  476.                 }); 
  477.             }, 
  478.             //这里面需要处理 map, 人物数据, box数据 
  479.             init : function() { 
  480.                 //更新地图; 
  481.                 //this.level[0].state 
  482.                 this.view.initMap( this.mapData.get().state  ); 
  483.                 this.view.initPerson( this.mapData.get().person ); 
  484.                 this.view.initBox( this.mapData.get().box ); 
  485.                 //this.person = this.factory.Person(0,0); 
  486.                 //this.box = this.factory.Box([{x:0,y:1},{x:1,y:1},{x:0,y:2},{x:1,y:2}]); 
  487.                 ifthis.hasBind ) { 
  488.                     return 
  489.                 }; 
  490.                 this.hasBind = true
  491.                 this.controller(); 
  492.             }, 
  493.             controller : function() { 
  494.                 function mobileDOM() { 
  495.                     var mobileDOMString = '\ 
  496.                         <div class="navbar-fixed-bottom height20percent operate-bar"  >\ 
  497.                             <div class="container height100percent">\ 
  498.                                 <div class="row text-center height100percent">\ 
  499.                                     <div class="height40percent arrow-up">\ 
  500.                                         <span class="glyphicon glyphicon-arrow-up" aria-hidden="true"></span>\ 
  501.                                     </div>\ 
  502.                                     <div  class="height30percent">\ 
  503.                                         <div class="col-xs-6 arrow-left">\ 
  504.                                             <span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span>\ 
  505.                                         </div>\ 
  506.                                         <div class="col-xs-6 arrow-right">\ 
  507.                                             <span class="glyphicon glyphicon-arrow-right" aria-hidden="true"></span>\ 
  508.                                         </div>\ 
  509.                                     </div>\ 
  510.                                     <div  class="height30percent arrow-down">\ 
  511.                                         <span class="glyphicon glyphicon-arrow-down" aria-hidden="true"></span>\ 
  512.                                     </div>\ 
  513.                                 </div>\ 
  514.                             </div>\ 
  515.                         </div>\ 
  516.                         '; 
  517.                         +function addDOM() { 
  518.                             $("#game").append( mobileDOMString ); 
  519.                         }(); 
  520.                 }; 
  521.                 var _this = this
  522.                 if( window.util.isMobile() ) { 
  523.                     $(window).on("swipeLeft",function() { 
  524.                         _this.step("left"); 
  525.                     }).on("swipeRight",function() { 
  526.                         _this.step("right"); 
  527.                     }).on("swipeUp",function() { 
  528.                         _this.step("top"); 
  529.                     }).on("swipeDown",function() { 
  530.                         _this.step("bottom"); 
  531.                     }); 
  532.                     mobileDOM(); 
  533.  
  534.                     $(".arrow-up").tap(function() { 
  535.                         _this.step("top"); 
  536.                     }); 
  537.                     $(".arrow-down").tap(function() { 
  538.                         _this.step("bottom"); 
  539.                     }); 
  540.                     $(".arrow-left").tap(function() { 
  541.                         _this.step("left"); 
  542.                     }); 
  543.                     $(".arrow-right").tap(function() { 
  544.                         _this.step("right"); 
  545.                     }); 
  546.                 }else
  547.                     $(window).on("keydown", function(ev) { 
  548.                         var state = ""
  549.                         switch( ev.keyCode ) { 
  550.                             case 37 : 
  551.                                 state = "left"
  552.                             break
  553.                             case 39 : 
  554.                                 state = "right"
  555.                             break
  556.                             case 38 : 
  557.                                 state = "top"
  558.                             break
  559.                             case 40 : 
  560.                                 state = "bottom"
  561.                             break
  562.                         }; 
  563.                         _this.step(state) 
  564.                     }); 
  565.                 }; 
  566.             } 
  567.         }; 
  568.  
  569.         function start( level ) { 
  570.             G.now = level; 
  571.             G.mapData.set(G.level[level] ); 
  572.             G.init(); 
  573.             $("#game").show(); 
  574.             $("#select").hide(); 
  575.         }; 
  576.  
  577.         function init() { 
  578.             var cookieLevel = $.cookie('level') || 0
  579.             start( cookieLevel ); 
  580.         }; 
  581.         $("#start").click(function() { 
  582.             init(); 
  583.         }); 
  584.         String.prototype.repeat = String.prototype.repeat || function(num) { 
  585.             return  (new Array(num+1)).join( this.toString() ); 
  586.         }; 
  587.  
  588.         window.onload = function() { 
  589.             var cookieLevel = $.cookie('level') || 0
  590.             $("#level").html( function() { 
  591.                 var index = 0
  592.                return "<a href='###' class='btn btn-info' onclick='start({{i}})'>关卡</a>&nbsp;&nbsp;&nbsp;&nbsp;".repeat((parseInt($.cookie('level')) || 0)+1).replace(/{{i}}/gi, function() { 
  593.                    return index++; 
  594.                }) 
  595.             }); 
  596.         } 
  597.     </script> 
  598. </body> 
  599. </html> 

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

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

参考:

别人写的推箱子:打开

妙味的杜鹏老师的推箱子视频:打开

 
责任编辑:王雪燕 来源: 博客园
相关推荐

2012-01-04 13:55:23

Canvas

2023-01-03 15:16:27

2022-12-27 14:39:38

2011-11-03 09:13:27

JavaScript

2011-10-21 09:10:12

JavaScript

2014-02-14 09:37:01

JavascriptDOM

2021-02-05 16:03:48

JavaScript游戏学习前端

2020-11-30 06:20:13

javascript

2020-09-26 21:50:26

JavaScript代码开发

2021-09-08 08:36:50

ncursesLinux猜谜游戏

2015-06-02 04:13:23

Python乒乓球类游戏

2024-04-09 14:27:39

2020-08-12 22:03:17

JavaScript开发技术

2023-11-29 08:10:36

javascriptH5游戏

2022-08-10 18:14:49

国际象棋游戏位字段C语言

2020-05-06 10:10:06

JavaScript 3D 游戏

2016-11-29 13:31:52

JavaScriptsetTimeout定时执行

2022-02-10 07:41:02

JavaScriponce函数

2018-08-27 11:40:59

小程序

2022-12-28 14:07:52

Gamerzilla开源游戏
点赞
收藏

51CTO技术栈公众号