前言
偶然间看到很多用js写游戏的感觉很炫酷的样子,所以就想试试,就看了一些资料和某前端站点的视屏。于是乎就自己动手实践了一下,上推箱子截图
感觉很丑陋,但是功能是实现了。再说貌似大多都是这样的吧,这一关其实还是有点难度的,我做完之后想检测一下下一关正确么,居然玩了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的下标的数据对应起来感觉很赞。
其次是逻辑判断,比如当人物推箱子是发现前面是墙,推箱子遇到箱子时前面也是箱子,此时如果又遇到了墙怎么处理。最后判断输赢就是如果红色区域的位置全部被箱子所占据那么也就
表示通过,进入下一关,当然下一关的数据我是自己随意填充的。如果你有兴趣请自行解决。
结论
这种类似的小游戏重在思路,如果复杂的话就要考虑架构性能等问题了,我猜的。因为没有做大的游戏,如有错误请指出。如果你觉得不错就支持推荐一下。