# JavaScript - 貪食蛇 * 完成範例:[**CodePen**](https://cdpn.io/pen/debug/MWRXXaG?authentication_hash=NjMYzNbwQdar)、[**GitHub**](https://liu-huangling.github.io/JSPractice/13F/index.html)、[**Medium**](https://medium.com/@LindaLiu0821/javascript%E5%9C%B0%E4%B8%8B%E5%9F%8E13-%E8%B2%AA%E9%A3%9F%E8%9B%87-45b4a8b9c870) ![螢幕擷取畫面 2024-06-11 092844](https://hackmd.io/_uploads/SkIDumSB0.png) > 13F 貪食蛇 ## 使用語法 * HTML、CSS(SCSS)、原生JS ## 攻略要點 1. 【特定技術】需記錄歷史最高分紀錄,吃到一個餌加一分。 2. 【特定技術】留意貪吃蛇變長時,也會依照蛇的長度提供漸層顏色。餌的座標也需提供十字光亮漸層。 3. 【特定技術】蛇體可穿牆到對向位置 4. 【特定技術】留意全程遊戲都必須透過鍵盤來操作。 5. 遊戲隨分數增加,蛇本體變速 ## 拆解步驟 ### **HTML** * 這次主要分成三大區塊來完成整個遊戲體驗 * 第一個介面就是準備開始遊戲(純圖片跟文字應該沒什麼好介紹ㄉ...) * 第二個介面是遊戲的重點,有遊戲主體以及旁邊的文字配置 * 第三個介面為遊戲結束,會顯示歷史紀錄以及當局的遊戲成績 ### **CSS(SCSS)** * 以下方圖片為例,特別說明一下文字 ![螢幕擷取畫面 2024-06-11 110036](https://hackmd.io/_uploads/r1BuRNSSC.png) * 可以做的方法很多,在本次專案中主要是<font color=purple>**`writing-mode`**</font>搭配<font color=purple>**`transform中rorate的特性`**</font>做使用 * 老樣子介紹 ■ <font color=cake>**`writing-mode`**</font>:當內容為水平或垂直佈局方式,包含在塊框元素中的行進方向。 | <div style="width:100px">參數</div> | 說明 | 範例 | | -------- | -------- | -------- | | horizontal-tb | 內容**水平**書寫並從左至右水平排序 |![螢幕擷取畫面 2024-06-11 112317](https://hackmd.io/_uploads/HJmOQBSHA.png)| | vertical-lr | 內容**垂直**書寫並從<font color=cake>左至右</font>水平排序 |![螢幕擷取畫面 2024-06-11 112636](https://hackmd.io/_uploads/Sy-VNHHSC.png)| | vertical-rl | 內容**垂直**書寫由<font color=cake>右至左</font>排序 |![螢幕擷取畫面 2024-06-11 112751](https://hackmd.io/_uploads/HJxuNrrS0.png)| * 除此之外,特別補充一下當內容為<font color=blue>**垂直**</font>時,常與之搭配的用法,換句話說就是<font color =cake>必須搭配 **`writing-mode`** </font>使用!!! ■ <font color=cake>**`text-orientation`**</font>:當內容為<font color=blue>**垂直**</font>的佈局方式,包含在塊框元素中,文字的方向控制。 | <div style="width:80px">參數</div> | 說明 | 範例 | | -------- | -------- | -------- | | mixed | 初始值(沒有啥變化) |![螢幕擷取畫面 2024-06-11 114647](https://hackmd.io/_uploads/rko3dBSBC.png)| | upright | 將英文旋轉 90 度,其他 (中文) 參照其原始方向 |![螢幕擷取畫面 2024-06-11 115133](https://hackmd.io/_uploads/BkD0tHrB0.png)| | sideways | 將所有內容旋轉 90 度 (不分語系) |![螢幕擷取畫面 2024-06-11 115104](https://hackmd.io/_uploads/SJ33KrrSC.png)| * 剩下的就是基本嚕,例如顏色透明度的使用、動畫的使用等等,相信大家一定都可以順利解決 σ`∀´)σ!! ### **JavaScript** > 呼~終於來到重頭戲( ´・・)ノ(._.`) * 遊戲主要分為以下的幾個部分: * 建立棋盤、隨機出現食物+食物光標、貪食蛇 * 鍵盤控制活動(空白鍵、<font color=purple>`Y`</font>鍵、<font color=purple>`N`</font>鍵、上下左右鍵) * 蛇移動(遊戲畫面更新) * 遊戲變速 * 遊戲結束 * 分數紀錄 #### **建立棋盤、隨機出現食物+食物光標、貪食蛇** - **建立棋盤** - 主要用兩層迴圈創立div,如下圖的方式將棋盤格添加進我們的遊戲畫面 ![螢幕擷取畫面 2024-06-12 102617](https://hackmd.io/_uploads/Sy3UPF8BC.png) ```javascript= let rowNum = 16; let columnNum = 28; for(var i = 0;i < rowNum ; i++){ let row = document.createElement('div'); row.classList.add('row'); // 紅色外框 for(let j=0;j<columnNum;j++){ let box = document.createElement('div') box.classList.add('box'); // 藍色方塊 box.dataset.x=j; box.dataset.y=i; row.appendChild(box); } gameArea.appendChild(row); } ``` - **隨機出現食物 + 食物光標** - 使用forEach遍歷所有棋盤的位置,利用隨機數抽取並將食物元素使用`appendChild`加入至棋盤格內 ```javascript= locationBox.forEach(box => { if(box.dataset.x == randomCol && box.dataset.y == randomRow ){ // 如果有其他元素,重新計算食物位置 if(document.querySelector(`.box[data-x="${randomCol}"][data-y="${randomRow}"]`).childNodes.length > 0){ foodPosition.x = randomNumber(6,parseInt(columnNum)-1); foodPosition.y = randomNumber(0,parseInt(rowNum)-1); document.querySelector(`.box[data-x="${foodPosition.x}"][data-y="${foodPosition.y}"]`).appendChild(food); return; } foodPosition.x = randomCol; foodPosition.y = randomRow; box.appendChild(food); createFoodBGC(randomCol,randomRow); // 建立食物光標 return; } }); ``` - 下圖預計食物光標位置圖,透過此圖就可以發現只需要得知食物上下左右的位置即可繪製,所以只需要先得知食物的確切座標,使用for迴圈分別添加classname以及設定透明度,即完成 ![螢幕擷取畫面 2024-06-12 110327](https://hackmd.io/_uploads/HyqMl98HR.png) - **建立貪食蛇** - 基本上就是利用迴圈建立身體,再找棋盤位置,再把元素添加至棋盤格內,下面會再講解,那特別講解怎麼去做出漸層的身體以及如何讓蛇穿牆 - 漸層的身體 - 使用`opacity`去做透明度的設定,其中最低設定要有0.2以上,以防根本看不到~ - 讓蛇穿牆 - 及時捕捉蛇頭位置,透過`if/else`控制蛇頭出現的地方,搭配`setInterval`就會出現穿牆的感覺摟~ #### **鍵盤控制活動(空白鍵、<font color=purple>Y</font>鍵、<font color=purple>N</font>鍵、上下左右鍵)** * 使用原生JS,所有的鍵盤控制都是由<font color=blue>` window.addEventListener('keydown')`</font>去做監聽,再由<font color=cake>**e.keyCode**</font>去做判斷目前要執行的程式與動作,本次專案所用到的event.keyCode如下方程式碼顯示: ```javascript= // 空白鍵 控制遊戲開始 e.keyCode = 32; // 上下左右鍵 貪食蛇的走向 e.keyCode = 38; // 上 e.keyCode = 40; // 下 e.keyCode = 37; // 左 e.keyCode = 39; // 右 // Y/N鍵 遊戲結束後始否要重新開始 e.keyCode = 89; // Y e.keyCode = 78; // N ``` * 其中為了預防會有其他鍵盤影響,所以在遊戲畫面中可以透過<font color=cake>**e.returnValue**</font>設定為**false**去禁用其他的鍵盤操作 #### **蛇移動(遊戲畫面更新)** - 說明一下整體的流程: 1. 首先,設定`setInterval`讓他可以每秒做更新。 2. 接下來,會分成兩個function - `snakeMove()`判定蛇的走位以及是否有吃到食物 - 透過前面鍵盤鍵位監聽,紀錄走位方向,走位方向怎麼看如下圖: ![螢幕擷取畫面 2024-06-17 102401](https://hackmd.io/_uploads/rJXYRGpHA.png) - 計算蛇頭加上走位方向後的新變數,並使用`unshift`添加至陣列的開頭成為新的蛇頭 - 判斷食物位置使否和新蛇頭位置相符,以及是否有接收到遊戲結束的回傳資訊 - `updateGameBoard()`更新蛇在畫面上的位置 - 每次更新之前,<font color=cake>**務必要**</font>先清除原本蛇的位置 - 接下來,就會回到上述建立貪食蛇的函式,因為在`snakeMove()`已經重新更新了蛇座標,所以在這邊重新繪製,看起來就會很像它動起來搂!ξ( ✿>◡❛) #### **遊戲變速** * 設定好速度的初始值,接下來在遊戲中,每吃到一次食物,成績會+1,每五分就加速0.05秒,直到遊戲結束 ```javascript= // score 遊戲成績 if(score % 5 === 0){ clearInterval(gameInterval); // 清除遊戲計時器 timer -= 50; if(timer<=100){ timer = 100; } speed(timer); // 重新添加設定的秒數,完成遊戲變速的條件 } ``` #### **遊戲結束** * 在此專案中,使用身體碰撞檢測是否有達成遊戲結束的條件,所以只需要透過`for`迴圈去判斷蛇頭的座標是否有跟我們蛇身體陣列中的任一數相同即可 #### **分數紀錄** * 歷史成績一樣用到老朋友<font color=purple>**localStorage**</font>做紀錄,判斷方式如下方圖示說明,當有需要更動在使用`setItem`的方式做更改~ ![螢幕擷取畫面 2024-06-12 113443](https://hackmd.io/_uploads/BkUwvqUrA.png) ## 結尾 寫完文章有種老了的感覺,這關我覺得很有趣,包含做完也有點小小成就感(?,當初花了不少時間處理要讓蛇動起來以及畫面處理的部分,所以花了不少篇幅講解,希望大家能看懂༼ ༎ຶ ෴ ༎ຶ༽ ## 參考資料 * [How to make a snake game with JavaScript and HTML](https://medium.com/@j0hnskot/how-to-make-a-snake-game-with-javascript-and-html-7c8c78b85873):這篇講解的很詳細,如果想試基本的可以直接看這個~~ ## ##### ヽ(∀゚ )人(゚∀)人( ゚∀)人(∀゚ )人(゚∀)人( ゚∀)ノヽ(∀゚ )人(゚∀゚)人( ゚∀)人(∀゚ )人(゚∀゚)人( ゚∀)人(∀゚ )人( ゚∀)人 ### 以上 如果哪裡有錯誤或有問題,歡迎提出來一起討論~~~~