iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 13
0
Software Development

用Canvas打造自己的遊樂場系列 第 13

[Day13]用Canvas打造自己的遊樂場-BB 收尾

  • 分享至 

  • xImage
  •  

今天要來把第一個遊戲“Brick Breaker”做個收尾。
第一件事情是處理原先resetBrick放的位置,原本是放在window.onload中,修改成下面這樣

window.onload = () => {
    canvas = document.getElementById('playground');
    canvasContext = canvas.getContext('2d');

    //一秒更新幾次畫面
    var timesPerSec = 30;
    setInterval(drawAll, 1000 / timesPerSec);

    canvas.addEventListener('mousemove', mousePos);

    resetBrick();
}

做一次resetBrick,來畫出第一次對舉陣陣列.

昨天的遊戲如果正常執行會遇到一個問題,當所有磚塊被消除時,磚塊並不會重置,這是因為檢查的if條件並沒有寫在我們定期執行的函式drawall中,所以只有第一次進入遊戲時會執行,要解決這問題,我嗎可已把判斷式移到跟paddle互動觸發時.

// paddle互動
actionPaddle = () => {
    // 定義paddle四個角的座標
    var paddleTopEdgeY = canvas.height - PADDLE_HEIGHT - PADDLE_HIGH;
    var paddleBottomEdgeY = paddleTopEdgeY + PADDLE_HEIGHT;
    var paddleLeftEdgeX = paddle_x;
    var paddleRightEdgeX = paddleLeftEdgeX + PADDLE_WIDTH;

    // 碰到paddle反彈
    if (ball_y > (paddleTopEdgeY - ball_r) &&
        ball_y < paddleBottomEdgeY &&
        ball_x > paddleLeftEdgeX &&
        ball_x < paddleRightEdgeX) {

        // 如果沒磚塊重置
        if (bricks_left == 0) {
            resetBrick();
        }
        // 加速
        ball_speed += 0.5;

        // paddle中心
        var centerOfPaddleX = paddle_x + PADDLE_WIDTH / 2;

        // 計算X軸球速
        ball_speed_x = ball_speed * ((ball_x - centerOfPaddleX) / (0.51 * PADDLE_WIDTH));

        // 計算Y軸球速
        ball_speed_y = -1 * countBallSpeedY(ball_speed, ball_speed_x);
    }
}

好啦~ 稍微整理一下程式排版,我們第一個遊戲算是完成了.總共花了八天的時間,比我想得快多了,看來有望在這30天完成3個遊戲左右.
繼續加油吧,以下附上完整的code

<!DOCTYPE html>
<html lang="en">

<head>
    <title>First Game</title>
    <meta name="description" content="第一個遊戲">
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
    <meta content="utf-8" http-equiv="encoding">
</head>

<body>
    <canvas id="playground" width="800" height="630"></canvas>
    <script>
        var canvas, canvasContext;

        // 球的參數
        var ball_x = 300;
        var ball_y = 300;
        var ball_r = 8;
        var ball_speed = 5;
        var ball_speed_x = 4;
        var ball_speed_y = 3;

        // paddle參數
        const PADDLE_WIDTH = 100;
        const PADDLE_HEIGHT = 10;
        const PADDLE_HIGH = 50; // paddle 距離底部高度
        var paddle_x = 400;

        // 磚參數
        const BRICK_WIDTH = 80;
        const BRICK_HEIGHT = 30;
        const BRICK_GAP = 2;
        const BRICK_COLS = 10;
        const BRICK_ROWS = 9;
        var brick_grid = new Array(BRICK_COLS * BRICK_ROWS);
        var bricks_left = 0;

        // 生命設定
        const LIFE_SIZE = 30;
        var chance = 3;

        window.onload = () => {
            canvas = document.getElementById('playground');
            canvasContext = canvas.getContext('2d');

            //一秒更新幾次畫面
            var timesPerSec = 30;
            setInterval(drawAll, 1000 / timesPerSec);

            canvas.addEventListener('mousemove', mousePos);

            resetBrick();
        }

        // 負責更新畫面
        drawAll = () => {
            move();
            draw();
        }

        // 負責畫畫
        draw = () => {
            // background
            drawRectangle(0, 0, canvas.width, canvas.height, 'black');

            // paddle
            drawRectangle(paddle_x, canvas.height - PADDLE_HEIGHT - PADDLE_HIGH,
                PADDLE_WIDTH, PADDLE_HEIGHT, 'white');

            // 畫球
            drawCircle(ball_x, ball_y, ball_r, 'red');

            // 畫線
            drawLine(0, LIFE_SIZE, canvas.width, LIFE_SIZE, 'white')

            // 畫出生命
            showLife(chance);

            // 畫磚
            showBricks();

        }

        // 負責處理動作
        move = () => {

            // 處理球的動作
            moveBall()

            // paddle互動
            actionPaddle()

            // 磚互動
            actionBrick()

        }

        // 球移動
        moveBall = () => {
            ball_x += ball_speed_x;
            ball_y += ball_speed_y;

            // 碰觸邊界動作
            if (ball_x < (0 + ball_r)) {
                ball_speed_x *= -1;
            }
            if (ball_x > (canvas.width - ball_r)) {
                ball_speed_x *= -1;
            }
            if (ball_y < (LIFE_SIZE + ball_r)) {
                ball_speed_y *= -1;
            }
            if (ball_y > canvas.height) {
                resetBall();
                chance -= 1;

                if (chance < 0) {
                    chance = 3;
                }
            }
        }

        // paddle互動
        actionPaddle = () => {
            // 定義paddle四個角的座標
            var paddleTopEdgeY = canvas.height - PADDLE_HEIGHT - PADDLE_HIGH;
            var paddleBottomEdgeY = paddleTopEdgeY + PADDLE_HEIGHT;
            var paddleLeftEdgeX = paddle_x;
            var paddleRightEdgeX = paddleLeftEdgeX + PADDLE_WIDTH;

            // 碰到paddle反彈
            if (ball_y > (paddleTopEdgeY - ball_r) &&
                ball_y < paddleBottomEdgeY &&
                ball_x > paddleLeftEdgeX &&
                ball_x < paddleRightEdgeX) {

                // 如果沒磚塊重置
                if (bricks_left == 0) {
                    resetBrick();
                }
                // 加速
                ball_speed += 0.5;

                // paddle中心
                var centerOfPaddleX = paddle_x + PADDLE_WIDTH / 2;

                // 計算X軸球速
                ball_speed_x = ball_speed * ((ball_x - centerOfPaddleX) / (0.51 * PADDLE_WIDTH));

                // 計算Y軸球速
                ball_speed_y = -1 * countBallSpeedY(ball_speed, ball_speed_x);
            }
        }

        //確認磚塊是不是存在
        isBrickExist = (col, row) => {
            if (col >= 0 && col < BRICK_COLS &&
                row >= 0 && row < BRICK_ROWS) {
                var brickIndexUnderCoord = rowColToArrayIndex(col, row);
                return brick_grid[brickIndexUnderCoord];
            } else {
                return false;
            }
        }

        // 碰到磚的動作
        actionBrick = () => {
            // 球的座標轉換成磚塊的陣列位置
            var ballBrickCol = Math.floor(ball_x / BRICK_WIDTH);
            var ballBrickRow = Math.floor(ball_y / BRICK_HEIGHT);
            var brickIndexUnderBall = rowColToArrayIndex(ballBrickCol, ballBrickRow);

            // 判斷球的位置是否在我們設計有磚塊的區域內
            if (ballBrickCol >= 0 && ballBrickCol < BRICK_COLS &&
                ballBrickRow >= 0 && ballBrickRow < BRICK_ROWS) {
                // 確認該位置的磚塊在不在
                if (isBrickExist(ballBrickCol, ballBrickRow)) {
                    // 消除方塊
                    brick_grid[brickIndexUnderBall] = false;

                    // 剩餘磚塊的數量減一
                    bricks_left--;
                    console.log(bricks_left);

                    // 方便判斷撞擊位置的參數
                    var prevBallX = ball_x - ball_speed_x;
                    var prevBallY = ball_y - ball_speed_y;
                    var prevBrickCol = Math.floor(prevBallX / BRICK_WIDTH);
                    var prevBrickRow = Math.floor(prevBallY / BRICK_HEIGHT);

                    // 預設非側邊或上下面撞擊
                    var bothTestFailed = true;

                    // 側邊撞擊
                    if (prevBrickCol != ballBrickCol) {

                        if (isBrickExist(ballBrickCol, ballBrickRow) == false) {
                            ball_speed_x *= -1;
                            bothTestFailed = false;
                        }
                    }

                    // 上下面撞擊
                    if (prevBrickRow != ballBrickRow) {

                        if (isBrickExist(ballBrickCol, ballBrickRow) == false) {
                            ball_speed_y *= -1;
                            bothTestFailed = false;
                        }
                    }

                    // 撞到角的情況
                    if (bothTestFailed) {
                        ball_speed_x *= -1;
                        ball_speed_y *= -1;
                    }
                }
            }
        }

        // 矩形元件
        drawRectangle = (topLeftX, topLeftY, boxWidth, boxHeight, color) => {
            canvasContext.fillStyle = color;
            canvasContext.fillRect(topLeftX, topLeftY, boxWidth, boxHeight);
        }

        // 圓形元件
        drawCircle = (centerX, centerY, r, color) => {
            canvasContext.fillStyle = color;
            canvasContext.beginPath();
            canvasContext.arc(centerX, centerY, r, 0, Math.PI * 2);
            canvasContext.fill();
        }

        // 畫線
        drawLine = (beginX, beginY, endX, endY, color) => {
            canvasContext.strokeStyle = color;
            canvasContext.beginPath();
            canvasContext.moveTo(beginX, beginY);
            canvasContext.lineTo(endX, endY);
            canvasContext.stroke();
        }

        // 畫生命
        drawLife = (lifeX, lifeY, size, color) => {
            canvasContext.fillStyle = color;
            canvasContext.beginPath();
            canvasContext.arc(lifeX + size / 4, lifeY + size / 4, size / 4, Math.PI * 1, Math.PI * 0);
            canvasContext.arc(lifeX + size * 3 / 4, lifeY + size / 4, size / 4, Math.PI * 1, Math.PI * 0);
            canvasContext.moveTo(lifeX + size, lifeY + size / 4);
            canvasContext.lineTo(lifeX + size / 2, lifeY + size);
            canvasContext.lineTo(lifeX, lifeY + size / 4);
            canvasContext.fill();
        }

        // 畫出生命
        showLife = (num) => {
            for (let life_count = 0; life_count < num; life_count++) {
                drawLife(LIFE_SIZE * life_count, 0, LIFE_SIZE, 'red');
            }
        }

        // 游標位置
        mousePos = (event) => {
            var rect = canvas.getBoundingClientRect();
            var root = document.documentElement;

            var mouse_x = event.clientX - rect.left - root.scrollLeft;

            // 設定paddle控制點在中央
            paddle_x = mouse_x - PADDLE_WIDTH / 2;
        }

        // 重置球
        resetBall = () => {
            ball_x = 300;
            ball_y = 300;
            ball_speed = 8;
            ball_speed_x = 5;
            ball_speed_y = countBallSpeedY(ball_speed, ball_speed_x);
        }

        // 重置磚
        resetBrick = () => {
            bricks_left = 0;
            var i;
            for (i = 0; i < 3 * BRICK_COLS; i++) {
                brick_grid[i] = false;
            }
            for (; i < BRICK_COLS * BRICK_ROWS; i++) {
                brick_grid[i] = true;
                bricks_left++;
            }
        }

        // 轉換磚塊位置
        rowColToArrayIndex = (col, row) => {
            return col + BRICK_COLS * row;
        }

        // 顯示磚塊
        showBricks = () => {
            for (var count_rows = 0; count_rows < BRICK_ROWS; count_rows++) {
                for (var count_cols = 0; count_cols < BRICK_COLS; count_cols++) {

                    var arrayIndex = rowColToArrayIndex(count_cols, count_rows);

                    if (brick_grid[arrayIndex]) {
                        drawRectangle(BRICK_WIDTH * count_cols, BRICK_HEIGHT * count_rows, BRICK_WIDTH - BRICK_GAP, BRICK_HEIGHT - BRICK_GAP, 'blue');
                    }
                }
            }
        }

        // 計算Y軸球速
        countBallSpeedY = (speed, speedX) => {
            return (speed ** 2 - speedX ** 2) ** 0.5;
        }

    </script>
</body>

</html>

上一篇
[Day12]用Canvas打造自己的遊樂場-BB 消滅磚塊
下一篇
[Day14]用Canvas打造自己的遊樂場-Galaxian 開工
系列文
用Canvas打造自己的遊樂場30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言