效果圖
最近使用 JS 寫了一個貪吃蛇游戲,效果如下:

谷歌瀏覽器點擊直接在線玩
前言
貪吃蛇作為一款經典又簡單的小游戲,每個人都玩過,實作一個貪吃蛇游戲基本具有以下功能:
- 棋盤(也被稱作 “地圖”,我這里畫的像一個圍棋棋盤,索性就叫棋盤)
- 蛇 (細致一點分為:蛇頭、蛇身、蛇尾)
- 方向(上下左右)控制,并且自動行走
- 碰撞檢測(撞墻、撞自己)
- 食物在隨機位置生成
- 蛇吃到食物,尾部生長一截
以上也便是我的實作步驟了,下面分享一些更詳細的實作思路,
棋盤
棋盤就是蛇運動的區域,簡陋一點甚至可以不要邊框,但是為了更好的游戲體驗,這里還是加上邊框,但是此邊框非彼邊框,使用 border 來實作效果圖中的邊框也可以,但這里我采用了其他的方式,比如 背景色 ,看下圖:

通過層級、定位,使得黑塊成為了藍塊視覺上的邊框,然后結合 grid 網格布局實作 格格分明 的棋盤便尤為簡單,

這里一個大塊采用 黑色背景 ,400個小塊采用 白色背景 ,使用 grid 布局橫、縱分別 20 個依次排列,再加上網格的行、列間距,便能形成圍棋棋盤式的布局,這里的黑線實則就是 遮擋+間隙 形成的,代碼如下:
<style>
// 大塊
#checkerboard {
display: grid;
grid-template-columns: repeat(20, 16px);
grid-template-rows: repeat(20, 16px);
grid-column-gap: 1px;
grid-row-gap: 1px;
padding: 1px;
background: #000;
}
// 小塊
.square {
background: #fff;
}
</style>
因為是 grid 布局,所有一些 上古瀏覽器 就不能玩這個游戲了,

坐標
很多游戲都有坐標的概念,貪吃蛇支持自動行走、食物生成在隨機位置以及碰撞檢測等功能都是依據坐標來實作的,
我這里的坐標實作很簡單:第一行第一個格子坐標為 1-1,第一行最后一個格子坐標為 1-20

在生成 div 時將每一個格子 對應的坐標 通過 css類名 對應起來,代碼如下:
/**
* 棋盤 20 * 20
* 坐標:1-1, ... 1-20, ... 20-20
*/
class Checkerboard {
constructor(container, gridNum) {
this.container = container
this.gridNum = gridNum
this.generate()
}
// 每個格子類名為:SX軸坐標_Y軸坐標 S1_20
generate() {
const fragment = document.createDocumentFragment()
for (let i = 0; i < this.gridNum; i++) {
for (let j = 0; j < this.gridNum; j++) {
const grid = document.createElement('div')
grid.classList.add('square', `S${j + 1}_${i + 1}`)
fragment.appendChild(grid)
}
}
this.container.appendChild(fragment)
}
}
這樣第一行第一個 div 的類名是 S1_1,第一行最后一個 div 的類名為 S1_20,可以這么說,這個 css 類名的設計就是這個游戲的核心設計 ,因為后續的自動行走、食物生成在隨機位置以及碰撞檢測等功能全部都是在操作 css類名 ,
更詳細代碼可以在底部查看原始碼~
蛇、食物的繪制
很多時候采用 CSS 樣式 結合 css類名 的切換能更好、更快、更高性能的實作影片或者其他的邏輯,那么在這里 蛇 和 食物 繪制我直接通過加 css類名 就解決了,代碼如下:
<style>
.square {
display: flex;
align-items: center;
justify-content: center;
background: #fff;
}
.snake::after {
display: block;
content: '';
width: 70%;
height: 70%;
border-radius: 100%;
background: #9bbef8;
}
.snake.head::after {
background: #0037ff;
}
.snake.tail::after {
background: #7400d2;
}
.food::after {
display: block;
content: '';
width: 70%;
height: 70%;
border-radius: 100%;
background: #d54830;
}
</style>
將相應的 css 類名 加在 .square 上就生成了蛇和食物了,粗暴一點偽元素可以不用,這里為了美觀一點使用 偽元素+圓角 修飾一下,
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Y7fVKbEn-1618582608058)(E:\gitStore\blog-md\純JS實作貪吃蛇——超上癮小游戲\image-20210416200314757.png)]](https://img.uj5u.com/2021/04/18/237303181157156.png)
蛇的行走 & 碰撞檢測 & 吃到食物
以上三個功能都是依據坐標來實作的,索性放在一起講
蛇的行走

就如上圖所示,實作蛇的行走其實非常簡單,這里只需要想懂一點即可:每次移動,除了蛇頭坐標(藍色圓點)是一個全新的坐標,其他圓點的坐標都是它之前的那一個坐標 ,這里蛇的原始坐標是 ['12-13', '11-13', '10-13'] ,那么它往右走的話只要將 13-13 作為蛇頭坐標即可,上一段模擬代碼:
const arr = ['12-13', '11-13', '10-13']
// 洗掉蛇尾坐標
arr.pop()
// 加入新的蛇頭坐標
arr.unshift('13-13')
// arr => ['13-13', '12-13', '11-13']
蛇尾坐標肯定要刪的,不然蛇不吃食物都能生長了,可以腦補一下畫面~
在一開始生成棋盤的時候,每個 格子(div) 都已經系結上與坐標相對應的 css類名 ,這里根據上面的坐標陣列將相應的 div 添加/移除css類名 即可,
碰撞檢測

碰撞檢測 聽起來好像 ”一頭霧水😲😲什么東東😱😱“,但是一個貪吃蛇游戲的碰撞檢測還能有多難,原理其實就是這樣:蛇只能在棋盤內活動,蛇存在的 有效坐標 也只能包含在棋盤內,這里橫縱坐標最小、最大值分別是 1 、20, 只需要判斷 蛇頭坐標 是否在 1-20 以內(包含1和20)就可以了,
吃到食物
吃到食物其實也是碰撞檢測的一種,判斷 新的蛇頭坐標 和 食物坐標 一樣即為吃到食物,并且會在當前蛇尾周圍可用坐標中隨機生成一個新的蛇尾坐標,如下圖:

當蛇頭和食物重合后,淡藍的圓點便成了新的蛇尾坐標,并且會在紅色箭頭所示的坐標中隨機選一個當做新的蛇尾坐標,需要注意的是 新的蛇尾坐標 可能會不存在,那么此時就 game over 了,
總結
更多代碼也就不貼了,以上也基本將這個小游戲的核心都講遍了,關于 模式 實則是坐標的另一種簡單運用罷了,難度 更僅僅是定時器的時間改變而已,還有很多想法后續有時間再實作吧,如下:
- 等級稱號(達成相應的分數來實作相應的稱號,比如:初出茅廬……)
- 加速功能(按住空格鍵來根據定時器的時間實作加速)
- 存檔功能(按住個什么鍵保存當前蛇的坐標、模式等資訊,存入本地快取,下次再玩)
- 周期性出現一個 大食物 一次長好幾節
- 等等等等~
原始碼
https://gitee.com/dizuncainiao/dz-greedysnake
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/277422.html
標籤:其他
上一篇:Python基礎篇:變數,串列
下一篇:DAY 1
