前言
偶然間看到很多用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的下標的數據對應起來感覺很贊。
其次是邏輯判斷,比如當人物推箱子是發現前面是牆,推箱子遇到箱子時前面也是箱子,此時如果又遇到了牆怎么處理。最后判斷輸贏就是如果紅色區域的位置全部被箱子所占據那么也就
表示通過,進入下一關,當然下一關的數據我是自己隨意填充的。如果你有興趣請自行解決。
結論
這種類似的小游戲重在思路,如果復雜的話就要考慮架構性能等問題了,我猜的。因為沒有做大的游戲,如有錯誤請指出。如果你覺得不錯就支持推薦一下。