使用js和canvas、html实现简单的俄罗斯方块小游戏

玩法介绍

点击开始游戏后,使用键盘上的←→控制移动,↑控制方块旋转,↓控制方块加速下落,累计一行即可消除并获得分数,触碰到顶部时游戏结束

代码实现

html代码复制即用,可阅读注释

html 复制代码
<!DOCTYPE html>
<html lang="en">
<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;
            margin: 0;
            background-color: #f0f0f0;
        }
        #game-container {
            display: flex;
            flex-direction: column;
            align-items: center;
        }
        #canvas {
            border: 1px solid black;
        }
        #score {
            margin-top: 10px;
            font-size: 20px;
        }
        #start-button {
            margin-top: 10px;
            padding: 10px 20px;
            font-size: 16px;
        }
    </style>
</head>
<body>
    <div id="game-container">
        <canvas id="canvas" width="300" height="600"></canvas>
        <div id="score">得分: 0</div>
        <button id="start-button">开始游戏</button>
    </div>

    <script>
        // 获取画布和上下文
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        const scoreDisplay = document.getElementById('score');
        const startButton = document.getElementById('start-button');

        // 游戏板的宽度和高度
        const boardWidth = 10;
        const boardHeight = 20;
        const blockSize = 30;

        // 初始化游戏板、当前方块、得分和游戏间隔
        let board = [];
        let currentPiece = null;
        let score = 0;
        let gameInterval = null;

        // 颜色数组
        const colors = [
            'cyan', 'blue', 'orange', 'yellow', 'green', 'purple', 'red'
        ];

        // 方块形状数组
        const pieces = [
            [[1, 1, 1, 1]], // I
            [[1, 1, 1], [0, 1, 0]], // T
            [[1, 1, 1], [1, 0, 0]], // L
            [[1, 1, 1], [0, 0, 1]], // J
            [[1, 1], [1, 1]], // O
            [[0, 1, 1], [1, 1, 0]], // S
            [[1, 1, 0], [0, 1, 1]] // Z
        ];

        // 方块类
        class Piece {
            constructor(tetromino) {
                this.tetromino = tetromino; // 方块形状
                this.color = colors[Math.floor(Math.random() * colors.length)]; // 随机颜色
                this.x = 3; // 初始位置
                this.y = -2; // 初始位置
            }

            // 旋转方块
            rotate() {
                const newTetromino = this.tetromino[0].map((_, i) => this.tetromino.map(row => row[i])).reverse();
                if (!this.collision(newTetromino, this.x, this.y)) {
                    this.tetromino = newTetromino;
                }
            }

            // 检测碰撞
            collision(tetromino, x, y) {
                for (let row = 0; row < tetromino.length; row++) {
                    for (let col = 0; col < tetromino[row].length; col++) {
                        if (tetromino[row][col] && (board[y + row] && board[y + row][x + col] || x + col < 0 || x + col >= boardWidth || y + row >= boardHeight)) {
                            return true;
                        }
                    }
                }
                return false;
            }

            // 向下移动
            moveDown() {
                if (!this.collision(this.tetromino, this.x, this.y + 1)) {
                    this.y++;
                } else {
                    this.lockPiece(); // 锁定方块
                    clearLines(); // 清除满行
                    if (this.y <= 0) { // 检查是否触顶
                        gameOver();
                    } else {
                        newPiece(); // 生成新方块
                    }
                }
            }

            // 向左移动
            moveLeft() {
                if (!this.collision(this.tetromino, this.x - 1, this.y)) {
                    this.x--;
                }
            }

            // 向右移动
            moveRight() {
                if (!this.collision(this.tetromino, this.x + 1, this.y)) {
                    this.x++;
                }
            }

            // 锁定方块到游戏板
            lockPiece() {
                for (let row = 0; row < this.tetromino.length; row++) {
                    for (let col = 0; col < this.tetromino[row].length; col++) {
                        if (this.tetromino[row][col]) {
                            board[this.y + row][this.x + col] = this.color;
                        }
                    }
                }
            }
        }

        // 生成新方块
        function newPiece() {
            const randomPiece = pieces[Math.floor(Math.random() * pieces.length)];
            currentPiece = new Piece(randomPiece);
        }

        // 绘制游戏板
        function drawBoard() {
            ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除画布
            for (let row = 0; row < boardHeight; row++) {
                for (let col = 0; col < boardWidth; col++) {
                    if (board[row][col]) {
                        ctx.fillStyle = board[row][col]; // 设置颜色
                        ctx.fillRect(col * blockSize, row * blockSize, blockSize, blockSize); // 绘制方块
                    }
                }
            }

            if (currentPiece) {
                for (let row = 0; row < currentPiece.tetromino.length; row++) {
                    for (let col = 0; col < currentPiece.tetromino[row].length; col++) {
                        if (currentPiece.tetromino[row][col]) {
                            ctx.fillStyle = currentPiece.color; // 设置颜色
                            ctx.fillRect((currentPiece.x + col) * blockSize, (currentPiece.y + row) * blockSize, blockSize, blockSize); // 绘制方块
                        }
                    }
                }
            }
        }

        // 清除满行
        function clearLines() {
            for (let row = boardHeight - 1; row >= 0; row--) {
                if (board[row].every(cell => cell !== 0)) { // 检查行是否满
                    board.splice(row, 1); // 删除满行
                    board.unshift(Array(boardWidth).fill(0)); // 添加空行
                    score += 10; // 增加得分
                    scoreDisplay.textContent = `得分: ${score}`; // 更新得分显示
                }
            }
        }

        // 游戏循环
        function gameLoop() {
            if (currentPiece) {
                currentPiece.moveDown(); // 移动方块
            }
            drawBoard(); // 绘制游戏板
        }

        // 开始游戏
        function startGame() {
            if (gameInterval) {
                clearInterval(gameInterval); // 清除之前的定时器
            }
            board = Array.from({ length: boardHeight }, () => Array(boardWidth).fill(0)); // 重置游戏板
            score = 0; // 重置得分
            scoreDisplay.textContent = `得分: ${score}`; // 更新得分显示
            newPiece(); // 生成新方块
            gameInterval = setInterval(gameLoop, 300); // 启动游戏循环
            document.addEventListener('keydown', handleKeyPress); // 监听键盘事件
        }

        // 处理键盘事件
        function handleKeyPress(event) {
            if (currentPiece) {
                switch (event.key) {
                    case 'ArrowDown':
                        currentPiece.moveDown(); // 向下移动
                        break;
                    case 'ArrowLeft':
                        currentPiece.moveLeft(); // 向左移动
                        break;
                    case 'ArrowRight':
                        currentPiece.moveRight(); // 向右移动
                        break;
                    case 'ArrowUp':
                        currentPiece.rotate(); // 旋转方块
                        break;
                }
            }
        }

        // 游戏结束
        function gameOver() {
            clearInterval(gameInterval); // 清除游戏循环
            document.removeEventListener('keydown', handleKeyPress); // 移除键盘事件监听
            alert('游戏结束!');
        }

        // 绑定开始游戏按钮的点击事件
        startButton.addEventListener('click', startGame);
    </script>
</body>
</html>
相关推荐
初遇你时动了情30 分钟前
uniapp 城市选择插件
开发语言·javascript·uni-app
zhangjr05751 小时前
【HarmonyOS Next】鸿蒙实用装饰器一览(一)
前端·harmonyos·arkts
不爱学习的YY酱1 小时前
【操作系统不挂科】<CPU调度(13)>选择题(带答案与解析)
java·linux·前端·算法·操作系统
zongzi_4941 小时前
二次封装的天气时间日历选择组件
开发语言·javascript·ecmascript
木子七2 小时前
vue2-vuex
前端·vue
麻辣_水煮鱼2 小时前
vue数据变化但页面不变
前端·javascript·vue.js
BY—-组态2 小时前
web组态软件
前端·物联网·工业互联网·web组态·组态
一条晒干的咸魚2 小时前
【Web前端】实现基于 Promise 的 API:alarm API
开发语言·前端·javascript·api·promise
WilliamLuo2 小时前
MP4结构初识-第一篇
前端·javascript·音视频开发
Beekeeper&&P...3 小时前
web钩子什么意思
前端·网络