iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 11
0
Software Development

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

[Day11]用Canvas打造自己的遊樂場-BB 磚塊、陣列、補班日進度少一點

  • 分享至 

  • xImage
  •  

悲傷的補班日,身心疲憊,今天內容就寫少一點,請見諒~~

今天呢,要畫出方塊,單畫一個方塊之前老早就做過了,現在要一次畫出遊戲中所需要的方塊.
老樣子,第一步先來把參數設定一下

// 磚參數
const BRICK_WIDTH = 80;
const BRICK_HEIGHT = 30;
const BRICK_GAP = 2;

分別是專的寬跟高還有磚與磚之間的距離.
如果遊戲中的每一個方塊要分開畫,太麻煩了,而且後面要做到被球碰觸後消失,會很麻煩.
這邊用陣列的方式來顯示方塊,我希望畫面中呈現10*9的方塊陣列,上面三行不要顯示.方塊顯示的條件就是,對應位置上的陣列數值如果是1,則顯示.
就先來寫個函式來初始化方塊矩陣吧,之後也能用在遊戲結束的時候

先加入一些要用到的參數

// 磚參數
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;

我們希望的行數還有列數,還有剩餘的磚塊數.
重置函式

 // 重置磚
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');
            }
        }
    }
}

先暫時加入重置磚塊的函式,之後會改位置

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

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

    // 如果沒磚塊重置,暫時放這
    if (bricks_left == 0) {
            resetBrick();
    }

    canvas.addEventListener('mousemove', mousePos);
}

當然也要補上顯示畫面的方程式

// 負責畫畫
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, 'wight');

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

    // 畫出生命
    showLife(chance);

    // 畫磚
    showBricks();

}

好啦~現在畫面應該能正常顯示了

今天就先到這吧,一週上六天班真的讓人疲憊~~~ 週休二日真的重要!!!

最後老樣子附上所有的程式

<!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 = 10;
        var ball_speed = 8;
        var ball_speed_x = 5;
        // 計算Y軸球速
        countBallSpeedY = (speed, speedX) => {
            return (speed ** 2 - speedX ** 2) ** 0.5;
        }
        var ball_speed_y = countBallSpeedY(ball_speed, ball_speed_x);

        // 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);

            // 如果沒磚塊重置,暫時放這
            if (bricks_left == 0) {
                    resetBrick();
            }

            canvas.addEventListener('mousemove', mousePos);
        }

        // 負責更新畫面
        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, 'wight');

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

            // 畫出生命
            showLife(chance);

            // 畫磚
            showBricks();

        }

        // 負責處理動作
        move = () => {
            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四個角的座標
            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) {

                    // 加速
                    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);
            }
        }

        // 矩形元件
        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');
                    }
                }
            }
        }

        

    </script>
</body>

</html>

上一篇
[Day10]用Canvas打造自己的遊樂場-遊戲版面、剩餘生命
下一篇
[Day12]用Canvas打造自己的遊樂場-BB 消滅磚塊
系列文
用Canvas打造自己的遊樂場30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言