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.                     if( this.mapData.collision(mapData.person.x + x, mapData.person.y+y) ) { 
  413.                         return; 
  414.                     }; 
  415.                     //如果碰到的是箱子, 而且箱子前面是墙壁, 就return 
  416.                     if( this.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.                     if( this.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.                 if( this.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关, 每一关成功通过即可解锁下一关, 地图的话其实可以多找些的,哈哈;

作者:方方和圆圆

来源:51CTO

时间: 2024-10-28 23:36:56

JavaScript写一个小乌龟推箱子游戏的相关文章

JavaScript编写推箱子游戏_javascript技巧

推箱子游戏是老游戏了, 网上有各种各样的版本, 说下推箱子游戏的简单实现,以及我找到的一些参考视频和实例: 如下是效果图: 这个拖箱子游戏做了移动端的适配, 我使用了zepto的touch模块, 通过手指滑动屏幕就可以控制乌龟走不同的方向: 因为推箱子这个游戏比较简单, 直接用了过程式的方式写代码, 模块也就是两个View 和 Model, 剩下就是用户的事件Controller, 用户每一次按下键盘的方向键都会改变数据模型的数据,然后重新生成游戏的静态html, 然后用innerHTML方式插

J2ME推箱子游戏

问题描述 我有一个想法想做一个推箱子游戏.游戏内容与现存的不一样,有没有人可以帮我做成这个游戏啊~~.我想做一个自动生成地图的游戏~~.我对游戏分析还可以--,就是对编程实在是不在行.希望高手们可以帮个忙--有意者请给我发邮件dingmingquan521@hotmail.com 解决方案 解决方案二: 解决方案三:这位大大的意识我不太懂!1:自动生成地图是随机的吗?2:还是写好很多地图后自动抽取一个地图来进行游戏?如果是1的话难度就高很多了因为自动生成地图来推箱子逻辑部分就...一个不小心生成

求助推箱子游戏用到的图片资源

问题描述 我想用java写一个推箱子游戏,但找不到需要用的图片资源,望各位大大求助 解决方案 解决方案二:去截图...然后使用...满足你的需求么........解决方案三:可以找网页版的推箱子,截图p下也可以

vs5015-求大神vs2015 环境下纯c语言编程推箱子游戏,QQ或微信红包重谢

问题描述 求大神vs2015 环境下纯c语言编程推箱子游戏,QQ或微信红包重谢 本人c语言初学者,求推向资源代码.我已写了部分,但不会将其联系起来,将其显示出来,刷屏出来...就是将地图呈现出.希望看到大神的源代码有所感悟 #ifndef _DITEM #define _DITEM #define X 8 #define Y 8 enum Kind{nul=0,wall,human,box,well,finish,empty,qiu}; typedef struct _item { enum K

C#编推箱子游戏的一些问题

问题描述 1,如何用Keydown键控制小人的上下左右行走,坐标是如何计算的?2.怎么控制小人和箱子坐标重合后,箱子的移动. 解决方案 解决方案二:坐等来人.....坐标应该可以通过数组解决吧?人和箱子重合的时候判断哪一边...然后....不懂了--解决方案三:"坐标"有两个含义,一个是编程世界的坐标,一个是应用领域的坐标.应用领域的坐标其x.y可能就是从1到5,表示最多有5行.5列.而编程领域的坐标,则可能是像素坐标系的坐标(例如x.y都是从0到800或者从0到任意n数值之间映射).

C语言实现的推箱子游戏

/* 这是彭搏同学的推箱子游戏,大家试试玩,谁有更好的Idea?*/ #include"stdio.h" #include"bios.h" #define LEFT 75 #define RIGHT 77 #define UPPER 72 #define DOWN 80 #define ESC 27 struct Boxss /*定义箱子结构体,其中包含坐标属性*/ { int x,y; }; union keyboard /*定义读取键盘码的共用体类型*/ { u

前端-javascript写一个九宫格运行出错

问题描述 javascript写一个九宫格运行出错 用循环判断胜负的时候在win[h].length处报错,Uncaught TypeError: Cannot read property 'length' of undefined,请高手解答一下,感激不尽. 解决方案 开发"> 应该是这里写错了吧 解决方案二: length是数组类型的属性,你的元素类型不确定时直接用就会报错. 修正内层for 循环j访问数组元素的代码如下:在for循环中 var var arr_i=new Array(

Cocos2D iOS之旅:如何写一个敲地鼠游戏(一):高清屏显示和UIKit

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流之用,请勿进行商业用途.同时,转载时不要移除本申明.如产生任何纠纷,均与本博客所有人.发表该翻译稿之人无任何关系.谢谢合作! 原文由Ray Wunderlich写成,地址在: http://www.raywenderlich.com/2560/cocos2d-tutorial-for-ios-how

Cocos2D iOS之旅:如何写一个敲地鼠游戏(六):放置地鼠

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流之用,请勿进行商业用途.同时,转载时不要移除本申明.如产生任何纠纷,均与本博客所有人.发表该翻译稿之人无任何关系.谢谢合作! 放置地鼠 对于这个游戏,你将要在场景中添加3只地鼠 - 每个洞一只.地鼠一般躲在草丛中的洞里 - 但会偶尔弹出来,所以你可以狠K它们喽. 首先,让我们在每个洞里添加一只地鼠.