BLOG

【簡単ゲームプログラミング】ブロック崩しゲームをつくってみよう!-#3 ブロック崩しゲーム完成!編-【HTML・CSS・JavaScript】

2024-03-11

■過去回はこちら!
#1:ベースデザインの作成編
#2:ボールとパドルを動かそう編

どうもロックシステムのみなみです!
ブロック崩しゲームづくり3回目にして早くも最終回です(泣)

前回の動画でボールの跳ね返りとパドルの動きを設定しました。
今回はブロックの衝突検出とゲームオーバー/ゲームクリアの設定をしていきます!

今回のポイント

<ブロックの衝突検出>

まず後の処理を行いやすいようブロックを一つずつ配列に入れます。
pushを使って配列の末尾にblock要素を追加。


              arrBlock.push(block);

ブロック配列をforeachで回してその中に衝突検出の条件分岐を書いていきます。
他の衝突検出と同様ボールを反転させて、さらにブロックのstyleをnoneとすることで、ボールがブロックに当たったタイミングでブロックを消すことができます。


        // 衝突検出(ブロック)
        arrBlock.forEach(block => {
            if (block.style.display !== "none") {
                allBlocksCleared = false;
                if (
                    ballX + 20 > block.offsetLeft &&
                    ballX - 20 < block.offsetLeft + block.offsetWidth &&
                    ballY + 20 > block.offsetTop &&
                    ballY - 20 < block.offsetTop + block.offsetHeight
                ) {
                    ballSpeedY = -ballSpeedY;
                    block.style.display = "none";
                }
            }
        });

<ゲームオーバー/ゲームクリアの設定>

ボールが下の白枠に当たった時、つまりゲームオーバーとなった時にダイアログが出るようにしていきます。
ボールの衝突検出(下)にgameOver関数を追加します。


 // 衝突検出(下)
        if (ballY + 20 > 400) {
            gameOver();
            return;
        }

  function gameOver() {
        const isRestart = confirm("ゲームオーバー もう一度プレイしますか?");
        if (isRestart) {
            resetGame();
        }
    }
confirm関数はダイアログを表示させる関数です。 ()の中には表示させたいメッセージを書きます。また返却値はbool値でOK押下時→true、キャンセル押下時→falseが返却されます。
返却値にisRestartを代入。条件分岐をresetgameにしているので「OK」を選択するとゲームがもう一度最初から開始されます。

同じようにゲームクリア時にもダイアログが出るようにします。


        if (allBlocksCleared) {
            gameClear();
            return;
        }

        if (allBlocksCleared) {
            gameClear();
            return;
        }
クリア時のダイアログはalert関数を使用して、「OK」のみ出るようにしました。

今回使用したコードはこちら

・00:36~
JavaScript

document.addEventListener("DOMContentLoaded", function() {
    const gameContainer = document.querySelector(".gameContainer");
    const ball = document.querySelector(".ball");
    const paddle = document.querySelector(".paddle");
    // ブロックの行数
    const blockLine = 4;
    // 1行あたりのブロック数
    const numBlocks = 8;
    // 1行ごとのブロックカラー
    const blockColor = ["yellow", "pink", "red", "blue"];
    // ブロック(配列)
    let arrBlock = [];
    for (let i = 0; i < blockLine * numBlocks; i++) {
        const block = document.createElement("div");
        block.classList.add("block");
        gameContainer.appendChild(block);
        const row = Math.floor(i / numBlocks);
        const col = i % numBlocks;
        const colorIndex = row % blockColor.length;
        block.style.top = row * 30 + "px";
        block.style.left = col * 75 + "px";
        block.style.backgroundColor = blockColor[colorIndex];
        arrBlock.push(block);
    }
    let ballX = 500;
    let ballY = 200;
    let ballSpeedX = 2;
    let ballSpeedY = 2;
    start();
    function start() {
        ballX += ballSpeedX;
        ballY += ballSpeedY;
        ball.style.left = ballX + "px";
        ball.style.top = ballY + "px";
        let allBlocksCleared = true;
        // 衝突検出(左右)
        if (ballX < 0 || ballX + 20 > 600) {
            ballSpeedX = -ballSpeedX;
        }
        // 衝突検出(上)
        if (ballY < 0) {
            ballSpeedY = -ballSpeedY;
        }
        // 衝突検出(下)
        if (ballY + 20 > 400) {
            gameOver();
            return;
        }
        // 衝突検出(パドル)
        if (
            ballX + 20 > paddle.offsetLeft &&
            ballX - 20 < paddle.offsetLeft + paddle.offsetWidth &&
            ballY + 20 > paddle.offsetTop &&
            ballY - 20 < paddle.offsetTop + paddle.offsetHeight
        ) {
            ballSpeedY = -ballSpeedY;
        }
        // 衝突検出(ブロック)
        arrBlock.forEach(block => {
            if (block.style.display !== "none") {
                allBlocksCleared = false;
                if (
                    ballX + 20 > block.offsetLeft &&
                    ballX - 20 < block.offsetLeft + block.offsetWidth &&
                    ballY + 20 > block.offsetTop &&
                    ballY - 20 < block.offsetTop + block.offsetHeight
                ) {
                    ballSpeedY = -ballSpeedY;
                    block.style.display = "none";
                }
            }
        });
        if (allBlocksCleared) {
            gameClear();
            return;
        }
        requestAnimationFrame(start);
    }
    document.addEventListener("mousemove", function(event) {
        const mouseX = event.clientX - gameContainer.getBoundingClientRect().left;
        const paddleX = mouseX - paddle.offsetWidth / 2;
        if (paddleX < 0) {
            paddle.style.left = "0px";
        } else if(paddleX + paddle.offsetWidth > gameContainer.offsetWidth) {
            paddle.style.left = gameContainer.offsetWidth - paddle.offsetWidth + "px";
        } else {
            paddle.style.left = paddleX + "px";
        }
    })
    function gameOver() {
        const isRestart = confirm("ゲームオーバー もう一度プレイしますか?");
        if (isRestart) {
            resetGame();
        }
    }
    function gameClear() {
        alert("ゲームクリア!新たにゲームを開始しますか?");
        resetGame();
    }
    function resetGame() {
        ballX = 500;
        ballY = 200;
        ballSpeedX = 2;
        ballSpeedY = 2;
        paddle.style.left = "250px";
        arrBlock.forEach(function (block) {
            block.style.display = "block";
        })
        start();
    }
})

今回でブロック崩しゲームは完成です!!
CSSを使ってブロックの大きさ・数の変更をしたり、JavaScriptでボールのスピードを変えることで難易度の調整もできるので是非つくって遊んでみてください!

▼ブロック崩しゲームの完成コードは以下のGoogleドライブからダウンロードできます!https://drive.google.com/file/d/11X5jM4raA-PD7EtsCybsQibhn2zQy2rH/view?usp=sharing

株式会社ロックシステム

「ブラック企業をやっつけろ!!」を企業理念にエンジニアが働きやすい環境をつきつめる大阪のシステム開発会社。2014年会社設立以来、残業時間ほぼゼロを達成し、高い従業員還元率でエンジニアファーストな会社としてIT業界に蔓延るブラックなイメージをホワイトに変えられる起爆剤となるべく日々活動中!絶賛エンジニア募集中。