前端遊戲之初步掃雷
阿新 • • 發佈:2018-11-19
掃雷應該算是最基礎的js入門遊戲吧,所以剛學完js就來寫寫掃雷的程式碼,初步思路想了很久,得出一個不算正規的掃雷思路,先放下圖看看:
注:上圖中的字型顏色,是我為了檢視每個小方塊執行的是哪步的註釋,可忽略。
具體思路:
·········先用ul>li,做一個由9*9的小方格組成一個大方塊——>就是掃雷的介面,在li中寫入每個格子的內容(空,數字,炸彈),再將字型顏色和小方塊的背景顏色設定一致,使內容能夠象徵性的消失,然後再通過點選事件,改變所點選小方塊的字型和背景顏色,使其又“出現”,以此實現掃雷的效果。
········以上的表面程式碼都比較好實現,但是有一點需要花些功夫,就是當我所點選的小方格為空時,需要連線觸發周邊的內容,使空方格周圍的上下左右的內容都顯示出來,且又遇到為空的鄰近方格,需要再進行上下左右的顯示,接下來以此類推,得到最終的點選空方格觸發周圍一片的顯示效果。
下面介紹下具體程式碼,首先是掃雷介面的設定:
html程式碼:
<ul class="ul1"> <br/> <li></li> <li></li> <li></li> <li>1</li> <li>*</li> <li>1</li> <li></li> <li></li> <li></li><br/> <li></li> <li></li> <li>2</li> <li>2</li> <li>2</li> <li></li> <li></li> <li></li> <li></li><br/> <li>1</li> <li>1</li> <li>2</li> <li>*</li> <li>1</li> <li>1</li> <li>1</li> <li>1</li> <li></li><br/> <li>1</li> <li>*</li> <li>3</li> <li>2</li> <li>2</li> <li>1</li> <li>*</li> <li>1</li> <li></li><br/> <li>1</li> <li>1</li> <li>2</li> <li>*</li> <li>1</li> <li>1</li> <li>1</li> <li>1</li> <li></li><br/> <li></li> <li>1</li> <li>2</li> <li>2</li> <li>1</li> <li></li> <li></li> <li>1</li> <li>1</li><br/> <li>1</li> <li>2</li> <li>*</li> <li>1</li> <li></li> <li>1</li> <li>1</li> <li>2</li> <li>*</li><br/> <li>2</li> <li>*</li> <li>3</li> <li>1</li> <li></li> <li>1</li> <li>*</li> <li>2</li> <li>1</li><br/> <li>2</li> <li>*</li> <li>2</li> <li></li> <li></li> <li>1</li> <li>1</li> <li>1</li> <li></li><br/> </ul>
注:上述html是模仿電腦自帶掃雷遊戲的內容設定的,有想換內容的可以參考電腦自帶的掃雷遊戲,或者自己想也可以。
設定完html,還沒將大方格做出來,因此需要css的幫忙,
css程式碼:
ul{ list-style: none; /*取消ul原有的格式*/ position: absolute; /*設定在頁面中的絕對定位*/ left: 40px; top: 40px; } .ul1 li{ float: left; /*使li能夠橫著排成9個小方格*/ width: 20px; /*小格子的寬*/ height: 20px; /*小格子的高*/ border: 1px solid rgb(158, 157, 157); /*格子的邊框*/ background-color: rgb(209, 207, 207); /*格子的初始背景顏色*/ line-height: 20px; /*使內容居中對齊*/ text-align: center; /*使內容居中對齊*/ color: rgb(209, 207, 207); /*內容字型顏色*/ cursor: pointer; /*當滑鼠移入方格時,箭頭變成指示型別的小手*/ }
有了上述的css,使我們的掃雷介面基本完成,接下來就是內部邏輯的處理了,就需要用到js的內容。
以下是js的程式碼:
function retSibling(e,n){ //這是提取e元素的前n個或後n個的元素的封裝函式
while(e && n){
if(n > 0){
if(e.nextElementSibling){
e = e.nextElementSibling;
}
else{
for(e = e.nextSibling;e && e.nodeType != 1;e = e.nextSibling)
;
}
n --;
}
else
{
if(e.previousElementSibling){
e = e.previousElementSibling;
}
else{
for( e = e.previousSiling;e && e.nodeType != 1;e = e.previousSiling);
}
n ++;
}
}
return e;
}
function k1(target){ //由下面呼叫一個所觸發的target元素,檢視它前一個兄弟元素節點,檢視的內容有:1.是否為邊界節點(br/);2.內容是否為空;3.內容是否為*;4.內容是否為數字
if(target.previousElementSibling == "<br/>"){
}else{
if(target.previousElementSibling.innerText == ""){ //當為空時,首先改變該元素的字型和背景顏色,再將它的前一個節點元素重新賦給target,利用遞迴的方法重複呼叫k1檢查下一個節點元素的內容
target.previousElementSibling.style.color = "black";
target.previousElementSibling.style.backgroundColor = "#fff";
target = target.previousElementSibling;
k1(target);
k3(target); //將target提供給k3,k3是向上判斷的封裝函式
k4(target); //將target提供給k4,k4是向下判斷的封裝函式
}else if(target.previousElementSibling.innerText == "*"){ //為*時,表示炸彈,因此不會觸發任何內容
}else{ //去掉空和*,剩下的自然是數字,因此當是數字時,只需要顯示該數字就可以了
target.previousElementSibling.style.color = "black";
target.previousElementSibling.style.backgroundColor = "#fff";
}
}
}
function k2(target){ //檢視所給的target的後一個元素,與上述的向前(k1)的判斷類似,下面不再仔細描述
if(target.nextElementSibling == "<br/>"){
}else{
if(target.nextElementSibling.innerText == ""){
target.nextElementSibling.style.color = "green";
target.nextElementSibling.style.backgroundColor = "#fff";
target = target.nextElementSibling;
k2(target);
k3(target);
k4(target);
}else if(target.nextElementSibling.innerText == "*"){
}else{
target.nextElementSibling.style.color = "green";
target.nextElementSibling.style.backgroundColor = "#fff";
}
}
}
function k3(target){ //檢視target的上方元素,利用9*9的特性,該節點元素的上方元素應該是該節點的前9個,不過由於在html中採用<br/>來做為轉折方法,所以該元素的上方元素節點應該是前10個所在的位置。
try{ //這裡會用到try,是因為實在想不出什麼好的方法可以限制當沒有前10個元素節點時不操作的方法,所以用try投機取巧的方法,綜合了下錯誤,當然錯誤仍然還在,只是不影響接下來的操作。
var key = true;
if(key == true){
target = retSibling(target,-10); //呼叫retSibling函式,將target和-10(-10表示前10個元素)提供給retSibling函式,使其返回該元素的第前10個的元素節點給target,替換原有的target所表示的元素節點
if(target.innerText == ""){
target.style.color = "red";
target.style.backgroundColor = "#fff";
k3(target);
k5(target); //注意:這裡的k5與k1的內容基本相似,卻不能改成k1,否則程式碼會蹦
k6(target); //注意:這裡的k6與k2的內容基本相似,卻不能改成k1,否則程式碼會蹦
}else if(target.innerText == "*"){
}else{
target.style.color = "red";
target.style.backgroundColor = "#fff";
k1(target);
k2(target);
}
}
}catch{
key = false;
}
}
function k4(target){ //與上述的k3內容基本一致,所不同的是他表示的是下方的元素節點
try{
var key = true;
if(key == true){
target = retSibling(target,10);
if(target.innerText == ""){
target.style.color = "blue";
target.style.backgroundColor = "#fff";
k4(target);
k5(target); //注意:這裡的k5與k1的內容基本相似,卻不能改成k1,否則程式碼會蹦
k6(target); //注意:這裡的k6與k2的內容基本相似,卻不能改成k1,否則程式碼會蹦
}else if(target.innerText == "*"){
}else{
target.style.color = "blue";
target.style.backgroundColor = "#fff";
k1(target);
k2(target);
}
}
}catch{
key = false;
}
}
function k5(target){ //額外多出來表示一行的左邊元素節點判斷,但很關鍵,與k1基本一致,只是缺少遞迴。
if(target.previousElementSibling == "<br/>"){
}else{
if(target.previousElementSibling.innerText == ""){
target.previousElementSibling.style.color = "black";
target.previousElementSibling.style.backgroundColor = "#fff";
target = target.previousElementSibling;
k5(target);
}else if(target.previousElementSibling.innerText == "*"){
}else{
target.previousElementSibling.style.color = "black";
target.previousElementSibling.style.backgroundColor = "#fff";
}
}
}
function k6(target){ //額外多出來表示一行的右邊元素節點判斷,但很關鍵與k2基本一致,只是缺少遞迴。
if(target.nextElementSibling == "<br/>"){
}else{
if(target.nextElementSibling.innerText == ""){
target.nextElementSibling.style.color = "green";
target.nextElementSibling.style.backgroundColor = "#fff";
target = target.nextElementSibling;
k6(target);
}else if(target.nextElementSibling.innerText == "*"){
}else{
target.nextElementSibling.style.color = "green";
target.nextElementSibling.style.backgroundColor = "#fff";
}
}
}
var ul = document.getElementsByTagName('ul')[0]; //將ul選擇出來
ul.onclick = function(e){ //對ul進行點選事件
var event = e||window.event;
var target = event.target||event.srcElement;
target.style.color = "black";
target.style.backgroundColor = "#fff";
if(target.innerText == "*"){ //為*(炸彈)時,表示遊戲結束
alert("Boom!!!");
}else if(target.innerText == ""){
// 以下k1,k2,k3,k4表示呼叫上述的封裝函式,具體作用上述已解釋
k1(target);
k2(target);
k3(target);
k4(target);
}
}
到此,基本的掃雷功能已經完全實現,該程式碼中的最大缺陷在於,用了遞迴的方法,使得當觸發聯動性比較大的空方格時,較容易發生程式碼崩潰的現象,所以對於剛開始在li中所寫內容需要注意,儘量使方格空空的連性少些。
**求助:**不知有哪位大佬能夠幫助解決上述的缺陷或者指出程式碼中有哪些不合理性,歡迎留下您留下寶貴的建議,謝謝!