貪吃蛇思路指迷

網站: 碼路在線

微信:c91374286javascript

不少人學習編程,居然未曾本身動手寫太小遊戲。css

這或多或少,在編程生涯中,應該是缺乏了一些樂趣的。
而沒有動手寫過的人,無外乎這麼幾種。
1,不屑。老子很牛逼,這種小兒科,浪費時間。
2,不會。思前想後,撾耳撓腮,無處下手。
3,不肯。我只對錢感興趣,對寫小遊戲,沒興趣。
不知道該如何評判,也不想評判,每一個人都有本身的世界觀,人生觀,價值觀。
那麼,對於那些有興趣,卻又無處下手的同窗,本文將一步一步,還原小遊戲《貪吃蛇》的思考過程和開發原理。

畫一個框

一個複雜的東西,分解成N個簡單的步驟,個個擊破,這就是編程之道。
因此,第一步,咱們要幹嗎呢?畫一個框。
玩過貪吃蛇的都知道,貪吃蛇實際上是在一個正方形或者長方形範圍內移動的。
因此,第一步,先把這個活動範圍定好。

那麼這裏呢,咱們給這個活動範圍起一個名字,叫作,snake-grassland(蛇之草地,蛇在草地上偷蛋吃)。

/* 先重置一下 */
* {
    padding: 0;
    border: 0;
    margin: 0;
    outline: 0;
    box-sizing: border-box;
}
/* 蛇之草地 - 樣式*/
.snake-grassland {
    width: 300px; /* 寬度 */
    height: 300px; /* 高度 */
    background-color: #52af4a; /* 綠色的草地 */
}
    複製代碼

<!-- 蛇之草地 - 結構 -->
<div class="snake-grassland"></div>複製代碼


我相信,不少人寫貪吃蛇的時候,腦殼裏冒出了無數的想法。
蛇長什麼樣?
蛇蛋長什麼樣?
蛇活動的區域怎麼設計好看?
我該用div浮動,仍是position定位?
怎麼去計算是否碰到了牆壁?
蛇吃到蛋以後,怎麼增長蛇的身體長度?
怎麼控制蛇的自動移動?
怎麼控制蛇的方向?
等等...等等...不一而足。
這裏,我告訴你,這些東西,不要去想!不要去想!不要去想!
由於我寫這個教程,確定是寫給不會或者沒有思路的同窗看的。
因此,這類同窗有什麼特色呢?
他們對於編程的思惟觀念,編程技巧,眼界閱歷,都處於積累的初期。
一會兒N個問題冒出來須要處理,是很難理清楚邏輯和規律的,而後得出解決方法的。
因此,與其同時思考N個問題,不如先集中注意力,解決第一個小問題。
後面還有問題怎麼辦呢?到了那步再說唄。正所謂,今朝有酒今朝醉,明日愁來明日愁。
管它三七二十一,解決不了的問題放後面,先把能解決的解決掉。
這讓我想起了讀初中的時候,數學解答題不會寫怎麼辦?先寫個「解:」唄,得2分再說!

畫格子

一間教室,60張椅子,那就只能坐60我的。一人一張,互不干涉,公平公正。
一家四口,那吃飯就是四雙碗筷,多了一雙,那必定是隔壁老王。
因此,第二步,應該要畫好格子。
畫好格子幹嗎?放蛇蛋嘛!
作個荷包蛋,總不能直接打開了煤氣竈就打蛋吧,確定得先放鍋對不對。

/* 先重置一下 */
* {
    padding: 0;
    border: 0;
    margin: 0;
    outline: 0;
    box-sizing: border-box;
}
/* 蛇之草地 - 樣式*/
.snake-grassland {
    width: 300px; /* 寬度 */
    height: 300px; /* 高度 */
    background-color: #52af4a; /* 綠色的草地 */
}
/* 行樣式 */
.snake-grassland .line{
    height:10%;
}
/* 草地格子 - 樣式 */
.snake-grassland .cell{
    width: 10%; /* 寬度佔10分之1 */
    height: 100%; /* 高度佔100分之100的行元素高度 */
    float: left; /* 每一個格子左浮動 */
    border-right: 1px solid #42a03a; /* 格子邊框 */
    border-bottom: 1px solid #42a03a; /* 格子邊框 */
}複製代碼

<!-- 格子結構 - 一共10行,代碼太多,只放2行 --> <div class="snake-grassland"> <!-- 第1行 --> <div class="line"> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> </div> <!-- 第2行 --> <div class="line"> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> </div> <!-- 第3行 --> ... </div>複製代碼


這裏,有幾個值得思考的地方。
1.爲何要用line規定一行,直接全部cell格子所有浮動不也能畫一樣的格子布局嗎?
2.爲何行高和格子的寬高用百分比,不用像素呢?
3.爲何格子用float定位?不是說這個容易引發BUG,不推薦使用嗎?並且你使用了,也沒清除浮動呢。
4.爲何不用絕對定位佈局呢?這樣放蛇蛋的時候,不是更容易用z-index設置層次嗎?
5.爲何使用div做爲格子的元素,而不用span呢?
下面,來回答一下以上,不過,但願同窗們看到上述問題的時候,能夠先自行思考一下。
事情的發生,總會有它的科學道理。否則,就是迷信,就是玄學。(雖然CSS自己就是玄學。。。)
回答1:寫代碼,也要合乎常理,這是一個二維平面遊戲,就跟教室裏面的桌子排佈一個道理。總不能100張桌子排成一行,而後浮動,一個挨着一個吧。這樣雖然也能達到一樣的效果,可是它們的本質仍是二維和一維的區別,後面蛇的移動,增長身體長度,是很差計算的。因此,用行和列,第幾行,第幾列來定位後面的蛇蛋和蛇,是比較合理的。
回答2:爲何用百分比不用像素?使用像素,須要先知道草地寬度,再除以10,而後獲得一個小數,再寫上這個數值。百分比不須要知道草地寬度,不須要計算,節省時間。
回答3:爲何用float(浮動)佈局。由於,簡單。爲何不用清除浮動?草地已經固定了是300px * 300px,裏面的格子浮動,不會對草地形成影響,不須要清除浮動。
回答4:爲何格子不用絕對定位?絕對定位須要計算每一個格子距離上方的距離,左方的距離。同窗,100個格子,你去計算試試。用浮動不須要計算,節省時間。
回答5:爲何格子用div來佈局,不用span?我的愛好,你想用其餘的也行,反正都是盒子。睡牀上和地上有啥區別嗎?其實區別不大,都能睡。
因此,綜上所述。咱們在寫代碼,開發項目的時候,要學會偷懶,使用能節省時間的技術,去寫代碼,去開發項目。由於,時間是最寶貴的。

畫一個蛋

沒錯,終於要畫蛋了。
到了這一步,我相信,確定會有部分同窗,開始糾結。
蛋怎麼畫?
放哪?
怎麼放?
被蛇吃了怎麼放新蛋?
新蛋怎麼放?
新蛋放哪?
好了,不用糾結了。
大道至簡,請記住前面說的,用最節省時間的方法畫一個最簡單的蛋!
剩下的問題,不要去思考,那是個無底洞。

/* 先重置一下 */
* {
    padding: 0;
    border: 0;
    margin: 0;
    outline: 0;
    box-sizing: border-box;
}
/* 蛇之草地 - 樣式*/
.snake-grassland {
    width: 300px; /* 寬度 */
    height: 300px; /* 高度 */
    background-color: #52af4a; /* 綠色的草地 */
}
 /* 行樣式 */
.snake-grassland .line{
    height:10%;
}
 /* 草地格子 - 樣式 */
.snake-grassland .cell{
    width: 10%; /* 寬度佔10分之1 */
    height: 100%; /* 高度佔10分之1 */
    float: left; /* 每一個格子左浮動 */
    border-right: 1px solid #42a03a; /* 格子邊框 */
    border-bottom: 1px solid #42a03a; /* 格子邊框 */
}
 /* 那一個蛋 - 樣式 */
.snake-grassland .egg{
    width: 80%; /* 蛋的寬度 */
    height: 80%; /* 蛋的高度 */
    margin: 10%; /* 蛋的外間距,讓蛋居中 */
    border-radius: 50%; /* 讓蛋變圓,畢竟,沒見過正方形的蛋 */
    background-color: #ffc107; /* 讓蛋變黃,固然,你要個紅蛋,綠蛋,也行 */
}
            複製代碼

 <!-- html - 結構 --> <div class="snake-grassland"> <!-- 第1行 --> <div class="line"> <div class="cell"> <div class="egg"></div> </div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> <div class="cell"></div> </div> <!-- 第2行 --> ... </div>複製代碼


很簡單,直接在第一個格子放了一個黃黃的蛋。
雖然這一步很簡單,可是它仍是有深入的含義在裏面的。
1.它結束的放蛋糾結症,對吧。沒什麼好糾結的,就放第一個格子。
2.它讓咱們很直觀地看到,蛋放到格子中,是什麼樣子的。因此,咱們接下來寫代碼,不須要再關心蛋的樣式問題了。

隨機放蛋

通過上一步,咱們放好了一個蛋。可是,咱們總不能一直讓蛋都只能出如今第一個格子裏面吧?確定是要隨機放置在任意一個格子中的。
因此,到了這一步,就要開始寫咱們又愛又恨的javascript代碼了。
首先聲明,本文全部javascript皆爲原生js代碼,不熟悉的說明該多練練基礎了。
那麼,如何隨機放置蛇蛋呢?
咱們先列舉一下知識點和思路。
1.隨機函數Math.random()根據格子的行,列數量,生成範圍內的橫座標和縱座標值,二者結合,能夠惟必定位一個格子位置。
2.根據隨機函數生成的行,列值,取得對應的格子。
3.把蛋,放到對應的格子中。
直接寫代碼了嗎?不,要多想。
好比,如何生成隨機數比較合適呢?
瞭解過個人朋友都知道,我常常提到一個四字短語:意念編程。
什麼意思呢?
就好比到了如今這一步,不少愣頭青同窗,直接動手生成隨機數,而後去取元素去了!
扯淡!你知道元素結構了嗎?你知道元素的嵌套方式,怎麼經過隨機數去獲取那個元素了嗎?
因此,一行代碼,報三個錯,不是沒有道理的。
來,看一下結構。


如今知道怎麼取?該取哪一個元素了嗎?
x軸,也就是line行的左邊,是否是就是草地元素下面的children數組裏面的line元素數組下標!
y軸,是否是對應的某個line元素下方的children數組裏面的cell元素數組的下班!
用肉眼去觀察,比用意念去瞎貓碰死耗子,去胡思亂猜不是準確得多嗎?
惋惜,仍是有那麼多人,嚴重使用着意念編程。
來,想學編程嗎?我教你啊。
到了這一步,不少人可能找到主角,就逃之夭夭了。
這很差,很很差。
還能幹啥呢?除了children,其餘的屬性含義,做用,用法,都不想了解了解嘛?
想當年我但是每一個屬性的含義,做用,都一個個百度去搜了的,對於理解和使用原生javascript,是具備很是大的好處的。
好了,繼續主題。請看一下,咱們的格子結構,是行,列布局,全部的格子元素
那麼,咱們確定須要兩個隨機數。第一個產生x軸,也就是橫座標值,也就是第幾行。
第二個產生y軸,也就是縱座標值,也就是第幾列。
那麼,咱們直接生成兩次0到9的隨機數就行了,恰好對應10行和10行裏面的每一個格子。
那,爲啥是0到9,不是1到10呢?
由於,數組的起始索引值是0。
----------聰明人的分割線----------
接下來,不要心急,先把隨機數寫好。

// 蛋的隨機x軸座標,也就是第幾行
var eggX = Math.floor(Math.random() * 10)複製代碼

來,分析一下,它憑什麼能夠取到0到9之間的值(包括0,包括9)。
怎麼分析呢?很簡單,打開菜鳥教程(runoob.com)。


找到這個函數。
找不到?回家放牛吧。
看紅圈部分,不用說,這確定是重點。
它的返回值是[0,1),之間的值。
還記得初中,高中的,開閉區間嗎?
中括號表示包含,小括號表示不包含。
也就是說,Math.random()生成的值,是0到0.999...99之間的一個數。
若是用生成的隨機數來乘以1,獲得的最小值是0 * 1 = 0,獲得的最大值是0.999...999 * 1 = 0.999...999
沒毛病吧?若是有,回家放牛去吧。
把1放大10倍。最小是依然是0,最大值是多少?9.999...999
把1放大100倍。最小是依然是0,最大值是多少?99.99...999
好了,先打住,該分析Math.floor了。
老套路,打開菜鳥教程(runoob.com)


看到沒?返回小於等於x的最大整數。
啥意思?
Math.floor(1.6)返回的是1
同理可得,Math.floor(9.999...999)返回的是9!!!
這不就獲得,0到9之間的值了嗎。
問題來了。
爲啥要這樣處理呢?
你獲取過數組的第3.16736237個元素嗎?沒有吧,數組的索引必須是整數!
隨機數完美收官,恭喜到了這一步的同窗,又前進了一小步。
你的一小步,那真的只是一小步,真正的編程還沒開始呢。
接下來,能夠根據隨機數,獲取那個,隨機格子了。

// 草地元素
var grasslandElement = document.querySelector("#snake-grassland");
// 蛋的隨機x軸座標,也就是第幾行
var eggX = Math.floor(Math.random() * 10);
// 蛋的隨機y軸座標,也就是第幾列
var eggY = Math.floor(Math.random() * 10);
// 隨機格子元素,也就是蛋須要被放置的元素
// 經過草地元素下面的children line數組對應的隨機數獲取
var eggElement = grasslandElement.children[eggX].children[eggY];
                            複製代碼


能夠看到,十分之完美!
再接下來能夠幹嗎了?
給這個隨機格子,放一個蛋。

// 草地元素
var grasslandElement = document.querySelector("#snake-grassland");
// 蛋的隨機x軸座標,也就是第幾行
var eggX = Math.floor(Math.random() * 10);
// 蛋的隨機y軸座標,也就是第幾列
var eggY = Math.floor(Math.random() * 10);
// 隨機格子元素,也就是蛋須要被放置的元素
// 經過草地元素下面的children line數組對應的隨機數獲取
var eggElement = grasslandElement.children[eggX].children[eggY];
// 給隨機格子元素放入一個蛋
eggElement.innerHTML = '<div class="egg"></div>';複製代碼


很不簡單(寫代碼簡單,把怎麼寫說清楚,不簡單),蛇蛋終於完成了。
而此時,是凌晨1點整。
用心作教育真的不容易,痛苦並快樂着。
看到這裏的同窗,若是以爲本文對你有用,能夠加我微信c91374286
探討編程,和編程圈子的朋友們,一塊兒交流,互助,提升。

隨機畫蛇頭

一號主角,終於要上場了。
在畫一號主角以前,咱們來總結一下,以前的思路。
化繁爲簡:複雜邏輯拆分爲簡單邏輯,各個擊破。
速戰速決:顏色,樣式,位置,大小,都是能夠隨時更改的,它們不是開發初期的核心要素。不要花太多時間在這上面,甚至不要花時間在這上面。要學會,節省時間。
不少人不知道什麼是編程思惟,也時常有人問我。我也說不清楚,道不明白。
案例是很好的說明方式,若是以前我沒有回答清楚的,這個案例應該足以說明一部分了。
蛇怎麼畫呢?
這個須要好好思考了,它跟前面有點不同,這是真正須要邏輯思考的開始。
咱們知道,程序,是由數據結構+算法組成的。
他們的關係,用貪吃蛇的例子來講,那就是,蛇的組成,是數據結構。蛇可以移動和吃到蛋,是算法。
換個例子,也能夠說,組成機器人的材料,是結構。機器人行,走,跳,躍,是算法。
javascript裏面,有兩個最爲常見的結構:數組和對象。
那麼,用哪一個來表示蛇的結構呢?答案是,用二者組合來表示。
首先,來看看蛇的特色。
頭在前,尾在後。順序是肯定性的,固定的,吃到蛋後,尾部會增加,不會增加到頭前面去。
可以表示這樣一個蛇的結構,很明顯,數組是很是合適的,由於數組裏面的元素順序是固定的,能夠日後添加元素,添加元素後,順序也是固定的。

// 蛇元素數組
var snakeArray = [];複製代碼

蛇的結構肯定了,接下來要幹嗎呢?
肯定蛇的位置。
理解了前面蛋的生成,相必這裏也不用多說。
可是呢?咱們知道,蛋,永遠只有一個,不會同時出現2個蛋,也不會有多個蛋排在一塊兒(固然,你能夠這麼作,不過這不在本教程範圍內)
而蛇呢?它的各個身體部分,是有關聯的。你不能頭在第一行,身體第二節在第四行,第三節在第八行。對吧,這不合理。
因此,咱們須要幹嗎呢?還記得前面說過的嗎?咱們要用數組和對象的結合,去肯定蛇的位置。那麼,蛇的頭部以及身體部分的每一個元素,都有它本身的橫軸和縱軸座標。那麼,怎麼表示呢?以下。

// 蛇元素數組
var snakeArray = [{x:0,y:0}];
                            複製代碼

爲了方便起見,咱們一開始,只生成蛇的頭部。因此,數組的第一個元素,就是蛇頭的位置。
能夠看到,它是一個對象。數組裏面放對象,對象裏面放座標,是否是很優雅?這就是數據結構的威力。
若是體會不到這威力的話,咱們舉個反例,用雙層數組去表示。

// 蛇元素數組
var snakeArray = [[0,0]];
                           複製代碼

數組的第一個元素仍是個數組,數組的第0個元素,是橫軸,第1個元素是縱軸。
比起上面的的結構,那個更好點?
很明顯是上面那個,代碼是寫給人看的,是給人寫的。越直觀,越通俗的代碼,越好。
好了,咱們來肯定一下,蛇頭的位置,跟蛋同樣,用兩個隨機數來生成。(這裏其實涉及到,蛇頭和蛋恰好在同一個位置怎麼辦呢?涼拌,若是看到這篇教程的這個部分,你還不懂得,速戰速決,各個擊破,兵來將擋,水來土掩的道理,那能夠回家放牛了。)
正所謂取捨,取捨。它從某方面來講,並不表明放棄,而是先作最重要的部分,再處理邊緣部分。

// 草地元素
var grasslandElement = document.querySelector("#snake-grassland-3");
// 蛋的隨機x軸座標,也就是第幾行
var eggX = Math.floor(Math.random() * 10);
// 蛋的隨機y軸座標,也就是第幾列
var eggY = Math.floor(Math.random() * 10);
// 隨機格子元素,經過草地元素下面的children line數組對應的隨機數獲取
var eggElement = grasslandElement.children[eggX].children[eggY];
// 給隨機格子元素放入一個蛋
eggElement.innerHTML = '<div class="egg"></div>';
// 蛇頭的橫座標
var snakeX = Math.floor(Math.random() * 10);
// 蛇頭的縱座標
var snakeY = Math.floor(Math.random() * 10);
// 貪吃蛇蛇元素數組
var snakeArray = [{ x: snakeX, y: snakeY }];
// 生成貪吃蛇
snakeArray.forEach(function(item, index) {
    // 隨機格子元素,表示蛇的位置,同上
    var snakeElement = grasslandElement.children[item.x].children[item.y];
    // 生成蛇
    if (index === 0) {
        snakeElement.innerHTML = '<div class="snake snake-head"></div>';
    } else {
        snakeElement.innerHTML = '<div class="snake snake-body"></div>';
    }
});複製代碼


貪吃蛇往上移動

蛋和蛇頭都畫好了,每次刷新本頁面,都會看到不一樣位置的蛇蛋和蛇頭
那麼接下來呢?固然是要控制蛇的移動,讓蛇可以吃到蛋。
這裏呢,咱們使用鍵盤的方向鍵對蛇的移動進行控制。
在進行咱們的代碼編寫以前呢,咱們必定要謹記,不要意念編程。
至少有兩個問題,須要首先理清楚。
1.如何監聽鍵盤按下的操做?
2.按下以後,如何判斷按的是哪一個鍵?
解決辦法很簡單:作實驗。

// 監聽鍵盤按下,打印按下的事件
document.addEventListener('keydown',function(event){
    console.dir(event);
});複製代碼


當咱們按下向上的方向鍵。
能夠看到,有多種屬性,都指明瞭按下的鍵的值。
那麼,選擇哪一個來判斷呢?爲了更直觀地描述,這裏咱們選擇key屬性。
記住,不要一口吃個胖子,咱們先控制蛇向上移動,而後,再控制它四面八方移動。
在寫代碼以前,咱們須要思考一個事情。
如何往上移動?
咱們知道,蛇的位置,依靠x軸和y軸定位。
那麼,往上移動,要修改的是x的值?仍是y的值呢?
按照咱們的慣性思惟來講,確定是修改y的值。
可是,你們還記得前面咱們怎麼畫格子的嗎?
x表示第幾行,y表示這一行的第幾列。
我往上移動,是否是至關於,個人行數變小了(從上往下,依次爲第0行到到第9行。)
因此,該怎麼設置呢?是否是x = x - 1,對吧。
行數減少了1行嘛,縮寫就是x -= 1
那麼,實現代碼以下。


// 監聽鍵盤按下
document.addEventListener('keydown',function(event){
    // 若是按的是 上
    if(event.key === 'ArrowUp'){
        snakeHead.x -= 1;
    }
    // 生成貪吃蛇
    snakeArray.forEach(function(item, index) {
        // 隨機格子元素,表示蛇的位置,同上
        var snakeElement = grasslandElement.children[item.x].children[item.y];
        // 生成蛇 - 數組的第一個元素是蛇頭
        if (index === 0) {
            snakeElement.innerHTML = '<div class="snake snake-head"></div>';
        } else {
            snakeElement.innerHTML = '<div class="snake snake-body"></div>';
        }
    });
});複製代碼


能夠發現,按上方向鍵的時候,蛇頭是移動了,可是以前的位置並無清除掉原來的蛇頭,因此形成了多個殘影。
那麼,下一步,在生產下一個位置的貪吃蛇以前,把上一個貪吃蛇刪除掉。
怎麼刪除呢?原理很簡單。
無論是蛇仍是蛇的身體,都有一個共同的snake類名,找到全部當前的snake類名,而後刪除就好了。

// 監聽鍵盤按下
document.addEventListener('keydown',function(event){
    console.dir(event);
    // 若是按的是 上
    if(event.key === 'ArrowUp'){
        snakeHead.x -= 1;
    }
    // 刪除上一個貪吃蛇
    grasslandElement.querySelectorAll(".snake").forEach(function(item) {
        item.parentElement.removeChild(item);
    });
    // 生成貪吃蛇
    snakeArray.forEach(function(item, index) {
        // 隨機格子元素,表示蛇的位置,同上
        var snakeElement = grasslandElement.children[item.x].children[item.y];
        // 生成蛇 - 數組的第一個元素是蛇頭
        if (index === 0) {
            snakeElement.innerHTML = '<div class="snake snake-head"></div>';
        } else {
            snakeElement.innerHTML = '<div class="snake snake-body"></div>';
        }
    });
});
       複製代碼

這裏要注意一個地方,咱們刪除的時候,使用了parentElement屬性。
這是什麼意思呢?表示當前蛇元素是父級元素,也就是cell類名元素。
爲何要用它呢?由於,原生js裏面,添加一個子元素,或者刪除一個子元素。
都要以該元素的父級爲參照。(固然,元素本身刪除本身可能也有方法,同窗們本身去研究下吧)。


好了,往上移動的貪吃蛇能夠了。
html

貪吃蛇上下左右移動

還記得前面說的嗎?不要一口吃個胖子,一步步來,又穩又準。
搞定了往上移動,其餘三個方向,那就很簡單了。

// 上方向鍵控制蛇往上移動
document.addEventListener("keydown", function(event) {
    console.dir(event);
    // 蛇頭,貪吃蛇數組的第0個元素
    var snakeHead = snakeArray[0];
    // 若是按的是 上
    if (event.key === "ArrowUp") {
        snakeHead.x -= 1;
    }
    // 若是按的是 下
    if (event.key === "ArrowDown") {
        snakeHead.x += 1;
    }
    // 若是按的是 左
    if (event.key === "ArrowLeft") {
        snakeHead.y -= 1;
    }
    // 若是按的是 右
    if (event.key === "ArrowRight") {
        snakeHead.y += 1;
    }
    // 刪除上一個貪吃蛇
    grasslandElement.querySelectorAll(".snake").forEach(function(item) {
        item.parentElement.removeChild(item);
    });
    // 生成貪吃蛇
    snakeArray.forEach(function(item, index) {
        // 隨機格子元素,表示蛇的位置,同上
        var snakeElement = grasslandElement.children[item.x].children[item.y];
        // 生成蛇 - 數組的第一個元素是蛇頭
        if (index === 0) {
            snakeElement.innerHTML = '<div class="snake snake-head"></div>';
        } else {
            snakeElement.innerHTML = '<div class="snake snake-body"></div>';
        }
    });
});複製代碼

這裏,只展現監聽部分代碼,前面的就不放了,以避免代碼過長。
java


如上圖,你們會發現一個頗有意思的問題。
蛋是怎麼消失的?
咱們的代碼裏面,只有刪除蛇,並無刪除蛇蛋呀?
這是由於,蛇通過的格子,都會被蛇類型佔據。
通過蛋格子的時候,蛋被替換成蛇元素,而後,當咱們按下下一個方向的時候,蛇的上一個位置被刪除了。
因此,蛋就不見了。
注意一下,這裏並非被吃掉,是被蛇元素覆蓋了。
那麼,接下來,就是寫,蛇如何吃蛋了。

蛇吃蛋

又到了思考時刻。
蛇怎麼吃蛋呢?
還記得咱們的蛇結構嗎?

// 蛇頭的橫座標
var snakeX = Math.floor(Math.random() * 10);
// 蛇頭的縱座標
var snakeY = Math.floor(Math.random() * 10);
// 蛇元素數組 - 目前只有一個蛇頭
var snakeArray = [{ x: snakeX, y: snakeY }];複製代碼

看到沒?蛇自己是一個數組,蛇的頭和身體,是這個數組的每個元素。這個元素是個對象,記錄了頭和身體的橫座標和縱座標的位置
那麼咱們要增長蛇的身體,是否是直接往數組裏面,添加身體元素就好了?
因此,蛇吃飽了以後的樣子,以下。

var snakeArray = [{ x: x1, y: y1 },{ x: x2, y: y2 },{ x: x3, y: y3 }...{ x: xn, y: yn }];複製代碼

咱們本節的標題是,蛇吃蛋。可是呢,咱們一直在說,蛇吃了蛋以後怎麼樣,那麼,蛇怎麼吃蛋呢?
這個再簡單不過了,蛇頭和蛋的座標一致時,蛇就吃到蛋了。
雖說起來簡單,可是,須要考慮的地方,卻比較隱密。
什麼問題呢?
蛇吃到蛋以後,怎麼放一個身體元素到尾部?
有人可能會說,數組不是有push方法嗎,直接push({x:n,y:m})啊。
是麼?too yang to simple
我就問一個問題,怎麼得出追加的身體部分的橫座標和縱座標值?
因此,事情沒有那麼簡單。
先看代碼。

// 草地元素
var grasslandElement = document.querySelector("#snake-grassland");
// 蛋的隨機x軸座標,也就是第幾行
var eggX = Math.floor(Math.random() * 10);
// 蛋的隨機y軸座標,也就是第幾列
var eggY = Math.floor(Math.random() * 10);
// 隨機格子元素,也就是蛋須要被放置的元素
// 經過草地元素下面的children line數組對應的隨機數獲取
var eggElement = grasslandElement.children[eggX].children[eggY];
// 給隨機格子元素放入一個蛋
eggElement.innerHTML = '<div class="egg"></div>';
// 蛇頭的橫座標
var snakeX = Math.floor(Math.random() * 10);
// 蛇頭的縱座標
var snakeY = Math.floor(Math.random() * 10);
// 蛇元素數組 - 目前只有一個蛇頭
var snakeArray = [{ x: snakeX, y: snakeY }];
// 生成貪吃蛇
snakeArray.forEach(function(item, index) {
    // 隨機格子元素,表示蛇的位置,同上
    var snakeElement = grasslandElement.children[item.x].children[item.y];
    // 生成蛇 - 數組的第一個元素是蛇頭
    if (index === 0) {
        snakeElement.innerHTML = '<div class="snake snake-head"></div>';
    } else {
        snakeElement.innerHTML = '<div class="snake snake-body"></div>';
    }
});
 // 上方向鍵控制蛇往上移動
document.addEventListener("keydown", function(event) {
    // 蛇頭,貪吃蛇數組的第0個元素
    var snakeHead = snakeArray[0];
    // 若是按的是 上
    if (event.key === "ArrowUp") {
        snakeHead.x -= 1;
        // 蛇是否吃到蛋
        if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // 蛋被吃掉了,再生成一個
            eggX = Math.floor(Math.random() * 10);
            // 蛋的隨機y軸座標,也就是第幾列
            eggY = Math.floor(Math.random() * 10);
            // 隨機格子元素,經過草地元素下面的children line數組對應的隨機數獲取
            eggElement = grasslandElement.children[eggX].children[eggY];
            // 給隨機格子元素放入一個蛋
            eggElement.innerHTML = '<div class="egg"></div>';
            // 蛇尾 - 蛇數組最有一個元素
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x - 1,
                y: snakeTail.y
            });
        }
    }
    // 若是按的是 下
    if (event.key === "ArrowDown") {
        snakeHead.x += 1;
        if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // 蛋被吃掉了,再生成一個
            eggX = Math.floor(Math.random() * 10);
            // 蛋的隨機y軸座標,也就是第幾列
            eggY = Math.floor(Math.random() * 10);
            // 隨機格子元素,經過草地元素下面的children line數組對應的隨機數獲取
            eggElement = grasslandElement.children[eggX].children[eggY];
            // 給隨機格子元素放入一個蛋
            eggElement.innerHTML = '<div class="egg"></div>';
            // 蛇尾 - 蛇數組最有一個元素
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x + 1,
                y: snakeTail.y
            });
        }
    }
    // 若是按的是 左
    if (event.key === "ArrowLeft") {
        snakeHead.y -= 1;
        if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // 蛋被吃掉了,再生成一個
            eggX = Math.floor(Math.random() * 10);
            // 蛋的隨機y軸座標,也就是第幾列
            eggY = Math.floor(Math.random() * 10);
            // 隨機格子元素,經過草地元素下面的children line數組對應的隨機數獲取
            eggElement = grasslandElement.children[eggX].children[eggY];
            // 給隨機格子元素放入一個蛋
            eggElement.innerHTML = '<div class="egg"></div>';
            // 蛇尾 - 蛇數組最有一個元素
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x,
                y: snakeTail.y + 1
            });
        }
    }
    // 若是按的是 右
    if (event.key === "ArrowRight") {
        snakeHead.y += 1;
        if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // 蛋被吃掉了,再生成一個
            eggX = Math.floor(Math.random() * 10);
            // 蛋的隨機y軸座標,也就是第幾列
            eggY = Math.floor(Math.random() * 10);
            // 隨機格子元素,經過草地元素下面的children line數組對應的隨機數獲取
            eggElement = grasslandElement.children[eggX].children[eggY];
            // 給隨機格子元素放入一個蛋
            eggElement.innerHTML = '<div class="egg"></div>';
            // 蛇尾 - 蛇數組最有一個元素
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x,
                y: snakeTail.y - 1
            });
        }
    }
    // 刪除上一個貪吃蛇
    grasslandElement.querySelectorAll(".snake").forEach(function(item) {
        item.parentElement.removeChild(item);
    });
     // 生成貪吃蛇
    snakeArray.forEach(function(item, index) {
        // 隨機格子元素,表示蛇的位置,同上
        var snakeElement = grasslandElement.children[item.x].children[item.y];
        // 生成蛇 - 數組的第一個元素是蛇頭
        if (index === 0) {
            snakeElement.innerHTML = '<div class="snake snake-head"></div>';
        } else {
            snakeElement.innerHTML = '<div class="snake snake-body"></div>';
        }
    });
});複製代碼

哦,天哪,代碼量瞬間多了起來。
不過,仔細看的話,其實有部分代碼邏輯是同樣的。
可能有疑惑,先無論,先看效果。


疑問應該挺多的。
一個個解答。
解答以前呢,咱們能夠看到如上圖,咱們已經實現了,蛇可以吃蛋,可以長身體這兩個功能。
可是呢,BUG也很是之明顯,身體不跟着頭走。
其次呢,咱們以前說過了,蛇長身體,沒這麼簡單。
怎麼不簡單呢?看看代碼量增長了這麼多就知道了。
具體增長了什麼呢?
根據不一樣的方向鍵,身體增長的方式不同。
也就是說,我按左方向吃了蛋,身體會追加到頭的右側。
按右方向鍵,身體會增長到頭的左側。
按上方向鍵,身體會增長到頭的下側。
按下方向鍵,身體會增長到頭的上側。
總而言之,身體永遠不會出如今頭移動方向上的的前方,左方和右方,只會增長到後方。
小小的身體增長,也蘊含了一小點的策略。NICE。
----------聰明的分割線---------
繼續,咱們還增長了什麼功能呢?
蛇吃完蛋了,總不能就沒得吃了吧,對不對?
因此,每次吃了蛋,就再生一個蛋。
邏輯部分很明顯了,可是重複代碼比較多,這個後面會講優化。
那麼,還剩下一個重頭戲,身體不跟着頭走!

身隨頭走

到了這個地方,就不只僅是寫代碼的問題了,而是編程思惟的問題,思考策略的問題,代碼調試的藝術的問題。
具體怎麼說呢?
首先,咱們要定位問題。
問題不是已經定位了嗎,身體不跟着頭走啊!
是的,概況的講,是這樣。可是!
你可以肯定,爲了讓身體跟着頭走,哪些代碼須要修改,哪些代碼不須要修改,哪些代碼須要從新整理邏輯嗎?
因此,解決問題,可不能紙上談兵,要有具體的分析,具體的判斷。
不少人就是缺少這一點,因此,代碼學得很難受,寫得更難受。
好了,咱們來分析一下。
蛇吃蛋,蛋隨機再生成,跟身體不隨頭走這個BUG,固然是無關的。
其次,每次吃了蛋以後,根據移動的方向,追加身體元素,這裏也是毫無破綻。
那麼,還剩下一個什麼呢?移動的方式有問題啊!
怎麼個有問題法?
仔細看代碼,每次移動的按方向鍵的時候,只有蛇頭加1,減1,減1,加1,對不對?
身體部分呢?風雨不動安如山。
因此,咱們須要讓身體部分,也動起來。
並且,是跟着蛇頭動起來。
怎麼跟着蛇頭動呢?
舉個例子。
你要坐火車,須要排隊買票。
人不少,排成一排,這個是否是就跟我咱們這個貪吃蛇如出一轍?
假設第一我的是蛇頭,第一我的買完票,走到一遍,那麼以前第一我的的位置,是否是被第二我的佔據?
而後呢?後面的人,以此類推,後一我的,佔據前一我的的位置。
因此,咱們要怎麼作?蛇頭移動的時候,要讓身體部分,一個個佔據前一個的位置。

// 草地元素
var grasslandElement = document.querySelector("#snake-grassland");
// 蛋的隨機x軸座標,也就是第幾行
var eggX = Math.floor(Math.random() * 10);
// 蛋的隨機y軸座標,也就是第幾列
var eggY = Math.floor(Math.random() * 10);
// 隨機格子元素,也就是蛋須要被放置的元素
// 經過草地元素下面的children line數組對應的隨機數獲取
var eggElement = grasslandElement.children[eggX].children[eggY];
// 給隨機格子元素放入一個蛋
eggElement.innerHTML = '<div class="egg"></div>';
// 蛇頭的橫座標
var snakeX = Math.floor(Math.random() * 10);
// 蛇頭的縱座標
var snakeY = Math.floor(Math.random() * 10);
// 蛇元素數組 - 目前只有一個蛇頭
var snakeArray = [{ x: snakeX, y: snakeY }];
// 生成貪吃蛇
snakeArray.forEach(function(item, index) {
    // 隨機格子元素,表示蛇的位置,同上
    var snakeElement = grasslandElement.children[item.x].children[item.y];
    // 生成蛇 - 數組的第一個元素是蛇頭
    if (index === 0) {
        snakeElement.innerHTML = '<div class="snake snake-head"></div>';
    } else {
        snakeElement.innerHTML = '<div class="snake snake-body"></div>';
    }
});
 // 上方向鍵控制蛇往上移動
document.addEventListener("keydown", function(event) {
    // 蛇頭,貪吃蛇數組的第0個元素
    var snakeHead = snakeArray[0];
    // 若是按的是 上
    if (event.key === "ArrowUp") {
        // 遞增移動,佔據前一個位置
        var snakeArray2 = [];
        snakeArray.forEach(function(item, index) {
            if (index === 0) {
                snakeArray2.push({
                    x: snakeArray[index].x - 1,
                    y: snakeArray[index].y
                });
            } else {
                snakeArray2.push({
                    x: snakeArray[index - 1].x,
                    y: snakeArray[index - 1].y
                });
            }
        });
        snakeArray = snakeArray2;
        // 蛇是否吃到蛋
        if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // 蛋被吃掉了,再生成一個
            eggX = Math.floor(Math.random() * 10);
            // 蛋的隨機y軸座標,也就是第幾列
            eggY = Math.floor(Math.random() * 10);
            // 隨機格子元素,經過草地元素下面的children line數組對應的隨機數獲取
            eggElement = grasslandElement.children[eggX].children[eggY];
            // 給隨機格子元素放入一個蛋
            eggElement.innerHTML = '<div class="egg"></div>';
            // 蛇尾 - 蛇數組最有一個元素
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x - 1,
                y: snakeTail.y
            });
        }
    }
    // 若是按的是 下
    if (event.key === "ArrowDown") {
        // 遞增移動,佔據前一個位置
        var snakeArray2 = [];
        snakeArray.forEach(function(item, index) {
            if (index === 0) {
                snakeArray2.push({
                    x: snakeArray[index].x + 1,
                    y: snakeArray[index].y
                });
            } else {
                snakeArray2.push({
                    x: snakeArray[index - 1].x,
                    y: snakeArray[index - 1].y
                });
            }
        });
        snakeArray = snakeArray2;
        if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // 蛋被吃掉了,再生成一個
            eggX = Math.floor(Math.random() * 10);
            // 蛋的隨機y軸座標,也就是第幾列
            eggY = Math.floor(Math.random() * 10);
            // 隨機格子元素,經過草地元素下面的children line數組對應的隨機數獲取
            eggElement = grasslandElement.children[eggX].children[eggY];
            // 給隨機格子元素放入一個蛋
            eggElement.innerHTML = '<div class="egg"></div>';
            // 蛇尾 - 蛇數組最有一個元素
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x + 1,
                y: snakeTail.y
            });
        }
    }
    // 若是按的是 左
    if (event.key === "ArrowLeft") {
        // 遞增移動,佔據前一個位置
        var snakeArray2 = [];
        snakeArray.forEach(function(item, index) {
            if (index === 0) {
                snakeArray2.push({
                    x: snakeArray[index].x,
                    y: snakeArray[index].y - 1
                });
            } else {
                snakeArray2.push({
                    x: snakeArray[index - 1].x,
                    y: snakeArray[index - 1].y
                });
            }
        });
        snakeArray = snakeArray2;
        if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // 蛋被吃掉了,再生成一個
            eggX = Math.floor(Math.random() * 10);
            // 蛋的隨機y軸座標,也就是第幾列
            eggY = Math.floor(Math.random() * 10);
            // 隨機格子元素,經過草地元素下面的children line數組對應的隨機數獲取
            eggElement = grasslandElement.children[eggX].children[eggY];
            // 給隨機格子元素放入一個蛋
            eggElement.innerHTML = '<div class="egg"></div>';
            // 蛇尾 - 蛇數組最有一個元素
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x,
                y: snakeTail.y + 1
            });
        }
    }
    // 若是按的是 右
    if (event.key === "ArrowRight") {
        // 遞增移動,佔據前一個位置
        var snakeArray2 = [];
        snakeArray.forEach(function(item, index) {
            if (index === 0) {
                snakeArray2.push({
                    x: snakeArray[index].x,
                    y: snakeArray[index].y + 1
                });
            } else {
                snakeArray2.push({
                    x: snakeArray[index - 1].x,
                    y: snakeArray[index - 1].y
                });
            }
        });
        snakeArray = snakeArray2;
        if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // 蛋被吃掉了,再生成一個
            eggX = Math.floor(Math.random() * 10);
            // 蛋的隨機y軸座標,也就是第幾列
            eggY = Math.floor(Math.random() * 10);
            // 隨機格子元素,經過草地元素下面的children line數組對應的隨機數獲取
            eggElement = grasslandElement.children[eggX].children[eggY];
            // 給隨機格子元素放入一個蛋
            eggElement.innerHTML = '<div class="egg"></div>';
            // 蛇尾 - 蛇數組最有一個元素
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x,
                y: snakeTail.y - 1
            });
        }
    }
    // 刪除上一個貪吃蛇
    grasslandElement.querySelectorAll(".snake").forEach(function(item) {
        item.parentElement.removeChild(item);
    });
     // 生成貪吃蛇
    snakeArray.forEach(function(item, index) {
        // 隨機格子元素,表示蛇的位置,同上
        var snakeElement = grasslandElement.children[item.x].children[item.y];
        // 生成蛇 - 數組的第一個元素是蛇頭
        if (index === 0) {
            snakeElement.innerHTML = '<div class="snake snake-head"></div>';
        } else {
            snakeElement.innerHTML = '<div class="snake snake-body"></div>';
        }
    });
});複製代碼


改形成功。
來看看,咱們改造了什麼?
主要是下面這段代碼。

// 遞增移動,佔據前一個位置
var snakeArray2 = [];
snakeArray.forEach(function(item, index) {
    if (index === 0) {
        snakeArray2.push({
            x: snakeArray[index].x - 1,
            y: snakeArray[index].y
        });
    } else {
        snakeArray2.push({
            x: snakeArray[index - 1].x,
            y: snakeArray[index - 1].y
        });
    }
});
snakeArray = snakeArray2;複製代碼

來分析一下,這段代碼作了啥?
首先看到,定義了一個數組,蛇之二號(snakeArray2)
爲啥要定義它呢?用來保存蛇移動以後的數據,而後替換掉蛇移動以前的數據。
爲啥要這麼作呢?這是一個關於,值類型和引用類型的故事,這個故事咱們這裏不講,否則這篇文章扯來扯去,能夠寫一本書了。
總而言之,咱們的思路是沒錯的:用蛇的新位置,替換舊位置,蛇就移動了。
那麼新位置怎麼來呢?固然是來源於蛇自己。
因此,咱們對蛇當前的數組,進行遍歷,來生成新的位置數據。
首先第一點,蛇頭是個特殊的元素。
怎麼個特殊法?
它前面沒有元素了,不能採起佔位法去佔據前一個位置,因此,它須要根據移動方向,增減橫座標或者縱座標的值。
仔細看看,能夠發現,它跟咱們前面身體不隨頭走的時候,那裏的蛇頭增減邏輯是同樣的。
那麼,剩下的就是身體了。
身體很簡單,把前一個的位置,賦值給本身,結束。

貪吃蛇自動移動

沒錯,結束了,非常突兀,就是這麼神奇。
如今,來到這一節,貪吃蛇自動移動。
玩過徹底手動控制,本身不會自動移動的貪吃蛇嗎?
上面那個就是,這是一條非主流的貪吃蛇。
那麼,咱們把它變成主流的貪吃蛇。
自動移動,很容易冒出一個詞:定時器。
沒錯,就是用它來實現,貪吃蛇自動移動。

// 草地元素
var grasslandElement = document.querySelector("#snake-grassland");
// 蛋的隨機x軸座標,也就是第幾行
var eggX = Math.floor(Math.random() * 10);
// 蛋的隨機y軸座標,也就是第幾列
var eggY = Math.floor(Math.random() * 10);
// 隨機格子元素,也就是蛋須要被放置的元素
// 經過草地元素下面的children line數組對應的隨機數獲取
var eggElement = grasslandElement.children[eggX].children[eggY];
// 給隨機格子元素放入一個蛋
eggElement.innerHTML = '<div class="egg"></div>';
// 蛇頭的橫座標
var snakeX = Math.floor(Math.random() * 10);
// 蛇頭的縱座標
var snakeY = Math.floor(Math.random() * 10);
// 蛇元素數組 - 目前只有一個蛇頭
var snakeArray = [{ x: snakeX, y: snakeY }];
// 生成貪吃蛇
snakeArray.forEach(function(item, index) {
    // 隨機格子元素,表示蛇的位置,同上
    var snakeElement = grasslandElement.children[item.x].children[item.y];
    // 生成蛇 - 數組的第一個元素是蛇頭
    if (index === 0) {
        snakeElement.innerHTML = '<div class="snake snake-head"></div>';
    } else {
        snakeElement.innerHTML = '<div class="snake snake-body"></div>';
    }
});
// 默認自動往右移動
var arrow = "ArrowRight";
// 移動循環函數
function MoveLoop (){
    // 蛇頭,貪吃蛇數組的第0個元素
    var snakeHead = snakeArray[0];
    // 若是按的是 上
    if (arrow === "ArrowUp") {
        // 遞增移動,佔據前一個位置
        var snakeArray2 = [];
        snakeArray.forEach(function(item, index) {
            if (index === 0) {
                snakeArray2.push({
                    x: snakeArray[index].x - 1,
                    y: snakeArray[index].y
                });
            } else {
                snakeArray2.push({
                    x: snakeArray[index - 1].x,
                    y: snakeArray[index - 1].y
                });
            }
        });
        snakeArray = snakeArray2;
        // 蛇是否吃到蛋
        if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // 蛋被吃掉了,再生成一個
            eggX = Math.floor(Math.random() * 10);
            // 蛋的隨機y軸座標,也就是第幾列
            eggY = Math.floor(Math.random() * 10);
            // 隨機格子元素,經過草地元素下面的children line數組對應的隨機數獲取
            eggElement = grasslandElement.children[eggX].children[eggY];
            // 給隨機格子元素放入一個蛋
            eggElement.innerHTML = '<div class="egg"></div>';
            // 蛇尾 - 蛇數組最有一個元素
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x - 1,
                y: snakeTail.y
            });
        }
    }
    // 若是按的是 下
    if (arrow === "ArrowDown") {
        // 遞增移動,佔據前一個位置
        var snakeArray2 = [];
        snakeArray.forEach(function(item, index) {
            if (index === 0) {
                snakeArray2.push({
                    x: snakeArray[index].x + 1,
                    y: snakeArray[index].y
                });
            } else {
                snakeArray2.push({
                    x: snakeArray[index - 1].x,
                    y: snakeArray[index - 1].y
                });
            }
        });
        snakeArray = snakeArray2;
        if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // 蛋被吃掉了,再生成一個
            eggX = Math.floor(Math.random() * 10);
            // 蛋的隨機y軸座標,也就是第幾列
            eggY = Math.floor(Math.random() * 10);
            // 隨機格子元素,經過草地元素下面的children line數組對應的隨機數獲取
            eggElement = grasslandElement.children[eggX].children[eggY];
            // 給隨機格子元素放入一個蛋
            eggElement.innerHTML = '<div class="egg"></div>';
            // 蛇尾 - 蛇數組最有一個元素
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x + 1,
                y: snakeTail.y
            });
        }
    }
    // 若是按的是 左
    if (arrow === "ArrowLeft") {
        // 遞增移動,佔據前一個位置
        var snakeArray2 = [];
        snakeArray.forEach(function(item, index) {
            if (index === 0) {
                snakeArray2.push({
                    x: snakeArray[index].x,
                    y: snakeArray[index].y - 1
                });
            } else {
                snakeArray2.push({
                    x: snakeArray[index - 1].x,
                    y: snakeArray[index - 1].y
                });
            }
        });
        snakeArray = snakeArray2;
        if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // 蛋被吃掉了,再生成一個
            eggX = Math.floor(Math.random() * 10);
            // 蛋的隨機y軸座標,也就是第幾列
            eggY = Math.floor(Math.random() * 10);
            // 隨機格子元素,經過草地元素下面的children line數組對應的隨機數獲取
            eggElement = grasslandElement.children[eggX].children[eggY];
            // 給隨機格子元素放入一個蛋
            eggElement.innerHTML = '<div class="egg"></div>';
            // 蛇尾 - 蛇數組最有一個元素
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x,
                y: snakeTail.y + 1
            });
        }
    }
    // 若是按的是 右
    if (arrow === "ArrowRight") {
        // 遞增移動,佔據前一個位置
        var snakeArray2 = [];
        snakeArray.forEach(function(item, index) {
            if (index === 0) {
                snakeArray2.push({
                    x: snakeArray[index].x,
                    y: snakeArray[index].y + 1
                });
            } else {
                snakeArray2.push({
                    x: snakeArray[index - 1].x,
                    y: snakeArray[index - 1].y
                });
            }
        });
        snakeArray = snakeArray2;
        if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // 蛋被吃掉了,再生成一個
            eggX = Math.floor(Math.random() * 10);
            // 蛋的隨機y軸座標,也就是第幾列
            eggY = Math.floor(Math.random() * 10);
            // 隨機格子元素,經過草地元素下面的children line數組對應的隨機數獲取
            eggElement = grasslandElement.children[eggX].children[eggY];
            // 給隨機格子元素放入一個蛋
            eggElement.innerHTML = '<div class="egg"></div>';
            // 蛇尾 - 蛇數組最有一個元素
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x,
                y: snakeTail.y - 1
            });
        }
    }
    // 刪除上一個貪吃蛇
    grasslandElement.querySelectorAll(".snake").forEach(function(item) {
        item.parentElement.removeChild(item);
    });
     // 生成貪吃蛇
    snakeArray.forEach(function(item, index) {
        // 隨機格子元素,表示蛇的位置,同上
        var snakeElement = grasslandElement.children[item.x].children[item.y];
        // 生成蛇 - 數組的第一個元素是蛇頭
        if (index === 0) {
            snakeElement.innerHTML = '<div class="snake snake-head"></div>';
        } else {
            snakeElement.innerHTML = '<div class="snake snake-body"></div>';
        }
    });
}
// 上方向鍵控制蛇往上移動
document.addEventListener("keydown", function(event) {
    // 賦值全局方向變量
    arrow = event.key;
    // 執行依次移動函數
    MoveLoop();
});
setInterval(function(){
    MoveLoop();
},500);複製代碼


每半秒,也就是500毫秒,移動一次。
怎麼作到的呢?
咱們把方向提取到一個全局變量,arrow,用它來控制移動的反向。
而後,把監聽內的手動代碼,所有提取到一個外部函數,外部函數根據arrow的值,來決定蛇的移動方向。
而後,定時器開動,蛇就自動移動了。
代碼裏改動很是少,可是,我相信,有不少人想不到。
這就是編程思惟,四兩撥千斤,看透問題的本質。
咱們須要鍛鍊這樣的思惟。
本網站的目的之一,就是鍛鍊經過一個個案例,一篇篇深刻分析的文章,來培養同窗們的編程思惟。
爲啥要用這種方式呢?
我想,不少同窗應該都據說過,編程思惟這個詞,卻很難理解,也很難講得通。
這是爲啥呢?由於這是一種思惟方式,是很難言說的。
那麼怎麼可以分享這種思惟呢?
很簡單。
張三說某某飯點的某某菜很好吃,不管張三怎麼描述,李四始終半信半疑。
那怎麼辦呢?帶李四去吃一頓不就行了。
這個很是淺顯易懂的道理,不少人不懂得,也不知道。
編程怎麼學習?要從原理,從根本上理解。
我是陳隨易,座右銘是:何以解憂,惟有代碼。
我喜歡分享,喜歡技術,喜歡交友,目前致力於下降編程的學習難度,讓天下沒有難學的編程。
編程很簡單,只是你還未曾學會編程思惟而已,想學的話,我教你啊。
相關文章
相關標籤/搜索