俄罗斯方块edit icon

创建者:
Ray_ming
Fork(复制)
下载
嵌入
BUG反馈
index.html
index.html
            
            <!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>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            touch-action: manipulation;
        }
        
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
            color: white;
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            align-items: center;
            padding: 10px;
            overflow-x: hidden;
        }
        
        .header {
            text-align: center;
            margin: 10px 0 20px;
            width: 100%;
        }
        
        h1 {
            font-size: 2.2rem;
            margin-bottom: 5px;
            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
        }
        
        .game-container {
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
            gap: 20px;
            max-width: 1000px;
            width: 100%;
        }
        
        .game-board {
            flex: 0 0 auto;
            background-color: rgba(0, 0, 0, 0.7);
            border-radius: 10px;
            box-shadow: 0 10px 20px rgba(0, 0, 0, 0.5);
            padding: 10px;
            position: relative;
        }
        
        canvas {
            display: block;
            background-color: #111;
            border-radius: 5px;
        }
        
        .side-panel {
            flex: 1;
            min-width: 200px;
            display: flex;
            flex-direction: column;
            gap: 20px;
        }
        
        .panel-section {
            background-color: rgba(0, 0, 0, 0.7);
            border-radius: 10px;
            padding: 15px;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
        }
        
        h2 {
            font-size: 1.5rem;
            margin-bottom: 10px;
            text-align: center;
            color: #fdbb2d;
        }
        
        .next-piece {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 120px;
        }
        
        #nextCanvas {
            background-color: #111;
            border-radius: 5px;
        }
        
        .score-display {
            text-align: center;
            font-size: 1.8rem;
            font-weight: bold;
            margin: 10px 0;
        }
        
        .controls {
            display: grid;
            grid-template-columns: 1fr 1fr 1fr;
            grid-template-rows: auto auto auto;
            gap: 10px;
            margin-top: 20px;
        }
        
        .control-btn {
            background: linear-gradient(135deg, #3498db, #2980b9);
            border: none;
            border-radius: 10px;
            color: white;
            font-size: 1.2rem;
            font-weight: bold;
            padding: 12px 5px;
            cursor: pointer;
            box-shadow: 0 4px 0 #1f5b85, 0 6px 8px rgba(0, 0, 0, 0.3);
            transition: all 0.1s;
            user-select: none;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 60px;
        }
        
        .control-btn:active {
            transform: translateY(4px);
            box-shadow: 0 0 0 #1f5b85, 0 2px 4px rgba(0, 0, 0, 0.3);
        }
        
        .rotate-btn {
            background: linear-gradient(135deg, #9b59b6, #8e44ad);
            grid-column: 2;
        }
        
        .down-btn {
            background: linear-gradient(135deg, #e74c3c, #c0392b);
            grid-column: 2;
            grid-row: 2;
        }
        
        .left-btn {
            background: linear-gradient(135deg, #2ecc71, #27ae60);
            grid-column: 1;
            grid-row: 2;
        }
        
        .right-btn {
            background: linear-gradient(135deg, #f39c12, #d35400);
            grid-column: 3;
            grid-row: 2;
        }
        
        .pause-btn {
            background: linear-gradient(135deg, #34495e, #2c3e50);
            grid-column: 1;
            grid-row: 3;
        }
        
        .restart-btn {
            background: linear-gradient(135deg, #e67e22, #d35400);
            grid-column: 3;
            grid-row: 3;
        }
        
        .game-info {
            display: flex;
            flex-direction: column;
            gap: 10px;
        }
        
        .info-item {
            display: flex;
            justify-content: space-between;
            padding: 8px 0;
            border-bottom: 1px solid rgba(255, 255, 255, 0.2);
        }
        
        .level-display {
            font-size: 1.5rem;
            font-weight: bold;
            text-align: center;
            margin: 10px 0;
            color: #fdbb2d;
        }
        
        .instructions {
            margin-top: 20px;
            text-align: center;
            font-size: 0.9rem;
            opacity: 0.8;
        }
        
        .game-over {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.85);
            display: none;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            border-radius: 10px;
            z-index: 10;
        }
        
        .game-over h2 {
            font-size: 2.5rem;
            color: #e74c3c;
            margin-bottom: 20px;
        }
        
        .start-screen {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.85);
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            border-radius: 10px;
            z-index: 10;
        }
        
        .start-btn {
            background: linear-gradient(135deg, #2ecc71, #27ae60);
            border: none;
            border-radius: 10px;
            color: white;
            font-size: 1.5rem;
            font-weight: bold;
            padding: 15px 30px;
            margin-top: 20px;
            cursor: pointer;
            box-shadow: 0 4px 0 #1e8449, 0 6px 8px rgba(0, 0, 0, 0.3);
            transition: all 0.1s;
        }
        
        .start-btn:active {
            transform: translateY(4px);
            box-shadow: 0 0 0 #1e8449, 0 2px 4px rgba(0, 0, 0, 0.3);
        }
        
        @media (max-width: 768px) {
            .game-container {
                flex-direction: column;
                align-items: center;
            }
            
            .side-panel {
                width: 100%;
                max-width: 400px;
            }
            
            .controls {
                grid-template-columns: 1fr 1fr 1fr;
                grid-template-rows: auto auto auto;
            }
            
            .control-btn {
                font-size: 1rem;
                padding: 10px 5px;
                min-height: 50px;
            }
            
            h1 {
                font-size: 1.8rem;
            }
        }
        
        @media (max-width: 480px) {
            .game-board {
                padding: 5px;
            }
            
            canvas {
                width: 100% !important;
                height: auto !important;
            }
            
            .control-btn {
                font-size: 0.9rem;
                padding: 8px 2px;
                min-height: 45px;
            }
            
            h1 {
                font-size: 1.5rem;
            }
            
            h2 {
                font-size: 1.3rem;
            }
            
            .score-display {
                font-size: 1.5rem;
            }
        }
    </style>
</head>
<body>
    <div class="header">
        <h1>俄罗斯方块</h1>
        <p>触屏版 - 自适应手机和平板</p>
    </div>
    
    <div class="game-container">
        <div class="game-board">
            <canvas id="tetris" width="300" height="600"></canvas>
            <div class="game-over" id="gameOver">
                <h2>游戏结束!</h2>
                <div class="score-display">得分: <span id="finalScore">0</span></div>
                <button class="start-btn" id="restartBtn">重新开始</button>
            </div>
            <div class="start-screen" id="startScreen">
                <h2>俄罗斯方块</h2>
                <p>准备好开始游戏了吗?</p>
                <button class="start-btn" id="startBtn">开始游戏</button>
            </div>
        </div>
        
        <div class="side-panel">
            <div class="panel-section">
                <h2>下一个方块</h2>
                <div class="next-piece">
                    <canvas id="nextCanvas" width="120" height="120"></canvas>
                </div>
            </div>
            
            <div class="panel-section">
                <h2>游戏信息</h2>
                <div class="score-display">
                    <span id="score">0</span>
                </div>
                <div class="level-display">
                    等级: <span id="level">1</span>
                </div>
                <div class="game-info">
                    <div class="info-item">
                        <span>已消除行数:</span>
                        <span id="lines">0</span>
                    </div>
                    <div class="info-item">
                        <span>最高分:</span>
                        <span id="highScore">0</span>
                    </div>
                </div>
            </div>
            
            <div class="panel-section">
                <h2>游戏控制</h2>
                <div class="controls">
                    <button class="control-btn rotate-btn">旋转</button>
                    <button class="control-btn left-btn">左移</button>
                    <button class="control-btn right-btn">右移</button>
                    <button class="control-btn down-btn">下移</button>
                    <button class="control-btn pause-btn">暂停</button>
                    <button class="control-btn restart-btn">重新开始</button>
                </div>
                <div class="instructions">
                    <p>点击按钮或使用键盘方向键控制</p>
                </div>
            </div>
        </div>
    </div>

    <script>
        // 游戏常量
        const COLS = 10;
        const ROWS = 20;
        const BLOCK_SIZE = 30;
        const COLORS = [
            'cyan', 'blue', 'orange', 'yellow', 'green', 'purple', 'red'
        ];
        
        // 方块形状定义
        const SHAPES = [
            // I
            [
                [0, 0, 0, 0],
                [1, 1, 1, 1],
                [0, 0, 0, 0],
                [0, 0, 0, 0]
            ],
            // J
            [
                [2, 0, 0],
                [2, 2, 2],
                [0, 0, 0]
            ],
            // L
            [
                [0, 0, 3],
                [3, 3, 3],
                [0, 0, 0]
            ],
            // O
            [
                [4, 4],
                [4, 4]
            ],
            // S
            [
                [0, 5, 5],
                [5, 5, 0],
                [0, 0, 0]
            ],
            // T
            [
                [0, 6, 0],
                [6, 6, 6],
                [0, 0, 0]
            ],
            // Z
            [
                [7, 7, 0],
                [0, 7, 7],
                [0, 0, 0]
            ]
        ];
        
        // 游戏变量
        let canvas, ctx, nextCanvas, nextCtx;
        let board = [];
        let currentPiece, nextPiece;
        let score = 0;
        let level = 1;
        let lines = 0;
        let highScore = localStorage.getItem('tetrisHighScore') || 0;
        let gameOver = false;
        let isPaused = false;
        let dropTime = 0;
        let dropInterval = 1000; // 初始下落速度(毫秒)
        
        // 初始化游戏
        function init() {
            canvas = document.getElementById('tetris');
            ctx = canvas.getContext('2d');
            nextCanvas = document.getElementById('nextCanvas');
            nextCtx = nextCanvas.getContext('2d');
            
            // 设置画布尺寸
            canvas.width = COLS * BLOCK_SIZE;
            canvas.height = ROWS * BLOCK_SIZE;
            
            // 初始化游戏板
            createBoard();
            
            // 创建第一个方块
            currentPiece = createPiece();
            nextPiece = createPiece();
            
            // 更新显示
            updateHighScore();
            
            // 游戏循环
            requestAnimationFrame(gameLoop);
            
            // 添加事件监听器
            setupEventListeners();
        }
        
        // 创建游戏板
        function createBoard() {
            board = [];
            for (let y = 0; y < ROWS; y++) {
                board[y] = [];
                for (let x = 0; x < COLS; x++) {
                    board[y][x] = 0;
                }
            }
        }
        
        // 创建随机方块
        function createPiece() {
            const shapeId = Math.floor(Math.random() * SHAPES.length);
            return {
                shape: SHAPES[shapeId],
                color: COLORS[shapeId],
                x: Math.floor(COLS / 2) - Math.floor(SHAPES[shapeId][0].length / 2),
                y: 0
            };
        }
        
        // 绘制游戏板
        function drawBoard() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            // 绘制已固定的方块
            for (let y = 0; y < ROWS; y++) {
                for (let x = 0; x < COLS; x++) {
                    if (board[y][x]) {
                        drawBlock(ctx, x, y, COLORS[board[y][x] - 1]);
                    }
                }
            }
            
            // 绘制当前下落的方块
            if (currentPiece) {
                drawPiece(currentPiece);
            }
            
            // 绘制网格线
            ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
            ctx.lineWidth = 0.5;
            for (let x = 0; x <= COLS; x++) {
                ctx.beginPath();
                ctx.moveTo(x * BLOCK_SIZE, 0);
                ctx.lineTo(x * BLOCK_SIZE, ROWS * BLOCK_SIZE);
                ctx.stroke();
            }
            for (let y = 0; y <= ROWS; y++) {
                ctx.beginPath();
                ctx.moveTo(0, y * BLOCK_SIZE);
                ctx.lineTo(COLS * BLOCK_SIZE, y * BLOCK_SIZE);
                ctx.stroke();
            }
        }
        
        // 绘制方块
        function drawPiece(piece) {
            for (let y = 0; y < piece.shape.length; y++) {
                for (let x = 0; x < piece.shape[y].length; x++) {
                    if (piece.shape[y][x]) {
                        drawBlock(ctx, piece.x + x, piece.y + y, piece.color);
                    }
                }
            }
        }
        
        // 绘制单个方块
        function drawBlock(context, x, y, color) {
            context.fillStyle = color;
            context.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
            
            context.strokeStyle = 'rgba(255, 255, 255, 0.5)';
            context.lineWidth = 1;
            context.strokeRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
            
            // 添加3D效果
            context.fillStyle = 'rgba(255, 255, 255, 0.2)';
            context.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, 3);
            context.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE, 3, BLOCK_SIZE);
            
            context.fillStyle = 'rgba(0, 0, 0, 0.2)';
            context.fillRect(x * BLOCK_SIZE + BLOCK_SIZE - 3, y * BLOCK_SIZE, 3, BLOCK_SIZE);
            context.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE + BLOCK_SIZE - 3, BLOCK_SIZE, 3);
        }
        
        // 绘制下一个方块预览
        function drawNextPiece() {
            nextCtx.clearRect(0, 0, nextCanvas.width, nextCanvas.height);
            
            // 计算居中位置
            const offsetX = (nextCanvas.width - nextPiece.shape[0].length * BLOCK_SIZE) / 2;
            const offsetY = (nextCanvas.height - nextPiece.shape.length * BLOCK_SIZE) / 2;
            
            for (let y = 0; y < nextPiece.shape.length; y++) {
                for (let x = 0; x < nextPiece.shape[y].length; x++) {
                    if (nextPiece.shape[y][x]) {
                        nextCtx.fillStyle = nextPiece.color;
                        nextCtx.fillRect(
                            offsetX + x * BLOCK_SIZE, 
                            offsetY + y * BLOCK_SIZE, 
                            BLOCK_SIZE, 
                            BLOCK_SIZE
                        );
                        
                        nextCtx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
                        nextCtx.lineWidth = 1;
                        nextCtx.strokeRect(
                            offsetX + x * BLOCK_SIZE, 
                            offsetY + y * BLOCK_SIZE, 
                            BLOCK_SIZE, 
                            BLOCK_SIZE
                        );
                    }
                }
            }
        }
        
        // 移动方块
        function movePiece(dx, dy) {
            if (isPaused || gameOver) return;
            
            currentPiece.x += dx;
            currentPiece.y += dy;
            
            if (isCollision()) {
                currentPiece.x -= dx;
                currentPiece.y -= dy;
                
                // 如果是向下移动发生碰撞,则固定方块
                if (dy > 0) {
                    lockPiece();
                    removeLines();
                    currentPiece = nextPiece;
                    nextPiece = createPiece();
                    
                    // 检查游戏是否结束
                    if (isCollision()) {
                        gameOver = true;
                        document.getElementById('gameOver').style.display = 'flex';
                        document.getElementById('finalScore').textContent = score;
                        
                        // 更新最高分
                        if (score > highScore) {
                            highScore = score;
                            localStorage.setItem('tetrisHighScore', highScore);
                            updateHighScore();
                        }
                    }
                }
                
                return false;
            }
            
            return true;
        }
        
        // 旋转方块
        function rotatePiece() {
            if (isPaused || gameOver) return;
            
            const originalShape = currentPiece.shape;
            const rows = originalShape.length;
            const cols = originalShape[0].length;
            
            // 创建新的旋转后的形状
            const rotated = [];
            for (let x = 0; x < cols; x++) {
                rotated[x] = [];
                for (let y = 0; y < rows; y++) {
                    rotated[x][y] = originalShape[rows - 1 - y][x];
                }
            }
            
            currentPiece.shape = rotated;
            
            // 如果旋转后发生碰撞,则恢复原来的形状
            if (isCollision()) {
                currentPiece.shape = originalShape;
            }
        }
        
        // 检查碰撞
        function isCollision() {
            for (let y = 0; y < currentPiece.shape.length; y++) {
                for (let x = 0; x < currentPiece.shape[y].length; x++) {
                    if (currentPiece.shape[y][x]) {
                        const newX = currentPiece.x + x;
                        const newY = currentPiece.y + y;
                        
                        if (
                            newX < 0 || 
                            newX >= COLS || 
                            newY >= ROWS || 
                            (newY >= 0 && board[newY][newX])
                        ) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }
        
        // 固定方块到游戏板
        function lockPiece() {
            for (let y = 0; y < currentPiece.shape.length; y++) {
                for (let x = 0; x < currentPiece.shape[y].length; x++) {
                    if (currentPiece.shape[y][x]) {
                        const boardY = currentPiece.y + y;
                        if (boardY >= 0) { // 确保不会在顶部之外固定方块
                            board[boardY][currentPiece.x + x] = currentPiece.shape[y][x];
                        }
                    }
                }
            }
        }
        
        // 移除完整的行
        function removeLines() {
            let linesRemoved = 0;
            
            for (let y = ROWS - 1; y >= 0; y--) {
                let isLineComplete = true;
                
                for (let x = 0; x < COLS; x++) {
                    if (!board[y][x]) {
                        isLineComplete = false;
                        break;
                    }
                }
                
                if (isLineComplete) {
                    // 移除该行
                    for (let yy = y; yy > 0; yy--) {
                        for (let x = 0; x < COLS; x++) {
                            board[yy][x] = board[yy - 1][x];
                        }
                    }
                    
                    // 清空顶行
                    for (let x = 0; x < COLS; x++) {
                        board[0][x] = 0;
                    }
                    
                    linesRemoved++;
                    y++; // 重新检查当前行,因为上面的行已经下移
                }
            }
            
            if (linesRemoved > 0) {
                // 更新分数
                updateScore(linesRemoved);
            }
        }
        
        // 更新分数
        function updateScore(linesRemoved) {
            // 根据一次消除的行数计算得分
            const points = [0, 40, 100, 300, 1200];
            score += points[linesRemoved] * level;
            
            // 更新已消除行数
            lines += linesRemoved;
            
            // 每消除10行提升一个等级
            level = Math.floor(lines / 10) + 1;
            
            // 根据等级调整下落速度
            dropInterval = Math.max(100, 1000 - (level - 1) * 100);
            
            // 更新显示
            document.getElementById('score').textContent = score;
            document.getElementById('level').textContent = level;
            document.getElementById('lines').textContent = lines;
        }
        
        // 更新最高分显示
        function updateHighScore() {
            document.getElementById('highScore').textContent = highScore;
        }
        
        // 游戏主循环
        function gameLoop(timestamp) {
            if (!isPaused && !gameOver) {
                // 控制方块下落速度
                if (timestamp - dropTime > dropInterval) {
                    movePiece(0, 1);
                    dropTime = timestamp;
                }
                
                drawBoard();
                drawNextPiece();
            }
            
            requestAnimationFrame(gameLoop);
        }
        
        // 设置事件监听器
        function setupEventListeners() {
            // 键盘控制
            document.addEventListener('keydown', (e) => {
                if (gameOver) return;
                
                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 ' ':
                        // 硬降(直接落到底部)
                        while(movePiece(0, 1)) {}
                        break;
                    case 'p':
                        togglePause();
                        break;
                }
            });
            
            // 按钮控制
            document.querySelector('.left-btn').addEventListener('click', () => movePiece(-1, 0));
            document.querySelector('.right-btn').addEventListener('click', () => movePiece(1, 0));
            document.querySelector('.down-btn').addEventListener('click', () => movePiece(0, 1));
            document.querySelector('.rotate-btn').addEventListener('click', () => rotatePiece());
            document.querySelector('.pause-btn').addEventListener('click', () => togglePause());
            document.querySelector('.restart-btn').addEventListener('click', () => resetGame());
            
            // 开始按钮
            document.getElementById('startBtn').addEventListener('click', () => {
                document.getElementById('startScreen').style.display = 'none';
                resetGame();
            });
            
            // 重新开始按钮
            document.getElementById('restartBtn').addEventListener('click', () => {
                document.getElementById('gameOver').style.display = 'none';
                resetGame();
            });
        }
        
        // 切换暂停状态
        function togglePause() {
            isPaused = !isPaused;
            document.querySelector('.pause-btn').textContent = isPaused ? '继续' : '暂停';
        }
        
        // 重置游戏
        function resetGame() {
            createBoard();
            currentPiece = createPiece();
            nextPiece = createPiece();
            score = 0;
            level = 1;
            lines = 0;
            gameOver = false;
            isPaused = false;
            dropInterval = 1000;
            
            document.getElementById('score').textContent = score;
            document.getElementById('level').textContent = level;
            document.getElementById('lines').textContent = lines;
            document.querySelector('.pause-btn').textContent = '暂停';
        }
        
        // 页面加载完成后初始化游戏
        window.addEventListener('load', init);
    </script>
</body>
</html>
        
编辑器加载中
预览
控制台