jQuery版推箱子游戏详解和源码

时间:2024-03-10 09:25:22

前言

偶然间看到很多用js写游戏的感觉很炫酷的样子,所以就想试试,就看了一些资料和某前端站点的视屏。于是乎就自己动手实践了一下,上推箱子截图

pushbox

感觉很丑陋,但是功能是实现了。再说貌似大多都是这样的吧,这一关其实还是有点难度的,我做完之后想检测一下下一关正确么,居然玩了20分钟才通关。

如果你看到这张图让你想起了你童年的回忆,说明你老了,这里可以试玩一下(很遗憾没有链接地址,最后又源码可以下载)。

css布局

主要考虑的是地图是怎么动态生成的,地图中有灰色的,还有墙,箱子,蓝色,红色背景,人物。先看css代码吧

 

* {
            padding: 0;
            margin: 0;
        }

        img {
            border: 0;
        }

        #container {
            position: relative;
            margin: 20px auto;
        }

        .pos1 {
            width: 50px;
            height: 50px;
            float: left;
            background: #666;
        }

        .pos2 {
            width: 50px;
            height: 50px;
            float: left;
            background: url(images/wall.png);
        }

        .pos3 {
            width: 50px;
            height: 50px;
            float: left;
            background: red;
        }

        .pos0 {
            width: 50px;
            height: 50px;
            float: left;
            background: blue;
        }

        .box {
            width: 50px;
            height: 50px;
            position: absolute;
            background: url(images/box.png);
        }

        .person {
            width: 50px;
            height: 50px;
            position: absolute;
            background: url(images/person.png);
        }

代码中的pos0/pos1/pos2/pos3/主要是墙,箱子,蓝色红色背景的样式,其中person和box就是人物和箱子的样式,

这里用样式下标来节省部分js代码

其次body中html布局,这里就很简单了,就是一个带id的div,其余的内容均动态生成,因为每个关卡的地图数据都是不一样的。

js代码部分

 

  1 $(function () {
  2             Game.init($("#container"));//初始化容器
  3         });
  4         var Game = {
  5             gk: [{//关卡
  6                 map: [//地图数据 按照坐标呈现的数组格式
  7                 1, 1, 2, 2, 2, 2, 1, 1,
  8                 1, 1, 2, 3, 3, 2, 1, 1,
  9                 1, 2, 2, 0, 3, 2, 2, 1,
 10                 1, 2, 0, 0, 0, 3, 2, 1,
 11                 2, 2, 0, 0, 0, 0, 2, 2,
 12                 2, 0, 0, 2, 0, 0, 0, 2,
 13                 2, 0, 0, 0, 0, 0, 0, 2,
 14                 2, 2, 2, 2, 2, 2, 2, 2
 15                 ],
 16                 box: [//箱子 坐标点对象
 17                     { x: 4, y: 3 },
 18                     { x: 3, y: 4 },
 19                     { x: 4, y: 5 },
 20                     { x: 5, y: 5 }
 21                 ],
 22                 person: { x: 3, y: 6 }//人物 坐标点对象
 23             },
 24             {
 25                 map: [
 26                 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1,
 27                 1, 1, 1, 1, 2, 0, 2, 2, 0, 0, 2, 1,
 28                 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 2, 1,
 29                 2, 2, 2, 2, 0, 0, 2, 0, 0, 0, 2, 1,
 30                 3, 3, 3, 2, 0, 0, 0, 0, 0, 0, 2, 2,
 31                 3, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2,
 32                 3, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 2,
 33                 3, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2,
 34                 3, 3, 3, 2, 2, 2, 0, 0, 2, 0, 0, 2,
 35                 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 1,
 36                 1, 1, 1, 1, 2, 0, 0, 2, 0, 0, 2, 1,
 37                 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1,
 38                 ],
 39                 box: [
 40                     { x: 8, y: 3 },
 41                     { x: 9, y: 3 },
 42                     { x: 7, y: 4 },
 43                     { x: 6, y: 7 },
 44                     { x: 7, y: 5 },
 45                     { x: 7, y: 8 },
 46                     { x: 8, y: 9 },
 47                     { x: 4, y: 5 },
 48                     { x: 6, y: 6 }
 49 
 50                 ],
 51                 person: { x: 3, y: 6 }
 52             }
 53             ],
 54             init: function (oParent) {
 55                 this.oParent = oParent;//此处将外层的对象引进来 
 56                 this.iNow = 0;
 57                 this.createMap(this.iNow);
 58             },
 59             createMap: function (iNow) {
 60                 //创建地图 很关键的是 将元素的样式下标和地图的坐标点关联
 61  this.oParent.empty(); document.title = "第" + (iNow + 1) + "关";
 62                 this.newJson = this.gk[iNow];
 63 
 64                 this.oParent.css("width", Math.sqrt(this.newJson.map.length) * 50);
 65                 var tempHtml = \'\';
 66                 $.each(this.newJson.map, $.proxy(function (i, elem) {
 67                     tempHtml += \'<div class="pos\' + elem + \'"></div>\';
 68                 }, this));
 69                 this.oParent.append(tempHtml);
 70                 this.createBox();
 71                 this.createPerson();
 72             },
 73             createBox: function () {//布局箱子所在的位置
 74                 $.each(this.newJson.box, $.proxy(function (i, elem) {
 75                     var oBox = $(\'<div class="box"></div>\');
 76                     oBox.css({ \'left\': elem.x * 50, \'top\': elem.y * 50 });
 77                     this.oParent.append(oBox);
 78                 }, this));
 79             },
 80             createPerson: function () {//布局人物所在的位置
 81                 var oPerson = $(\'<div class="person"></div>\');
 82                 var pos = this.newJson.person;
 83                 oPerson.css({ \'left\': pos.x * 50, \'top\': pos.y * 50 });
 84                 oPerson.data(\'x\', pos.x);//缓存在oPerson上的数据
 85                 oPerson.data(\'y\', pos.y);
 86                 this.oParent.append(oPerson);
 87                 this.bindPerson(oPerson);
 88             },
 89             bindPerson: function (oPerson) {//绑定对人物←↑→↓操作
 90                 $(document).keydown($.proxy(function (ev) {
 91                     switch (ev.which) {
 92                         case 37: //
 93                             oPerson.css(\'backgroundPosition\', \'-150px 0\');
 94                             this.movePerson(oPerson, { x: -1 });
 95                             break;
 96                         case 38: //
 97                             oPerson.css("backgroundPosition", "0 0");
 98                             this.movePerson(oPerson, { y: -1 });
 99                             break;
100                         case 39: //
101                             oPerson.css("backgroundPosition", "-50px 0");
102                             this.movePerson(oPerson, { x: 1 });
103                             break;
104                         case 40: //
105                             oPerson.css("backgroundPosition", "100px 0");
106                             this.movePerson(oPerson, { y: 1 });
107                             break;
108                         default:
109                     }
110                 }, this));
111             },
112             movePerson: function (oP, opt) {//移动人物
113                 var xValue = opt.x || 0;
114                 var yValue = opt.y || 0;
115                 var length = Math.sqrt(this.newJson.map.length);
116                 var currentMapIndex = (oP.data(\'x\') + xValue) + (oP.data(\'y\') + yValue) * length;
117                 if (this.newJson.map[currentMapIndex] != 2) {//遇到墙的判断
118                     oP.data(\'x\', oP.data(\'x\') + xValue);
119                     oP.data(\'y\', oP.data(\'y\') + yValue);
120                     oP.css({ "left": oP.data("x") * 50, "top": oP.data("y") * 50 });
121                     $(".box").each($.proxy(function (i, elem) {
122                         //当和箱子发生碰撞时遇到墙的判断
123                         if (this.pz(oP, $(elem)) && this.newJson.map[(oP.data(\'x\') + xValue) + (oP.data(\'y\') + yValue) * length] != 2) {
124                             $(elem).css({ \'left\': (oP.data(\'x\') + xValue) * 50, \'top\': (oP.data(\'y\') + yValue) * 50 });
125                             $(".box").each($.proxy(function (j, elem2) {
126                                 if (this.pz($(elem), $(elem2)) && elem != elem2) {
127                                     //当遇到箱子和箱子的的碰撞时同时前面也不是强的判断
128                                     $(elem).css({ \'left\': oP.data(\'x\') * 50, \'top\': oP.data(\'y\') * 50 });
129                                     oP.data(\'x\', oP.data(\'x\') - xValue);
130                                     oP.data(\'y\', oP.data(\'y\') - yValue);
131                                     oP.css({ "left": oP.data("x") * 50, "top": oP.data("y") * 50 });
132                                 }
133                             }, this));
134                         }
135                         else if (this.pz(oP, $(elem))) {//和墙之间的碰撞
136                             oP.data(\'x\', oP.data(\'x\') - xValue);
137                             oP.data(\'y\', oP.data(\'y\') - yValue);
138                             oP.css({ "left": oP.data("x") * 50, "top": oP.data("y") * 50 });
139                         }
140                     }, this));
141                 }
142                 this.nextShow();
143             },
144             nextShow: function () {//判断是否赢
145                 var iNum = 0;
146                 //红色区域所在的位置是否全部被箱子所占据
147                 $(".box").each($.proxy(function (i, elem) {
148                     $(".pos3").each($.proxy(function (j, elem1) {
149                         if (this.pz($(elem), $(elem1))) {
150                             iNum++;
151                         }
152                     }, this));
153                 }, this));
154                 if (iNum == this.newJson.box.length) {
155                     this.iNow++;
156                     this.createMap(this.iNow);
157                 }
158             },
159             pz: function (obj1, obj2) { //碰撞检测
160                 var L1 = obj1.offset().left;
161                 var R1 = obj1.offset().left + obj1.width();
162                 var T1 = obj1.offset().top;
163                 var B1 = obj1.offset().top + obj1.height();
164 
165                 var L2 = obj2.offset().left;
166                 var R2 = obj2.offset().left + obj2.width();
167                 var T2 = obj2.offset().top;
168                 var B2 = obj2.offset().top + obj2.height();
169                 if (L1 >= R2 || B2 <= T1 || T2 >= B1 || R1 <= L2)
170                 { return false; }
171                 else
172                 { return true; }
173             }
174         };


基本代码中都有注释,应该是可以理解的,具体如果你有疑问那么请留言,我定会回复。

我个人认为其中的精华部分就是首先地图数据的构造用一维数组来确定地图坐标,其中的内容的数据和样式中pos的下标的数据对应起来感觉很赞。

其次是逻辑判断,比如当人物推箱子是发现前面是墙,推箱子遇到箱子时前面也是箱子,此时如果又遇到了墙怎么处理。最后判断输赢就是如果红色区域的位置全部被箱子所占据那么也就

表示通过,进入下一关,当然下一关的数据我是自己随意填充的。如果你有兴趣请自行解决。

结论

这种类似的小游戏重在思路,如果复杂的话就要考虑架构性能等问题了,我猜的。因为没有做大的游戏,如有错误请指出。如果你觉得不错就支持推荐一下。

download PushBox