iT邦幫忙

2021 iThome 鐵人賽

DAY 13
2

截圖

前言:

昨天介紹完了一些簡單的應用,今天阿森想用JavaScript來寫一個貪吃蛇的網頁遊戲,相信透過這個過程大家可以更清楚JavaScript的使用邏輯。

那我們就開始吧!

HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="./style.css">
    <title>Document</title>
</head>
<body>
    <h1>貪吃蛇</h1>
    <canvas id = "game" width="400" height="400"></canvas>
		<h2>使用上下左右來控制!</h2>
    <h2>先得25分就贏了!</h2>
    <script src="app.js"></script>
</body>
</html>

HTML的部分我們就簡單的使用一個h1當標題,再用canvas這個tag當作遊戲畫面。

所以只要這樣HTML的部分就完成囉。

CSS:

再來我們幫這個頁面做一點美化:

body {
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}

canvas{
    box-shadow: black 10px 10px 50px;
}

這時我們的頁面會變成這樣:

截圖

可以看到東西都置中了,而且遊戲畫面也多了陰影。

JavaScript:

接下來就是重頭戲了,首先我們主要的遊戲架構是這樣:

function startGame() {
    snakePosition();
    let lose = isOver();
    if(lose){
        document.body.addEventListener('keydown', playAgain);
        return;
    }
    clearScreen();

    checkColli();
    let win = isWin();
    if(win){
        return;
    }
    drawApple();
    drawSnake();
    drawScore();
    
    setSpeed();
    
    setTimeout(startGame, 1000/speed);
}

依序是

snakePosition-負責管理與調整snake的位置

isOver-確認遊戲結束了沒

playAgain-確認是否再玩一次

clearScreen-初始化遊戲畫面

checkColli-確認蛇和蘋果的碰撞

isWin-確認勝利條件

drawApple-生產蘋果方塊

drawSnake-生產蛇方塊

drawScore-顯示分數

setSpeed-更改速度

setTimeout-重複跑上述內容

接下來是一開始的DOM操作:

const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');

先抓到id為game的canvas後,設定他的Context為ctx。

再來是一些遊戲常數設定:

class SnakePart{
    constructor(x, y){
        this.x = x;
        this.y = y;
    }
}

let speed = 8;

let tileCount = 20;
let tileSize = canvas.width / tileCount - 2;
let headX = 10;
let headY = 10;
const snakePart = [];
let tailLen = 0;

let appleX = 5;
let appleY = 5;

let xV = 0;
let yV = 0;

let score = 0;

這裡我們設定這張地圖為20 * 20大小的方格地圖,每個方格大小是canvas總寬度除以20再減2,這樣可以讓每個方格隔開一點空間,跑起來也比較好看。

再來我們設定一個class叫snakePart,如果你沒有學過class的概念,你只要想像他是一塊模板,透過宣告一個東西是這個class type我們可以建構出很多內容結構一樣的物件。

我們也透過head x 跟 y 設定蛇一開始的位置為(10, 10),蘋果一開始的位置為(5, 5),蛇長度一開始為0,x軸和y軸方向速度皆為0,分數也為0。

再來我們一個一個介紹function:

snakePosition:

function snakePosition() {
    headX = headX + xV;
    headY = headY + yV;
}

位置和速度的加成為新的位置。

isOver:

function isOver() {
    let Over = false;
    if(headX < 0 || headX == 20 || headY < 0 || headY == 20){
        Over = true;
    }
    for(let i = 0; i < snakePart.length; i++){
        if(headX == snakePart[i].x && headY == snakePart[i].y){
            Over = true;
        }
    }
    if(Over){
        ctx.fillStyle = "white";
        ctx.font = "50px Poppins";
        ctx.fillText("Game Over!", canvas.width/6.5, canvas.height /2);
        ctx.font = "40px Poppins";
        ctx.fillText("再玩一次?", canvas.width/3.5, canvas.height /2 + 50 );
        ctx.font = "25px Poppins";
        ctx.fillText("按空白鍵", canvas.width/2.7, canvas.height /2 +100 );
    }
    return Over;
}

如果蛇頭跑到了地圖的邊界,或是撞上自己的身體,則回傳Over = true,遊戲結束。

playAgain:

function playAgain(event) {
    if(event.keyCode == 32){
        location.reload();
    }
}

如果按下空白鍵則重新載入這個頁面,讓遊戲再跑一次。

clearScreen:

function clearScreen() {
    ctx.fillStyle= 'black';
    ctx.fillRect(0, 0, 400, 400);
}

把背景設為黑色。

checkColli:

function checkColli() {
    if(appleX === headX && appleY === headY){
        appleX = Math.floor(Math.random() * tileCount);
        appleY = Math.floor(Math.random() * tileCount);
        tailLen ++;
        score ++;
        if(score > 5 && score % 2 == 0){
            speed ++;
        }
    }
}

如果蛇和蘋果碰撞,則分數和長度皆加一,再如果分數大於5,每到達偶數分速度就加一。

isWin:

function isWin() {
    let win = false;
    if(score == 25){
        win = true;
    }
    if(win){
        ctx.fillStyle = "white";
        ctx.font = "50px Poppins";
        ctx.fillText("你贏了!", canvas.width/3.3, canvas.height /2)
    }
    return win;
}

當得到25分時會出現"你贏了!"的字樣,同時回傳win = true。

drawApple:

function drawApple() {
    ctx.fillStyle = "red";
    ctx.fillRect(appleX * tileCount, appleY * tileCount, tileSize, tileSize);
}

設定頻果為紅色,還有設定size。

drawSnake:

function drawSnake() {
    
    ctx.fillStyle = "green";
    for(let i = 0; i< snakePart.length; i++){
        let part = snakePart[i];
        ctx.fillRect(part.x * tileCount, part.y * tileCount, tileSize, tileSize);
    }

    snakePart.push( new SnakePart(headX, headY));
    if(snakePart.length > tailLen){
        snakePart.shift();
    }

    ctx.fillStyle = 'orange';
    ctx.fillRect(headX * tileCount, headY *tileCount, tileSize, tileSize);
}

負責畫出蛇的身體,並把他接在身上!

drawScore:

function drawScore() {
    ctx.fillStyle = "white";
    ctx.font = "10px Poppins";
    ctx.fillText("Score: " + score, canvas.width-50, 10);
}

設定字顏色、字體和位置等。

setSpeed:

function setSpeed() {
        if(score == 5){
            speed = 10;
        }    
}

這裡我設定在得到五分的時候會進行一個提速。

再來我們要在最下面寫幾個負責偵測按鍵的Listener:

document.body.addEventListener('keydown', keyDown);

function keyDown(event) {

    //go up
    if(event.keyCode== 38){
        if(yV == 1) 
            return;
        yV = -1;
        xV = 0;
    }

    //go down
    if(event.keyCode == 40){
        if(yV == -1) 
            return;
        yV = 1;
        xV = 0;
    }

    //go left
    if(event.keyCode == 37){
        if(xV == 1) 
            return;
        yV = 0;
        xV = -1;
    }

    //go right
    if(event.keyCode == 39){
        if(xV == -1) 
            return;
        yV = 0;
        xV = 1;
    }
}
function playAgain(event) {
    if(event.keyCode == 32){
        location.reload();
    }
}

這樣就可以透過上下左右來改變方向了!

最後記得在最下面加上:

startGame();

這樣我們的貪吃蛇遊戲就大功告成了!

想試玩或想做比對的可以到這裡:

https://cooksen.github.io/eating-snake.github.io/

小結:

今天我們透過JavaScript和DOM寫出了一個簡單的小遊戲,當然這個遊戲還有很多可以加強的地方,像是難度選擇、計分板、客製化顏色等等。可是從今天的進度來看,JavaScript只需要短短的100行就可以寫出這樣一個好玩的小遊戲,可見他真的是很強大的一個程式語言呢!

接下來阿森要介紹的是一個由JavaScript延伸出來的一個非常厲害的工具,沒錯就是React!講了這麼久終於要講到題目裡的內容了,希望接下來大家可以運用這些前面學到的概念,應用在React的實際操作上,跟阿森一起繼續變強吧!


上一篇
DAY12-JavaScript(二)
下一篇
DAY14-React Overview
系列文
1995到2021,php到react網站開發歷程30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
Oo_花之舞__oO
iT邦新手 1 級 ‧ 2022-05-29 21:54:04

很棒的教學 可是玩起來有BUG...

我要留言

立即登入留言