0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

テトリス作ってみた

Posted at

テトリスつくってみた

index.htm
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>簡易テトリス</title>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            flex-direction: column;
            background-color: #2c3e50;
            color: #ecf0f1;
            font-family: monospace;
            padding: 20px;
        }
        canvas {
            border: 2px solid #ecf0f1;
            background-color: #34495e;
            touch-action: none; /* タッチ操作でのスクロールを防止 */
        }
        .controls {
            margin-top: 20px;
            display: grid;
            grid-template-areas:
                ". rotate ."
                "left . right"
                ". drop .";
            gap: 10px;
            width: 200px;
        }
        .control-button {
            padding: 15px 25px;
            font-size: 16px;
            background-color: #3498db;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            user-select: none; /* テキスト選択を防止 */
            -webkit-tap-highlight-color: transparent; /* タップ時のハイライトを非表示 */
        }
        .control-button:active {
            background-color: #2980b9;
        }
        #moveLeft { grid-area: left; }
        #rotate { grid-area: rotate; }
        #moveRight { grid-area: right; }
        #hardDrop { grid-area: drop; }

        #score-display {
            font-size: 24px;
            margin-top: 10px;
        }

        #high-scores {
            margin-top: 20px;
            font-size: 18px;
            text-align: center;
        }
    </style>
</head>
<body>
    <h1>簡易テトリス</h1>
    <canvas id="tetris" width="200" height="400"></canvas>
    <div id="score-display">スコア: 0</div>
    <div id="high-scores">
        <h2>ハイスコア</h2>
        <p>記録なし</p>
    </div>
    <div class="controls">
        <button id="moveLeft" class="control-button"></button>
        <button id="rotate" class="control-button"></button>
        <button id="moveRight" class="control-button"></button>
        <button id="hardDrop" class="control-button"></button>
    </div>

    <script>
        const canvas = document.getElementById('tetris');
        const context = canvas.getContext('2d');
        const scoreDisplay = document.getElementById('score-display');
        const highScoresDisplay = document.getElementById('high-scores');
        const scale = 20;
        const width = canvas.width / scale;
        const height = canvas.height / scale;

        const playfield = [];
        for (let row = 0; row < height; row++) {
            playfield[row] = new Array(width).fill(0);
        }

        const tetrominos = [
            // T-piece
            [
                [0, 1, 0],
                [1, 1, 1],
                [0, 0, 0]
            ],
            // S-piece
            [
                [0, 2, 2],
                [2, 2, 0],
                [0, 0, 0]
            ],
            // Z-piece
            [
                [3, 3, 0],
                [0, 3, 3],
                [0, 0, 0]
            ],
            // I-piece
            [
                [0, 4, 0, 0],
                [0, 4, 0, 0],
                [0, 4, 0, 0],
                [0, 4, 0, 0]
            ],
            // L-piece
            [
                [0, 5, 0],
                [0, 5, 0],
                [0, 5, 5]
            ],
            // J-piece
            [
                [0, 6, 0],
                [0, 6, 0],
                [6, 6, 0]
            ],
            // O-piece
            [
                [7, 7],
                [7, 7]
            ]
        ];

        let score = 0;
        let currentTetromino = getRandomTetromino();
        let tetrominoX = 3;
        let tetrominoY = 0;
        let dropCounter = 0;
        let dropInterval = 1000;
        let lastTime = 0;
        let moveIntervalId = null;

        function getRandomTetromino() {
            const index = Math.floor(Math.random() * tetrominos.length);
            return tetrominos[index];
        }

        function draw() {
            context.fillStyle = '#34495e';
            context.fillRect(0, 0, canvas.width, canvas.height);
            drawMatrix(playfield, { x: 0, y: 0 });
            drawMatrix(currentTetromino, { x: tetrominoX, y: tetrominoY });
            updateScoreDisplay();
        }

        function drawMatrix(matrix, offset) {
            matrix.forEach((row, y) => {
                row.forEach((value, x) => {
                    if (value !== 0) {
                        context.fillStyle = getColor(value);
                        context.fillRect((offset.x + x) * scale, (offset.y + y) * scale, scale - 1, scale - 1);
                    }
                });
            });
        }

        function getColor(value) {
            const colors = ['#000', '#9b59b6', '#2ecc71', '#e74c3c', '#3498db', '#e67e22', '#f1c40f', '#95a5a6'];
            return colors[value];
        }

        function merge() {
            currentTetromino.forEach((row, y) => {
                row.forEach((value, x) => {
                    if (value !== 0) {
                        playfield[tetrominoY + y][tetrominoX + x] = value;
                    }
                });
            });
        }

        function collide() {
            for (let y = 0; y < currentTetromino.length; y++) {
                for (let x = 0; x < currentTetromino[y].length; x++) {
                    if (currentTetromino[y][x] !== 0) {
                        if (
                            tetrominoY + y >= height ||
                            (playfield[tetrominoY + y] && playfield[tetrominoY + y][tetrominoX + x]) !== 0
                        ) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        function rotate(matrix) {
            const newMatrix = matrix.map((_, colIndex) => matrix.map(row => row[colIndex])).reverse();
            return newMatrix;
        }

        function sweepLines() {
            let clearedLines = 0;
            for (let y = height - 1; y >= 0; --y) {
                let isLineFull = true;
                for (let x = 0; x < width; ++x) {
                    if (playfield[y][x] === 0) {
                        isLineFull = false;
                        break;
                    }
                }
                if (isLineFull) {
                    const row = playfield.splice(y, 1)[0].fill(0);
                    playfield.unshift(row);
                    clearedLines++;
                    y++;
                }
            }
            if (clearedLines > 0) {
                const points = [0, 40, 100, 300, 1200];
                score += points[clearedLines];
            }
        }

        function updateScoreDisplay() {
            scoreDisplay.innerText = `スコア: ${score}`;
        }

        function saveHighScore() {
            const highScoreData = JSON.parse(localStorage.getItem('tetrisHighScore')) || { score: 0, timestamp: '' };
            if (score > highScoreData.score) {
                const now = new Date();
                const formattedDate = `${now.getFullYear()}/${(now.getMonth() + 1).toString().padStart(2, '0')}/${now.getDate().toString().padStart(2, '0')} ${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;
                localStorage.setItem('tetrisHighScore', JSON.stringify({ score: score, timestamp: formattedDate }));
            }
        }

        function loadAndDisplayHighScores() {
            const highScoreData = JSON.parse(localStorage.getItem('tetrisHighScore'));
            if (highScoreData && highScoreData.score > 0) {
                highScoresDisplay.innerHTML = `<h2>ハイスコア</h2><p>スコア: ${highScoreData.score}<br>達成日時: ${highScoreData.timestamp}</p>`;
            } else {
                highScoresDisplay.innerHTML = `<h2>ハイスコア</h2><p>記録なし</p>`;
            }
        }

        function gameover() {
            saveHighScore();
            alert(`ゲームオーバー!\nあなたのスコアは ${score} です。`);
            playfield.forEach(row => row.fill(0));
            score = 0;
            loadAndDisplayHighScores();
        }

        function reset() {
            currentTetromino = getRandomTetromino();
            tetrominoX = 3;
            tetrominoY = 0;
            if (collide()) {
                gameover();
            }
        }

        function update(time = 0) {
            const deltaTime = time - lastTime;
            lastTime = time;

            dropCounter += deltaTime;
            if (dropCounter > dropInterval) {
                tetrominoY++;
                if (collide()) {
                    tetrominoY--;
                    merge();
                    sweepLines();
                    reset();
                }
                dropCounter = 0;
            }

            draw();
            requestAnimationFrame(update);
        }

        // キーボード操作
        document.addEventListener('keydown', event => {
            if (event.key === 'ArrowLeft') {
                tetrominoX--;
                if (collide()) tetrominoX++;
            } else if (event.key === 'ArrowRight') {
                tetrominoX++;
                if (collide()) tetrominoX--;
            } else if (event.key === 'ArrowDown') {
                tetrominoY++;
                if (collide()) {
                    tetrominoY--;
                    merge();
                    sweepLines();
                    reset();
                }
            } else if (event.key === 'ArrowUp') {
                const rotatedTetromino = rotate(currentTetromino);
                const originalX = tetrominoX;
                currentTetromino = rotatedTetromino;
                while (collide()) {
                    tetrominoX++;
                    if (collide()) {
                        tetrominoX = originalX;
                        currentTetromino = rotate(rotate(rotate(currentTetromino)));
                        break;
                    }
                }
            }
        });

        // タッチ操作用のボタンを取得
        const moveLeftBtn = document.getElementById('moveLeft');
        const rotateBtn = document.getElementById('rotate');
        const moveRightBtn = document.getElementById('moveRight');
        const hardDropBtn = document.getElementById('hardDrop');
/*
        // 連続移動を制御する関数
        function startMove(direction) {
            if (moveIntervalId) return;
            move(direction);
            moveIntervalId = setInterval(() => {
                move(direction);
            }, 100);
        }
*/
        // 左移動ボタン
        moveLeftBtn.addEventListener('touchstart', (event) => { event.preventDefault(); move('left'); });
        moveLeftBtn.addEventListener('click', (event) => { event.preventDefault(); move('left'); });

        // 右移動ボタン
        moveRightBtn.addEventListener('touchstart', (event) => { event.preventDefault(); move('right'); });
        moveRightBtn.addEventListener('click', (event) => { event.preventDefault(); move('right'); });

        function move(direction) {
            if (direction === 'left') {
                tetrominoX--;
                if (collide()) {
                    tetrominoX++;
                }
            } else if (direction === 'right') {
                tetrominoX++;
                if (collide()) {
                    tetrominoX--;
                }
            }
        }
/*
        function stopMove() {
            clearInterval(moveIntervalId);
            moveIntervalId = null;
        }
*/
/*
        // 左移動ボタンのイベントリスナー
        moveLeftBtn.addEventListener('touchstart', (event) => {
            event.preventDefault();
            startMove('left');
        });
        moveLeftBtn.addEventListener('mousedown', (event) => {
            event.preventDefault();
            startMove('left');
        });
        moveLeftBtn.addEventListener('touchend', stopMove);
        moveLeftBtn.addEventListener('mouseup', stopMove);

        // 右移動ボタンのイベントリスナー
        moveRightBtn.addEventListener('touchstart', (event) => {
            event.preventDefault();
            startMove('right');
        });
        moveRightBtn.addEventListener('mousedown', (event) => {
            event.preventDefault();
            startMove('right');
        });
        moveRightBtn.addEventListener('touchend', stopMove);
        moveRightBtn.addEventListener('mouseup', stopMove);
*/
        // 回転ボタンのイベントリスナー
        const handleRotate = (event) => {
            event.preventDefault();
            const rotatedTetromino = rotate(currentTetromino);
            const originalX = tetrominoX;
            currentTetromino = rotatedTetromino;
            while (collide()) {
                tetrominoX++;
                if (collide()) {
                    tetrominoX = originalX;
                    currentTetromino = rotate(rotate(rotate(currentTetromino)));
                    break;
                }
            }
        };
        rotateBtn.addEventListener('touchstart', handleRotate);
        rotateBtn.addEventListener('click', handleRotate);

        // ハードドロップボタンのイベントリスナー
        const handleHardDrop = (event) => {
            event.preventDefault();
            let linesDropped = 0;
            while (!collide()) {
                tetrominoY++;
                linesDropped++;
            }
            tetrominoY--;
            linesDropped--;
            score += linesDropped * 2;
            merge();
            sweepLines();
            reset();
        };
        hardDropBtn.addEventListener('touchstart', handleHardDrop);
        hardDropBtn.addEventListener('click', handleHardDrop);

        // ページ読み込み時にハイスコアを読み込んで表示
        document.addEventListener('DOMContentLoaded', loadAndDisplayHighScores);

        update();
    </script>
</body>
</html>

昔見かけたのは、コードがもっとシンプルなのに豪華だった気が・・・

なんか、まとまらん。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?