このサイトはアフィリエイトリンクを含んでいます

スマホで遊べるHTMLテトリスを作ろう!JavaScriptソースコード解説付き【Ver1.4】

テトリス背景HTML Javascript HTML5 CANVAS 作ってみた!

誰でも簡単に遊べる定番ゲーム「テトリス」をHTMLとJavaScriptで実装してみませんか?本記事では、スマホでも快適に遊べるHTMLテトリスのソースコードを徹底解説!実際に動くサンプルコード(Ver1.4.6)を元に、ゲームの仕組みから操作方法まで詳しく説明します。プログラミング初心者の方でも理解しやすいように、丁寧に解説しているので、ぜひチャレンジしてみてください!

あんちゃん
あんちゃん

PCブラウザ版での操作方法はこれさ↓
Qキーは左回り、Wキーは右回りでブロックが回るよ。

左右の矢印キーでブロックを左右に動かせるよ!

下矢印キーはブロックが下に落ちます~。

最初に「START」ボタンを押してね~。

HTMLテトリス Ver1.4.2 の概要

HTML テトリス Javascript TETRIS スマホ PC ブラウザ

HTMLテトリス Ver1.4.2は、HTML5とJavaScriptを使って実装された、シンプルで魅力的なテトリスゲームです。このバージョンでは、ゲーム画面の描画にCanvas要素を使用し、ゲームロジックの制御にはJavaScriptを使用しています。スマートフォンやPCブラウザで快適にプレイできるように設計されており、特にプログラミング初心者に優しい構成となっています。

このテトリスは、基本的なテトリスのルールを忠実に再現しており、ブロックが上から落ちてくる様子を視覚的に楽しめます。プレイヤーは、ブロックを左右に移動させたり、回転させたりして、隙間なくブロックを積み上げることを目指します。ラインが揃うと消えるという、テトリスならではの爽快感を提供します。

HTMLテトリス Ver1.4.2の大きな特徴は、スマホ対応の操作性にあります。スマホでは画面上のボタンをタッチすることで、ブロックを直感的に操作できます。左右移動、回転、落下といった操作は、すべてタッチで行えるため、スマホユーザーでもストレスなく楽しめます。また、PCではキーボード操作にも対応しており、矢印キーや特定のキーを使ってブロックを操作します。これにより、どのデバイスでも快適にプレイできるようになっています。

提供されるサンプルコードは、非常にシンプルで分かりやすく構成されています。各関数の役割が明確に分かれており、初めてゲーム開発に挑戦する方でも理解しやすいようになっています。コードには詳細なコメントが付けられており、どの部分がどのように機能しているのかを一つ一つ確認しながら学ぶことができます。これにより、JavaScriptやHTML5の基礎を実践的に学ぶことができます。

HTMLテトリス Ver1.4.2のもう一つの魅力は、簡単に試せる点です。記事内に掲載されているコードをコピーして自分の環境に貼り付けるだけで、すぐに動作を確認することができます。これにより、プログラミング初心者でも手軽にテトリスの開発に挑戦することができます。コードを自分で操作しながら学ぶことで、理解が深まり、スキルも自然と向上していきます。

HTMLテトリス Ver1.4.2は、プログラミングの基礎を学ぶための理想的な教材です。シンプルなコードと詳細な解説により、初心者でも無理なくゲーム開発の基本を習得できます。スマホ対応の設計と直感的な操作性が、このテトリスの大きな魅力です。

スマホ対応!HTMLとJavaScriptで作るテトリス

こちらの記事で解説するHTMLテトリスは、スマホでの操作を考慮した設計になっています。PCブラウザはもちろん、AndroidやiPhoneなどのスマートフォンでも快適にプレイできます。

シンプルなコードで分かりやすい解説

提供するサンプルコードは、分かりやすさを重視したシンプルな構成です。各関数の役割を丁寧に解説することで、HTML5ゲーム開発の基礎を学ぶことができます。

コピー&ペーストで今すぐ遊べる!

必要なコードは全て記事内に掲載しています。コードをコピー&ペーストするだけで、すぐにHTMLテトリスを体験できます。

ゲーム画面と操作方法

操作はタッチとキーボードに対応

スマホでは画面上のボタンで操作、PCではキーボードで操作できます。タッチ操作は、左右移動、回転、落下ボタンで直感的に操作可能です。キーボード操作は、矢印キーとQWキーで操作します。

スコア表示でゲームを盛り上げよう

ゲーム画面上部には、現在のスコアが表示されます。高スコアを目指して、何度もプレイしたくなる!

ゲームオーバー時のスコア表示

ゲームオーバーになると、最終スコアが表示されます。

ソースコード解説:ゲームの仕組みを理解しよう

テトリスの盤面とブロックの表現

JavaScriptの配列を使って、テトリスの盤面とブロックを表現する方法を解説。createMatrix関数でどのように盤面を生成しているのかを詳しく説明。

ブロックの移動と回転処理

playerMove関数とplayerRotate関数で、ブロックの移動と回転処理を実装。衝突判定や回転処理のアルゴリズムを分かりやすく解説。

ブロックの落下処理とラインの消去

playerDrop関数でブロックの落下処理、arenaSweep関数でラインの消去処理を実装。ゲームの進行を制御する重要な関数の仕組みを解説。

HTMLテトリス Ver1.4.2 をカスタマイズしよう!

ブロックのデザインを変更

colors配列を変更することで、ブロックの色をカスタマイズ可能。自分好みのデザインにアレンジしてみよう!

ゲームスピードの調整

dropInterval変数を変更することで、ブロックの落下速度を調整可能。より難易度を上げたい場合は、この値を小さくしてみましょう。

背景画像の変更

CSSで背景画像を設定しているので、簡単に変更できます。background-imageプロパティで好きな画像を設定してみましょう。

まとめ: HTMLテトリスでゲーム開発の第一歩を踏み出そう!

本記事で学んだこと

HTMLとJavaScriptでテトリスを実装する方法を学びました。スマホ対応のゲーム開発の基礎を理解することができました。

さらなる学習に向けて

より複雑なゲームを作るためのヒントや、関連する技術を紹介します。CanvasやWebGLを使ったゲーム開発について触れます。

楽しく学びながらスキルアップ!

実際に手を動かして、オリジナルのテトリスを作ってみましょう!

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>テトリスゲーム Ver1.4.6</title>
    <style>
        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            overflow: hidden;
            display: flex;
            flex-direction: column;
            background-image: url('https://tokodomo.xyz/wp-content/uploads/2024/08/tetris_ver1.4_background.webp');
            background-size: cover;
            background-position: center;
        }
        #gameContainer {
            position: relative;
            width: 100%;
            flex-grow: 1;
            display: flex;
            justify-content: center;
            align-items: center;
            background-color: rgba(0, 0, 0, 0.7);
            z-index: 5;
        }
        #game {
            background-color: #000;
        }
        #scoreContainer {
            font-size: 20px;
            background-color: rgba(255, 255, 255, 0.7);
            padding: 10px;
            position: fixed;
            top: 0;
            width: 100%;
            display: flex;
            justify-content: space-between;
            box-sizing: border-box;
            z-index: 10;
        }
        #finalScore {
            font-size: 30px;
            color: red;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background-color: rgba(255, 255, 255, 0.7);
            padding: 10px;
            display: none;
            width: 100%;
            text-align: center;
        }
        #controls {
            position: fixed;
            bottom: 0;
            width: 100%;
            display: flex;
            flex-direction: row;
            justify-content: space-between;
            align-items: center;
            padding: 10px 20px;
            box-sizing: border-box;
            z-index: 10;
            background-color: rgba(0, 0, 0, 0.5);
        }
        .control-button {
            width: 60px;
            height: 60px;
            background-color: rgba(255, 255, 255, 0.7);
            border: none;
            border-radius: 50%;
            font-size: 20px;
            font-weight: bold;
            color: black;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        #moveControl {
            width: 140px;
            display: flex;
            justify-content: space-between;
        }
        .connected-button {
            border-radius: 0;
            flex-grow: 1;
            margin: 0;
        }
        .connected-button:first-child {
            border-top-left-radius: 50%;
            border-bottom-left-radius: 50%;
        }
        .connected-button:last-child {
            border-top-right-radius: 50%;
            border-bottom-right-radius: 50%;
        }
        @media (min-width: 601px) {
            #controls {
                display: none;
            }
        }
    </style>
</head>
<body>
    <div id="scoreContainer">
        <div id="score">Score: 0</div>
        <button id="startButton" onclick="startGame()">START</button>
    </div>
    <div id="gameContainer">
        <canvas id="game" width="240" height="400"></canvas>
        <div id="finalScore">GAME OVER<br>SCORE: 0</div>
    </div>
    <div id="controls">
        <div id="moveControl">
            <button class="control-button connected-button" id="leftButton">&larr;</button>
            <button class="control-button connected-button" id="rightButton">&rarr;</button>
        </div>
        <button class="control-button" id="rotateButton">&#x21BB;</button>
        <button class="control-button" id="dropButton">&#x2B07;</button>
    </div>
    <script>
        const canvas = document.getElementById('game');
        const context = canvas.getContext('2d');
        const scale = 20;
        context.scale(scale, scale);

        const arena = createMatrix(12, 20);

        const player = {
            pos: {x: 0, y: 0},
            matrix: null,
            score: 0,
        };

        const colors = [
            null,
            '#FF0D72',
            '#0DC2FF',
            '#0DFF72',
            '#F538FF',
            '#FF8E0D',
            '#FFE138',
            '#3877FF',
        ];

        let gameOver = false;
        let dropStart = false;
        let dropSpeed = 50;

        function createMatrix(w, h) {
            const matrix = [];
            while (h--) {
                matrix.push(new Array(w).fill(0));
            }
            return matrix;
        }

        function createPiece(type) {
            if (type === 'T') {
                return [
                    [0, 0, 0],
                    [1, 1, 1],
                    [0, 1, 0],
                ];
            } else if (type === 'O') {
                return [
                    [2, 2],
                    [2, 2],
                ];
            } else if (type === 'L') {
                return [
                    [0, 3, 0],
                    [0, 3, 0],
                    [0, 3, 3],
                ];
            } else if (type === 'J') {
                return [
                    [0, 4, 0],
                    [0, 4, 0],
                    [4, 4, 0],
                ];
            } else if (type === 'I') {
                return [
                    [0, 5, 0, 0],
                    [0, 5, 0, 0],
                    [0, 5, 0, 0],
                    [0, 5, 0, 0],
                ];
            } else if (type === 'S') {
                return [
                    [0, 6, 6],
                    [6, 6, 0],
                    [0, 0, 0],
                ];
            } else if (type === 'Z') {
                return [
                    [7, 7, 0],
                    [0, 7, 7],
                    [0, 0, 0],
                ];
            }
        }

        function drawMatrix(matrix, offset) {
            matrix.forEach((row, y) => {
                row.forEach((value, x) => {
                    if (value !== 0) {
                        context.fillStyle = colors[value];
                        context.fillRect(x + offset.x, y + offset.y, 1, 1);
                    }
                });
            });
        }

        function draw() {
            context.fillStyle = '#000';
            context.fillRect(0, 0, canvas.width / scale, canvas.height / scale);

            const xOffset = (canvas.width / scale - arena[0].length) / 2;
            drawMatrix(arena, {x: xOffset, y: 0});
            drawMatrix(player.matrix, {x: player.pos.x + xOffset, y: player.pos.y});
        }

        function merge(arena, player) {
            player.matrix.forEach((row, y) => {
                row.forEach((value, x) => {
                    if (value !== 0) {
                        arena[y + player.pos.y][x + player.pos.x] = value;
                    }
                });
            });
        }

        function rotate(matrix, dir) {
            for (let y = 0; y < matrix.length; ++y) {
                for (let x = 0; x < y; ++x) {
                    [matrix[x][y], matrix[y][x]] = [matrix[y][x], matrix[x][y]];
                }
            }

            if (dir > 0) {
                matrix.forEach(row => row.reverse());
            } else {
                matrix.reverse();
            }
        }

        function playerDrop() {
            player.pos.y++;
            if (collide(arena, player)) {
                player.pos.y--;
                merge(arena, player);
                playerReset();
                arenaSweep();
                updateScore();
                if (collide(arena, player)) {
                    gameOver = true;
                    displayFinalScore();
                }
            }
            dropCounter = 0;
        }

        function playerMove(offset) {
            player.pos.x += offset;
            if (collide(arena, player)) {
                player.pos.x -= offset;
            }
        }

        function playerReset() {
            const pieces = 'TJLOSZI';
            player.matrix = createPiece(pieces[pieces.length * Math.random() | 0]);
            player.pos.y = 0;
            player.pos.x = (arena[0].length / 2 | 0) - (player.matrix[0].length / 2 | 0);
            if (collide(arena, player)) {
                gameOver = true;
                displayFinalScore();
            }
        }

        function playerRotate(dir) {
            const pos = player.pos.x;
            let offset = 1;
            rotate(player.matrix, dir);
            while (collide(arena, player)) {
                player.pos.x += offset;
                offset = -(offset + (offset > 0 ? 1 : -1));
                if (offset > player.matrix[0].length) {
                    rotate(player.matrix, -dir);
                    player.pos.x = pos;
                    return;
                }
            }
        }

        function collide(arena, player) {
            const [m, o] = [player.matrix, player.pos];
            for (let y = 0; y < m.length; ++y) {
                for (let x = 0; x < m[y].length; ++x) {
                    if (m[y][x] !== 0 &&
                        (arena[y + o.y] &&
                        arena[y + o.y][x + o.x]) !== 0) {
                        return true;
                    }
                }
            }
            return false;
        }

        function arenaSweep() {
            outer: for (let y = arena.length - 1; y > 0; --y) {
                for (let x = 0; x < arena[y].length; ++x) {
                    if (arena[y][x] === 0) {
                        continue outer;
                    }
                }

                const row = arena.splice(y, 1)[0].fill(0);
                arena.unshift(row);
                ++y;

                player.score += 10;
            }
        }

        let dropCounter = 0;
        let dropInterval = 1000;

        let lastTime = 0;
        function update(time = 0) {
            if (!gameOver) {
                const deltaTime = time - lastTime;

                dropCounter += deltaTime;
                if (dropCounter > (dropStart ? dropSpeed : dropInterval)) {
                    playerDrop();
                }

                lastTime = time;

                draw();
                requestAnimationFrame(update);
            }
        }

        function updateScore() {
            document.getElementById('score').innerText = `Score: ${player.score}`;
            document.getElementById('finalScore').innerText = `GAME OVER\nSCORE: ${player.score}`;
        }

        document.addEventListener('keydown', event => {
            if (event.keyCode === 37) {
                playerMove(-1);
            } else if (event.keyCode === 39) {
                playerMove(1);
            } else if (event.keyCode === 40) {
                event.preventDefault(); // ページのスクロールを防ぐ
                playerDrop();
            } else if (event.keyCode === 81) {
                playerRotate(-1);
            } else if (event.keyCode === 87) {
                playerRotate(1);
            }
        });

        // ボタンのイベントリスナー
        document.getElementById('leftButton').addEventListener('click', () => playerMove(-1));
        document.getElementById('rightButton').addEventListener('click', () => playerMove(1));
        document.getElementById('rotateButton').addEventListener('click', () => playerRotate(1));
        
        // 長押しでの落下速度の調整
        document.getElementById('dropButton').addEventListener('mousedown', (event) => {
            event.preventDefault();
            dropStart = true;
        });

        document.getElementById('dropButton').addEventListener('mouseup', (event) => {
            event.preventDefault();
            dropStart = false;
        });

        document.getElementById('dropButton').addEventListener('touchstart', (event) => {
            event.preventDefault();
            dropStart = true;
        });

        document.getElementById('dropButton').addEventListener('touchend', (event) => {
            event.preventDefault();
            dropStart = false;
        });

        function startGame() {
            arena.forEach(row => row.fill(0));
            player.score = 0;
            gameOver = false;
            document.getElementById('finalScore').style.display = "none";
            playerReset();
            updateScore();
            update();
        }

        function displayFinalScore() {
            document.getElementById('finalScore').style.display = "block";
            document.getElementById('finalScore').innerText = `GAME OVER\nSCORE: ${player.score}`;
        }

        playerReset();
        updateScore();
    </script>
</body>
</html>

コメント

タイトルとURLをコピーしました