俄罗斯方块:用 Trae 一句话复刻经典

俄罗斯方块,是无数人童年的回忆。七种不同形状的方块从天而降,玩家通过左右移动、旋转它们,把它们拼成完整的横行,一旦填满就会自动消除。这种看似简单的玩法,却让人一玩就是几个小时,甚至成为全球最经典的电子游戏之一。

但如果你要自己写一款俄罗斯方块,麻烦可就来了。

方块的随机生成旋转逻辑碰撞检测消除行算法 ,每一个都是代码大坑,更别提 UI 和响应式布局了。可现在,有了 Trae IDE,这些曾经的难题,居然一句话就能解决。


💡 我想要的,其实很简单

我的需求只有几个核心点:

  • 七种经典形状的方块:正方形、长条、T 形、L 形......必须一个不落。
  • 方块可以左右移动、旋转:用箭头键就能操作,手感要顺滑。
  • 消除行的机制:当方块拼满一行时,整行消除,上方方块自动下落。
  • 简单的 UI:不用复杂装饰,干净、直观,能看到方块、分数、下一个预览就够了。

过去我可能要花好几天写完,但这次我只在 Trae 里敲了一句:

"生成俄罗斯方块游戏,玩家控制下落的方块拼接,消除行。"


✨ Trae 如何"读懂"并实现

只等了几秒,Trae 就吐出了一份完整的俄罗斯方块代码,核心功能全都齐了:

方块系统 :七种形状、随机出现,还自动预览下一个方块。

移动与旋转 :上下左右控制逻辑自动写好,方块贴边、堆叠都能处理。

行消除 :只要拼满一行,动画闪烁一下就清掉,还自动计算得分。

简洁 UI:屏幕上是干净的网格,右边显示分数和预览窗口,视觉清晰又不乱。

Trae 写的代码结构也很干净,逻辑清晰,连变量命名都很顺手,几乎可以直接丢进项目跑


🧩 玩起来,熟悉的"经典味道"

第一次运行 Trae 生成的俄罗斯方块,熟悉的感觉瞬间回来了:

🎮 左右箭头 → 控制方块移动

🔄 上箭头 → 旋转方块

下箭头 → 加速下落

每次消除一行,屏幕闪一下,分数跳一下,音效可以随手加,经典街机感立刻拉满


🛠 想扩展?一句话就行

Trae 的强大不止于"生成",更在于追加功能毫无压力,比如:

  • 加入下一个方块预览("加一个方块预览框")
  • 增加难度提升机制("每消 10 行,下落速度加快")
  • 给方块上个皮肤("让方块变成彩色渐变")
  • 增加排行榜和计时模式("记录玩家最高分和游戏时间")

一句自然语言指令,Trae 就能直接补齐功能代码,完美融合现有逻辑。


🎮 这是开发俄罗斯方块的新方式

以前写俄罗斯方块,逻辑又多又绕

  • 要写"方块旋转矩阵"
  • 要做"碰撞检测"
  • 要搞定"行消除"

全都得自己撸代码、调 bug。

而现在,有了 Trae,开发过程完全变了:

👉 不再是写代码的苦工 ,而是告诉工具我要什么,它帮我写好


✅ 结语

如果你也想写一个俄罗斯方块,不必再从零开始。打开 Trae,输入:

"生成俄罗斯方块游戏,玩家控制下落的方块拼接,消除行。"

几秒钟,一个完整的俄罗斯方块就出现在你的屏幕上,旋转、移动、消除行、计分,全都好用,还能随时扩展功能。

这不只是"写游戏更快",而是从今天起,开发本身也变得像玩游戏一样有趣

ini 复制代码
<!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 {
            font-family: Arial, sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            background-color: #f0f0f0;
        }
        .game-container {
            display: flex;
            gap: 20px;
        }
        .game-board {
            border: 2px solid #333;
            background-color: #111;
            width: 300px;
            height: 600px;
            position: relative;
            overflow: hidden;
        }
        .next-piece {
            border: 2px solid #333;
            background-color: #111;
            width: 150px;
            height: 150px;
            position: relative;
        }
        .info-panel {
            display: flex;
            flex-direction: column;
            gap: 20px;
        }
        .score-panel {
            border: 2px solid #333;
            background-color: #111;
            color: white;
            padding: 10px;
            width: 150px;
        }
        .controls {
            border: 2px solid #333;
            background-color: #111;
            color: white;
            padding: 10px;
            width: 150px;
        }
        .block {
            position: absolute;
            width: 30px;
            height: 30px;
            box-sizing: border-box;
            border: 1px solid rgba(255, 255, 255, 0.2);
        }
        .I { background-color: cyan; }
        .O { background-color: yellow; }
        .T { background-color: purple; }
        .S { background-color: green; }
        .Z { background-color: red; }
        .J { background-color: blue; }
        .L { background-color: orange; }
        .game-over {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.8);
            color: white;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            font-size: 24px;
            display: none;
        }
        button {
            background-color: #4CAF50;
            border: none;
            color: white;
            padding: 10px 20px;
            text-align: center;
            text-decoration: none;
            display: inline-block;
            font-size: 16px;
            margin: 10px 2px;
            cursor: pointer;
            border-radius: 5px;
        }
    </style>
</head>
<body>
    <div class="game-container">
        <div class="game-board" id="game-board">
            <div class="game-over" id="game-over">
                <div>游戏结束</div>
                <button id="restart-btn">重新开始</button>
            </div>
        </div>
        <div class="info-panel">
            <div class="next-piece" id="next-piece"></div>
            <div class="score-panel">
                <h3>分数</h3>
                <div id="score">0</div>
                <h3>等级</h3>
                <div id="level">1</div>
                <h3>行数</h3>
                <div id="lines">0</div>
            </div>
            <div class="controls">
                <h3>操作说明</h3>
                <p>← → : 左右移动</p>
                <p>↑ : 旋转</p>
                <p>↓ : 加速下落</p>
                <p>空格 : 直接落下</p>
                <p>P : 暂停/继续</p>
            </div>
            <button id="start-btn">开始游戏</button>
        </div>
    </div>

    <script>
        // 游戏常量
        const COLS = 10;
        const ROWS = 20;
        const BLOCK_SIZE = 30;
        const EMPTY = 0;

        // 方块形状定义
        const SHAPES = {
            I: [
                [0, 0, 0, 0],
                [1, 1, 1, 1],
                [0, 0, 0, 0],
                [0, 0, 0, 0]
            ],
            O: [
                [1, 1],
                [1, 1]
            ],
            T: [
                [0, 1, 0],
                [1, 1, 1],
                [0, 0, 0]
            ],
            S: [
                [0, 1, 1],
                [1, 1, 0],
                [0, 0, 0]
            ],
            Z: [
                [1, 1, 0],
                [0, 1, 1],
                [0, 0, 0]
            ],
            J: [
                [1, 0, 0],
                [1, 1, 1],
                [0, 0, 0]
            ],
            L: [
                [0, 0, 1],
                [1, 1, 1],
                [0, 0, 0]
            ]
        };

        // 游戏状态
        let board = [];
        let currentPiece = null;
        let nextPiece = null;
        let score = 0;
        let level = 1;
        let lines = 0;
        let gameInterval = null;
        let isPaused = false;
        let isGameOver = false;

        // DOM 元素
        const gameBoard = document.getElementById('game-board');
        const nextPieceDisplay = document.getElementById('next-piece');
        const scoreDisplay = document.getElementById('score');
        const levelDisplay = document.getElementById('level');
        const linesDisplay = document.getElementById('lines');
        const startBtn = document.getElementById('start-btn');
        const restartBtn = document.getElementById('restart-btn');
        const gameOverDisplay = document.getElementById('game-over');

        // 初始化游戏板
        function initBoard() {
            board = Array.from({ length: ROWS }, () => Array(COLS).fill(EMPTY));
        }

        // 随机生成方块
        function getRandomPiece() {
            const shapes = Object.keys(SHAPES);
            const randomShape = shapes[Math.floor(Math.random() * shapes.length)];
            return {
                shape: randomShape,
                matrix: SHAPES[randomShape],
                x: Math.floor((COLS - SHAPES[randomShape][0].length) / 2),
                y: 0
            };
        }

        // 绘制游戏板
        function drawBoard() {
            // 清空游戏板
            while (gameBoard.firstChild) {
                if (gameBoard.lastChild === gameOverDisplay) {
                    break;
                }
                gameBoard.removeChild(gameBoard.lastChild);
            }

            // 绘制已固定的方块
            for (let y = 0; y < ROWS; y++) {
                for (let x = 0; x < COLS; x++) {
                    if (board[y][x] !== EMPTY) {
                        const block = document.createElement('div');
                        block.className = `block ${board[y][x]}`;
                        block.style.left = `${x * BLOCK_SIZE}px`;
                        block.style.top = `${y * BLOCK_SIZE}px`;
                        gameBoard.appendChild(block);
                    }
                }
            }

            // 绘制当前方块
            if (currentPiece) {
                const { matrix, x, y, shape } = currentPiece;
                for (let row = 0; row < matrix.length; row++) {
                    for (let col = 0; col < matrix[row].length; col++) {
                        if (matrix[row][col]) {
                            const block = document.createElement('div');
                            block.className = `block ${shape}`;
                            block.style.left = `${(x + col) * BLOCK_SIZE}px`;
                            block.style.top = `${(y + row) * BLOCK_SIZE}px`;
                            gameBoard.appendChild(block);
                        }
                    }
                }
            }
        }

        // 绘制下一个方块
        function drawNextPiece() {
            // 清空下一个方块显示区域
            while (nextPieceDisplay.firstChild) {
                nextPieceDisplay.removeChild(nextPieceDisplay.lastChild);
            }

            if (nextPiece) {
                const { matrix, shape } = nextPiece;
                const offsetX = (150 - matrix[0].length * BLOCK_SIZE) / 2;
                const offsetY = (150 - matrix.length * BLOCK_SIZE) / 2;

                for (let row = 0; row < matrix.length; row++) {
                    for (let col = 0; col < matrix[row].length; col++) {
                        if (matrix[row][col]) {
                            const block = document.createElement('div');
                            block.className = `block ${shape}`;
                            block.style.left = `${offsetX + col * BLOCK_SIZE}px`;
                            block.style.top = `${offsetY + row * BLOCK_SIZE}px`;
                            nextPieceDisplay.appendChild(block);
                        }
                    }
                }
            }
        }

        // 检查碰撞
        function isCollision(matrix, x, y) {
            for (let row = 0; row < matrix.length; row++) {
                for (let col = 0; col < matrix[row].length; col++) {
                    if (matrix[row][col] !== 0) {
                        const newX = x + col;
                        const newY = y + row;

                        // 检查边界
                        if (newX < 0 || newX >= COLS || newY >= ROWS) {
                            return true;
                        }

                        // 检查已有方块
                        if (newY >= 0 && board[newY][newX] !== EMPTY) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        // 旋转矩阵
        function rotateMatrix(matrix) {
            const N = matrix.length;
            const result = Array.from({ length: N }, () => Array(N).fill(0));

            for (let row = 0; row < N; row++) {
                for (let col = 0; col < N; col++) {
                    result[col][N - 1 - row] = matrix[row][col];
                }
            }

            return result;
        }

        // 移动方块
        function movePiece(dx, dy) {
            if (!currentPiece || isPaused || isGameOver) return false;

            const newX = currentPiece.x + dx;
            const newY = currentPiece.y + dy;

            if (!isCollision(currentPiece.matrix, newX, newY)) {
                currentPiece.x = newX;
                currentPiece.y = newY;
                drawBoard();
                return true;
            }

            // 如果是向下移动且发生碰撞,则固定方块
            if (dy > 0) {
                lockPiece();
                return false;
            }

            return false;
        }

        // 旋转方块
        function rotatePiece() {
            if (!currentPiece || isPaused || isGameOver) return;

            const rotated = rotateMatrix(currentPiece.matrix);
            if (!isCollision(rotated, currentPiece.x, currentPiece.y)) {
                currentPiece.matrix = rotated;
                drawBoard();
            } else {
                // 尝试墙踢(Wall Kick)
                // 向左尝试
                if (!isCollision(rotated, currentPiece.x - 1, currentPiece.y)) {
                    currentPiece.matrix = rotated;
                    currentPiece.x -= 1;
                    drawBoard();
                }
                // 向右尝试
                else if (!isCollision(rotated, currentPiece.x + 1, currentPiece.y)) {
                    currentPiece.matrix = rotated;
                    currentPiece.x += 1;
                    drawBoard();
                }
                // 向上尝试(I型方块特殊处理)
                else if (currentPiece.shape === 'I' && !isCollision(rotated, currentPiece.x, currentPiece.y - 1)) {
                    currentPiece.matrix = rotated;
                    currentPiece.y -= 1;
                    drawBoard();
                }
            }
        }

        // 固定方块到游戏板
        function lockPiece() {
            const { matrix, x, y, shape } = currentPiece;

            for (let row = 0; row < matrix.length; row++) {
                for (let col = 0; col < matrix[row].length; col++) {
                    if (matrix[row][col]) {
                        const boardY = y + row;
                        const boardX = x + col;

                        // 游戏结束检查
                        if (boardY < 0) {
                            gameOver();
                            return;
                        }

                        board[boardY][boardX] = shape;
                    }
                }
            }

            // 检查并清除完整的行
            clearLines();

            // 生成新方块
            currentPiece = nextPiece;
            nextPiece = getRandomPiece();
            drawNextPiece();

            // 检查新方块是否可以放置
            if (isCollision(currentPiece.matrix, currentPiece.x, currentPiece.y)) {
                gameOver();
            }

            drawBoard();
        }

        // 清除完整的行
        function clearLines() {
            let linesCleared = 0;

            for (let y = ROWS - 1; y >= 0; y--) {
                if (board[y].every(cell => cell !== EMPTY)) {
                    // 移除该行
                    board.splice(y, 1);
                    // 在顶部添加新行
                    board.unshift(Array(COLS).fill(EMPTY));
                    linesCleared++;
                    y++; // 重新检查当前行(现在是新行)
                }
            }

            if (linesCleared > 0) {
                // 更新分数
                const linePoints = [0, 40, 100, 300, 1200]; // 0, 1, 2, 3, 4 行的分数
                score += linePoints[linesCleared] * level;
                lines += linesCleared;
                level = Math.floor(lines / 10) + 1;

                // 更新显示
                scoreDisplay.textContent = score;
                levelDisplay.textContent = level;
                linesDisplay.textContent = lines;

                // 调整游戏速度
                updateGameSpeed();
            }
        }

        // 更新游戏速度
        function updateGameSpeed() {
            if (gameInterval) {
                clearInterval(gameInterval);
            }
            const speed = Math.max(100, 1000 - (level - 1) * 100); // 最快100ms一次
            gameInterval = setInterval(gameLoop, speed);
        }

        // 硬降(直接落下)
        function hardDrop() {
            if (!currentPiece || isPaused || isGameOver) return;

            while (movePiece(0, 1)) {
                // 继续下落直到不能移动
            }
        }

        // 游戏主循环
        function gameLoop() {
            if (isPaused || isGameOver) return;
            movePiece(0, 1);
        }

        // 游戏结束
        function gameOver() {
            isGameOver = true;
            clearInterval(gameInterval);
            gameOverDisplay.style.display = 'flex';
        }

        // 开始新游戏
        function startGame() {
            // 重置游戏状态
            initBoard();
            score = 0;
            level = 1;
            lines = 0;
            isPaused = false;
            isGameOver = false;

            // 更新显示
            scoreDisplay.textContent = score;
            levelDisplay.textContent = level;
            linesDisplay.textContent = lines;
            gameOverDisplay.style.display = 'none';

            // 生成初始方块
            currentPiece = getRandomPiece();
            nextPiece = getRandomPiece();

            // 绘制游戏
            drawBoard();
            drawNextPiece();

            // 设置游戏循环
            if (gameInterval) {
                clearInterval(gameInterval);
            }
            gameInterval = setInterval(gameLoop, 1000);
        }

        // 暂停/继续游戏
        function togglePause() {
            if (isGameOver) return;

            isPaused = !isPaused;
            if (isPaused) {
                clearInterval(gameInterval);
            } else {
                updateGameSpeed();
            }
        }

        // 键盘控制
        document.addEventListener('keydown', (e) => {
            switch (e.key) {
                case 'ArrowLeft':
                    movePiece(-1, 0);
                    break;
                case 'ArrowRight':
                    movePiece(1, 0);
                    break;
                case 'ArrowDown':
                    movePiece(0, 1);
                    break;
                case 'ArrowUp':
                    rotatePiece();
                    break;
                case ' ':
                    hardDrop();
                    break;
                case 'p':
                case 'P':
                    togglePause();
                    break;
            }
        });

        // 按钮事件
        startBtn.addEventListener('click', startGame);
        restartBtn.addEventListener('click', startGame);

        // 初始化游戏板
        initBoard();
        drawBoard();
    </script>
</body>
</html>
相关推荐
Goboy4 小时前
打地鼠游戏:Trae 轻松实现点击挑战
ai编程·trae
程序员Better4 小时前
你开始用扣子空间了吗?让AI为你打工的智能助手,小白也能轻松上手!
aigc·ai编程·mistral.ai
学历真的很重要8 小时前
Eino 开源框架全景解析 - 以“大模型应用的搭积木指南”方式理解(一)
后端·语言模型·面试·golang·ai编程·eino
waynaqua8 小时前
FastAPI开发AI应用四:新增豆包、kimi模型
python·fastapi·ai编程
围巾哥萧尘10 小时前
「播客总结」AI时代软件开发模式的变革🧣
trae
老崔AI编程10 小时前
2小时写了1.3万行代码,我用qwen code做了一个mcp大管家
ai编程
围巾哥萧尘10 小时前
人工智能(AI)如何重塑软件开发🧣
trae
双向3314 小时前
当Trae遇上高德MCP:一次国庆武汉之旅的AI技术实践
人工智能·trae
子昕14 小时前
阿里Qoder实战体验:被吐槽的AI编程工具真有那么糟吗?
ai编程