(3) JS實作系列 貪吃蛇

snake-game

Demo 連結
程式碼

這次要來實作貪吃蛇小遊戲,因為程式較多,會將JS檔案拆分再用export 、import來匯入和匯出,方便做程式碼管理。

繪製網格

在遊戲畫面中的格子,我們可以用 grid 來繪製。用 grid 製作的好處是方便我們在js中取得每個格子的座標將蛇繪製到網格上,網格數量則使用css變數來帶入。

1
2
3
4
5
6
7
8
9
10
11
12
.game-board {
--col-num: 28;
--row-num: 16;
width: 100%;
height: 640px;
background-color: var(--color-primary);
border: 1px solid var(--color-primary);
display: grid;
gap: 1px;
grid-template-columns: repeat(var(--col-num), 1fr);
grid-template-rows: repeat(var(--row-num), 1fr);
}

建立動畫 loop 函式

要讓貪吃蛇不停地在畫面上移動,我們需要一個可以每秒更新的函式,以往可以用 setInterval來達成。但 setInterval 可能會有時間誤差的問題,所以我們改用 requestAnimationFrame 來製作。

1
2
3
4
5
6
7
8
9
10
11
12
13
let lastRenderTime = 0;
let speed = 2;
function animate(currentTime) {
const currentPage = getCurrentPage();
const requestId = window.requestAnimationFrame(animate);
const secondSinceLastRender = (currentTime - lastRenderTime) / 1000; // 將milisecond 轉成 second
if (secondSinceLastRender < 1 / speed) return;
lastRenderTime = currentTime;
update();
draw();
if (currentPage !== 'game-page') cancelAnimationFrame(requestId);
}
window.requestAnimationFrame(animate);

requestAnimationFrame 會在我們設定的時間到後自動執行函式,讓貪吃蛇能持續移動。

繪製貪吃蛇

貪吃蛇的身體是由不同格子組成,我們可以用物件帶入x 跟y的值,代表該節身體的位置。之後我們在操作蛇的移動和增加身體時,都是在操作這個陣列。

1
2
3
4
5
let snakeBody = [
{ x: 14, y: 6 },
{ x: 14, y: 7 },
{ x: 14, y: 8 },
];

移動身體的部分,概念是將每節身體的位置,設定成上一節身體的位置,這樣每次繪製影格時,蛇就會移動。

1
2
3
4
5
for (let i = snakeBody.length - 2; i >= 0; i--) {
snakeBody[i + 1] = { ...snakeBody[i] }; // 設定最後一個的元素變成倒數第二的元素
}
snakeBody[0].x += direction.x;
snakeBody[0].y += direction.y;

那有了蛇的 x y 座標後,剩下的就是在html中將對應的座標選起來,並加上對應的class。
如蛇的頭部 index 是 0,就加上head的class。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export function drawSnake(gameBoard, boardSet) {
const gridItems = document.querySelectorAll('.grid-item');
gridItems.forEach((item) => item.className = 'grid-item');
snakeBody.forEach((body, index) => {
const gridItem = getGridItemByXY({ gameBoard, boardSet, postion: body });
gridItem.className = 'grid-item';
if (index === 0) gridItem.classList.add('head');
if (index === 1) gridItem.classList.add('gray-100');
if (index === 2) gridItem.classList.add('gray-200');
if (index === 3) gridItem.classList.add('gray-300');
if (index === 4) gridItem.classList.add('gray-400');
if (index > 4) gridItem.classList.add('body');
})
}

以上就是製作的貪吃蛇遊戲的方法囉。