俄罗斯方块游戏

我没使用js和css写了一个HTML页面来实现这个俄罗斯方块游戏。以下是完整的代码:

关键功能和设计介绍

HTML 结构

  1. <canvas> 元素:
  • 主要的 <canvas> 元素用于绘制游戏区域。
  • 辅助的 <canvas> 元素用于显示下一个方块。
  1. <div> 用于显示游戏信息:
  • 包含当前分数、下一个方块等信息。
  • 使用绝对定位的 <div> 显示游戏说明文字。

CSS 样式

  1. 页面背景色:
  • 页面背景色为深灰色 (#222)。
  1. Flexbox 布局:
  • 使用 Flexbox 使游戏区域居中,并且上下左右留白适当。
  1. 游戏区域样式:
  • canvas 元素有黑色边框 (1px solid #000)。
  • 游戏区域的背景色为黑色 (#000)。
  1. 游戏说明文字:
  • 绝对定位在页面顶部,居中显示,文字颜色为浅色 (#fff)。

JavaScript 游戏逻辑

  1. 画布设置:
  • 默认画布尺寸为 width = 600pxheight = 800px
  • 游戏区域的宽度为 15 列,比例为 scale = 40
  1. 方块矩阵和旋转:
  • 定义了七种不同的方块类型及其对应的矩阵表示。
  • 每种方块对应一个颜色。
  1. 方块移动与碰撞检测:
  • 实现了左右移动、下落加速和旋转功能。
  • 检测每次移动或旋转后的碰撞情况。
  1. 计时器与自动下落:
  • 使用 dropCounter 控制方块的自动下落。
  • dropCounter 超过 dropInterval,触发 playerDrop() 方法进行方块下落。
  1. 清除满行与得分:
  • arenaSweep() 方法负责检查并清除所有满行。
  • 每清除一行,得分增加 1。
  1. 游戏结束与重置:
  • 如果新生成的方块与已放置的方块发生碰撞,则游戏结束。
  • 在游戏结束后,游戏区域会重置,得分清零。
  1. 动画效果:
  • 使用 requestAnimationFrame() 平滑过渡方块下落过程。

通过这些设计和功能实现,你可以获得一个流畅且直观的俄罗斯方块游戏体验。

<!DOCTYPE html>
<html lang="zh-CN">
<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;
            height: 100vh;
            background-color: #222;
            margin: 0;
            position: relative;
        }
        .game-container {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 20px;
        }
        canvas {
            border: 1px solid #000;
            background-color: #000;
        }
        .info-panel {
            color: #fff;
            text-align: center;
            font-family: 'Arial', sans-serif;
        }
        .instructions {
            position: absolute;
            top: 10px;
            left: 50%;
            transform: translateX(-50%);
            color: #fff;
            font-family: 'Arial', sans-serif;
        }
    </style>
</head>
<body>
    <div class="instructions">
        使用方向键移动和旋转方块。按 Q/W 顺时针/逆时针旋转。
    </div>
    <div class="game-container">
        <div class="info-panel">
            得分: <span id="score">0</span><br>
            下一个方块:<br>
            <canvas id="next-canvas" width="160" height="160"></canvas>
        </div>
        <canvas id="tetris-canvas" width="600" height="800"></canvas>
    </div>

    <script>
        const canvas = document.getElementById('tetris-canvas');
        const context = canvas.getContext('2d');
        const nextCanvas = document.getElementById('next-canvas');
        const nextContext = nextCanvas.getContext('2d');

        const scale = 40; // 每个方块的大小为 40x40 像素
        const arenaWidth = 15; // 游戏区域宽度为 15 列
        const arenaHeight = 20; // 游戏区域高度为 20 行

        const colors = [
            null,
            '#FF0D72', // T 形方块颜色
            '#0DC2FF', // O 形方块颜色
            '#0DFF72', // L 形方块颜色
            '#F538FF', // J 形方块颜色
            '#FF8E0D', // I 形方块颜色
            '#FFE138', // S 形方块颜色
            '#3877FF'  // Z 形方块颜色
        ];

        const shapes = {
            T: [[0, 1, 0], [1, 1, 1], [0, 0, 0]], // T 形方块矩阵
            O: [[2, 2], [2, 2]],                   // O 形方块矩阵
            L: [[0, 0, 3], [3, 3, 3], [0, 0, 0]], // L 形方块矩阵
            J: [[4, 0, 0], [4, 4, 4], [0, 0, 0]], // J 形方块矩阵
            I: [[0, 5, 0, 0], [0, 5, 0, 0], [0, 5, 0, 0], [0, 5, 0, 0]], // I 形方块矩阵
            S: [[0, 6, 6], [6, 6, 0], [0, 0, 0]], // S 形方块矩阵
            Z: [[7, 7, 0], [0, 7, 7], [0, 0, 0]]  // Z 形方块矩阵
        };

        function createMatrix(w, h) {
            const matrix = [];
            while (h--) {
                matrix.push(new Array(w).fill(0)); // 创建一个 w x h 的矩阵,初始值为 0
            }
            return matrix;
        }

        function draw(matrix, offset, ctx) {
            ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // 清除画布
            matrix.forEach((row, y) => {
                row.forEach((value, x) => {
                    if (value !== 0) {
                        ctx.fillStyle = colors[value]; // 设置填充颜色
                        ctx.fillRect((x + offset.x) * scale, (y + offset.y) * scale, scale, scale); // 绘制方块
                        ctx.strokeStyle = '#000'; // 设置描边颜色
                        ctx.strokeRect((x + offset.x) * scale, (y + offset.y) * scale, scale, scale); // 描绘方块边框
                    }
                });
            });
        }

        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 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 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(); // 逆时针旋转
            }
        }

        let dropCounter = 0;
        let dropInterval = 1000; // 方块自动下落的时间间隔

        let lastTime = 0;

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

            dropCounter += deltaTime;
            if (dropCounter > dropInterval) {
                playerDrop();
            }

            draw(arena, { x: 0, y: 0 }, context); // 绘制游戏区域
            draw(player.matrix, player.pos, context); // 绘制玩家方块

            requestAnimationFrame(update); // 请求下一帧动画
        }

        function playerReset() {
            const pieces = Object.keys(shapes);
            player.matrix = shapes[pieces[Math.floor(Math.random() * pieces.length)]]; // 随机选择一个方块
            nextPlayer.matrix = shapes[pieces[Math.floor(Math.random() * pieces.length)]]; // 初始化下一个方块
            showNextPiece(); // 显示新的下一个方块
            player.pos.y = 0; // 初始化位置
            player.pos.x = ((arena[0].length / 2) | 0) - ((player.matrix[0].length / 2) | 0); // 居中对齐
            if (collide(arena, player)) {
                arena.forEach(row => row.fill(0)); // 清空游戏区域
                scoreElement.innerText = score = 0; // 重置得分
            }
        }

        function playerMove(offset) {
            player.pos.x += offset; // 移动方块
            if (collide(arena, player)) {
                player.pos.x -= offset; // 如果发生碰撞,则回退
            }
        }

        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 playerDrop() {
            player.pos.y++; // 方块下落一行
            if (collide(arena, player)) {
                player.pos.y--; // 回退
                merge(arena, player); // 合并方块到游戏区域
                playerReset(); // 重置玩家方块
                arenaSweep(); // 清除满行
            }
            dropCounter = 0; // 重置计时器
        }

        function arenaSweep() {
            let rowCount = 1;
            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;

                score += rowCount * 10; // 更新得分
                rowCount *= 2;
            }
            scoreElement.innerText = score; // 显示得分
        }

        function createPiece(type) {
            return shapes[type]; // 根据类型返回对应的矩阵
        }

        document.addEventListener('keydown', event => {
            if (event.key === 'ArrowLeft') {
                playerMove(-1); // 左移
            } else if (event.key === 'ArrowRight') {
                playerMove(1); // 右移
            } else if (event.key === 'ArrowDown') {
                playerDrop(); // 加速下落
            } else if (event.key === 'Q') {
                playerRotate(-1); // 逆时针旋转
            } else if (event.key === 'W') {
                playerRotate(1); // 顺时针旋转
            }
        });

        const arena = createMatrix(arenaWidth, arenaHeight); // 创建游戏区域矩阵
        const player = {
            pos: { x: 0, y: 0 },
            matrix: null
        };
        const nextPlayer = {
            pos: { x: 0, y: 0 },
            matrix: null
        };

        const scoreElement = document.getElementById('score'); // 获取得分元素
        let score = 0; // 初始化得分

        playerReset(); // 初始化玩家方块
        update(); // 开始游戏循环

        function showNextPiece() {
            nextContext.clearRect(0, 0, nextCanvas.width, nextCanvas.height); // 清除下一个方块显示区域
            draw(nextPlayer.matrix, { x: 0, y: 0 }, nextContext); // 绘制下一个方块
        }

        setInterval(() => {
            nextPlayer.matrix = createPiece(Object.keys(shapes)[Math.floor(Math.random() * Object.keys(shapes).length)]); // 更新下一个方块
            showNextPiece(); // 显示下一个方块
        }, 5000); // 每 5 秒更新一次

        showNextPiece(); // 初始化显示下一个方块
    </script>
</body>
</html>

No responses yet

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注