文件名:game5.html

俄罗斯方块网页游戏,运行的效果还不错,直接po图和代码。

<!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; }
        body {
            background: linear-gradient(135deg, #0f0f1e, #1a1a3e);
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            font-family: "Microsoft YaHei", sans-serif;
            color: #fff;
            user-select: none;
        }
        #wrapper {
            display: flex;
            gap: 20px;
            align-items: flex-start;
        }
        h1 {
            text-align: center;
            font-size: 28px;
            color: #00e5ff;
            margin-bottom: 10px;
            text-shadow: 0 0 20px rgba(0,229,255,0.4);
        }
        #game-container {
            position: relative;
            border-radius: 10px;
            overflow: hidden;
            box-shadow: 0 0 40px rgba(0,229,255,0.1), 0 20px 60px rgba(0,0,0,0.5);
        }
        canvas { display: block; background: #0a0a1a; }
        #sidebar {
            width: 140px;
            display: flex;
            flex-direction: column;
            gap: 12px;
        }
        .panel {
            background: rgba(255,255,255,0.05);
            border: 1px solid rgba(0,229,255,0.2);
            border-radius: 10px;
            padding: 12px;
        }
        .panel h3 {
            font-size: 13px;
            color: #00e5ff;
            margin-bottom: 8px;
            text-transform: uppercase;
            letter-spacing: 1px;
        }
        .panel .value {
            font-size: 24px;
            font-weight: bold;
            color: #ffd700;
        }
        #next-canvas {
            background: rgba(0,0,0,0.3);
            border-radius: 6px;
        }
        #overlay {
            position: absolute;
            top: 0; left: 0; right: 0; bottom: 0;
            background: rgba(0,0,0,0.8);
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            z-index: 10;
        }
        #overlay h2 { font-size: 36px; color: #00e5ff; margin-bottom: 12px; }
        #overlay p { font-size: 18px; color: #ccc; margin-bottom: 20px; }
        #overlay button {
            padding: 12px 36px;
            font-size: 18px;
            font-weight: bold;
            color: #0a0a1a;
            background: linear-gradient(135deg, #00e5ff, #00b0ff);
            border: none;
            border-radius: 30px;
            cursor: pointer;
            box-shadow: 0 4px 15px rgba(0,229,255,0.4);
            transition: transform 0.2s;
        }
        #overlay button:hover { transform: scale(1.08); }
        #hint {
            font-size: 12px;
            color: rgba(255,255,255,0.4);
            line-height: 1.8;
            text-align: center;
        }
    </style>
</head>
<body>
    <div>
        <h1>俄罗斯方块</h1>
        <div id="wrapper">
            <div id="game-container">
                <canvas id="game" width="300" height="600"></canvas>
                <div id="overlay">
                    <h2 id="ov-title">俄罗斯方块</h2>
                    <p id="ov-text">消除整行获得分数</p>
                    <button onclick="startGame()">开始游戏</button>
                </div>
            </div>
            <div id="sidebar">
                <div class="panel">
                    <h3>分数</h3>
                    <div class="value" id="score">0</div>
                </div>
                <div class="panel">
                    <h3>最高</h3>
                    <div class="value" id="high">0</div>
                </div>
                <div class="panel">
                    <h3>等级</h3>
                    <div class="value" id="level">1</div>
                </div>
                <div class="panel">
                    <h3>行数</h3>
                    <div class="value" id="lines">0</div>
                </div>
                <div class="panel">
                    <h3>下一个</h3>
                    <canvas id="next-canvas" width="120" height="100"></canvas>
                </div>
                <div id="hint">
                    ← → 移动<br>
                    ↑ 旋转<br>
                    ↓ 加速<br>
                    空格 直落<br>
                    P 暂停
                </div>
            </div>
        </div>
    </div>

<script>
var canvas = document.getElementById('game');
var ctx = canvas.getContext('2d');
var nextCanvas = document.getElementById('next-canvas');
var nextCtx = nextCanvas.getContext('2d');

var COLS = 10;
var ROWS = 20;
var CELL = 30;

// 7种方块定义
var SHAPES = {
    I: { color: '#00e5ff', blocks: [[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]] },
    O: { color: '#ffd700', blocks: [[1,1],[1,1]] },
    T: { color: '#ab47bc', blocks: [[0,1,0],[1,1,1],[0,0,0]] },
    S: { color: '#4caf50', blocks: [[0,1,1],[1,1,0],[0,0,0]] },
    Z: { color: '#ff5252', blocks: [[1,1,0],[0,1,1],[0,0,0]] },
    J: { color: '#42a5f5', blocks: [[1,0,0],[1,1,1],[0,0,0]] },
    L: { color: '#ff9800', blocks: [[0,0,1],[1,1,1],[0,0,0]] }
};
var KEYS = ['I','O','T','S','Z','J','L'];

var board, current, nextPiece;
var score, lines, level, highScore;
var dropTimer, dropInterval;
var state; // menu, playing, paused, gameover
var isPaused = false;

highScore = parseInt(localStorage.getItem('tetrisHigh') || '0');
document.getElementById('high').textContent = highScore;

function newBoard() {
    var b = [];
    for (var y = 0; y < ROWS; y++) {
        b.push(new Array(COLS).fill(null));
    }
    return b;
}

function randomPiece() {
    var key = KEYS[Math.floor(Math.random() * KEYS.length)];
    var shape = SHAPES[key];
    var blocks = shape.blocks.map(function(r) { return r.slice(); });
    return {
        type: key,
        color: shape.color,
        blocks: blocks,
        x: Math.floor((COLS - blocks[0].length) / 2),
        y: 0
    };
}

function init() {
    board = newBoard();
    score = 0;
    lines = 0;
    level = 1;
    dropInterval = 1000;
    current = randomPiece();
    nextPiece = randomPiece();
    state = 'playing';
    isPaused = false;
    updateUI();
}

function startGame() {
    hideOverlay();
    init();
    clearInterval(dropTimer);
    dropTimer = setInterval(tick, 50);
}

function tick() {
    if (state !== 'playing' || isPaused) {
        draw();
        return;
    }
    dropCounter += 50;
    if (dropCounter >= dropInterval) {
        dropCounter = 0;
        moveDown();
    }
    draw();
}

var dropCounter = 0;

function moveDown() {
    current.y++;
    if (collides(current)) {
        current.y--;
        lockPiece();
        return;
    }
}

function collides(piece) {
    for (var y = 0; y < piece.blocks.length; y++) {
        for (var x = 0; x < piece.blocks[y].length; x++) {
            if (piece.blocks[y][x]) {
                var bx = piece.x + x;
                var by = piece.y + y;
                if (bx < 0 || bx >= COLS || by >= ROWS) return true;
                if (by >= 0 && board[by][bx]) return true;
            }
        }
    }
    return false;
}

function lockPiece() {
    for (var y = 0; y < current.blocks.length; y++) {
        for (var x = 0; x < current.blocks[y].length; x++) {
            if (current.blocks[y][x]) {
                var by = current.y + y;
                var bx = current.x + x;
                if (by < 0) {
                    gameOver();
                    return;
                }
                board[by][bx] = current.color;
            }
        }
    }
    clearLines();
    current = nextPiece;
    nextPiece = randomPiece();
    if (collides(current)) {
        gameOver();
    }
}

function clearLines() {
    var cleared = 0;
    for (var y = ROWS - 1; y >= 0; y--) {
        var full = true;
        for (var x = 0; x < COLS; x++) {
            if (!board[y][x]) { full = false; break; }
        }
        if (full) {
            board.splice(y, 1);
            board.unshift(new Array(COLS).fill(null));
            cleared++;
            y++;
        }
    }
    if (cleared > 0) {
        var points = [0, 100, 300, 500, 800][cleared] * level;
        score += points;
        lines += cleared;
        // 升级
        var newLevel = Math.floor(lines / 10) + 1;
        if (newLevel > level) {
            level = newLevel;
            dropInterval = Math.max(100, 1000 - (level - 1) * 80);
        }
        updateUI();
    }
}

function rotate() {
    var b = current.blocks;
    var n = b.length;
    var rotated = [];
    for (var y = 0; y < n; y++) {
        rotated.push([]);
        for (var x = 0; x < n; x++) {
            rotated[y][x] = b[n - 1 - x][y];
        }
    }
    var oldBlocks = current.blocks;
    current.blocks = rotated;
    // 墙踢:尝试偏移
    var kicks = [0, -1, 1, -2, 2];
    for (var k = 0; k < kicks.length; k++) {
        current.x += kicks[k];
        if (!collides(current)) return;
        current.x -= kicks[k];
    }
    current.blocks = oldBlocks; // 旋转失败
}

function move(dx) {
    current.x += dx;
    if (collides(current)) current.x -= dx;
}

function hardDrop() {
    while (!collides(current)) {
        current.y++;
    }
    current.y--;
    score += 2;
    lockPiece();
    updateUI();
}

function gameOver() {
    state = 'gameover';
    clearInterval(dropTimer);
    if (score > highScore) {
        highScore = score;
        localStorage.setItem('tetrisHigh', highScore);
    }
    showOverlay('游戏结束', '分数: ' + score, '重新开始');
}

function togglePause() {
    if (state !== 'playing') return;
    isPaused = !isPaused;
}

function updateUI() {
    document.getElementById('score').textContent = score;
    document.getElementById('lines').textContent = lines;
    document.getElementById('level').textContent = level;
    document.getElementById('high').textContent = highScore;
}

function showOverlay(title, text, btn) {
    document.getElementById('ov-title').textContent = title;
    document.getElementById('ov-text').textContent = text;
    document.querySelector('#overlay button').textContent = btn;
    document.getElementById('overlay').style.display = 'flex';
}

function hideOverlay() {
    document.getElementById('overlay').style.display = 'none';
}

// ============ 渲染 ============

function draw() {
    ctx.fillStyle = '#0a0a1a';
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // 网格
    ctx.strokeStyle = 'rgba(255,255,255,0.03)';
    ctx.lineWidth = 1;
    for (var i = 0; i <= COLS; i++) {
        ctx.beginPath();
        ctx.moveTo(i * CELL, 0);
        ctx.lineTo(i * CELL, canvas.height);
        ctx.stroke();
    }
    for (var j = 0; j <= ROWS; j++) {
        ctx.beginPath();
        ctx.moveTo(0, j * CELL);
        ctx.lineTo(canvas.width, j * CELL);
        ctx.stroke();
    }

    // 已锁定方块
    for (var y = 0; y < ROWS; y++) {
        for (var x = 0; x < COLS; x++) {
            if (board[y][x]) {
                drawBlock(ctx, x * CELL, y * CELL, CELL, board[y][x]);
            }
        }
    }

    // 投影
    if (current && state === 'playing' && !isPaused) {
        var ghost = { blocks: current.blocks, x: current.x, y: current.y };
        while (!collides(ghost)) ghost.y++;
        ghost.y--;
        for (var gy = 0; gy < ghost.blocks.length; gy++) {
            for (var gx = 0; gx < ghost.blocks[gy].length; gx++) {
                if (ghost.blocks[gy][gx]) {
                    drawGhost(x * CELL, gy, gx, ghost);
                }
            }
        }
    }

    // 当前方块
    if (current) {
        for (var cy = 0; cy < current.blocks.length; cy++) {
            for (var cx = 0; cx < current.blocks[cy].length; cx++) {
                if (current.blocks[cy][cx]) {
                    drawBlock(ctx, (current.x + cx) * CELL, (current.y + cy) * CELL, CELL, current.color);
                }
            }
        }
    }

    // 暂停
    if (isPaused) {
        ctx.fillStyle = 'rgba(0,0,0,0.6)';
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = '#00e5ff';
        ctx.font = 'bold 28px "Microsoft YaHei"';
        ctx.textAlign = 'center';
        ctx.fillText('暂停', canvas.width / 2, canvas.height / 2);
    }

    // 画下一个
    drawNext();
}

function drawGhost(cy_offset, gy, gx, ghost) {
    var px = (ghost.x + gx) * CELL;
    var py = (ghost.y + gy) * CELL;
    ctx.strokeStyle = 'rgba(255,255,255,0.2)';
    ctx.lineWidth = 1.5;
    ctx.strokeRect(px + 2, py + 2, CELL - 4, CELL - 4);
}

function drawBlock(c, px, py, size, color) {
    // 主体
    var grad = c.createLinearGradient(px, py, px + size, py + size);
    grad.addColorStop(0, lighten(color, 0.25));
    grad.addColorStop(0.5, color);
    grad.addColorStop(1, darken(color, 0.3));
    c.fillStyle = grad;
    c.fillRect(px + 1, py + 1, size - 2, size - 2);

    // 高光
    c.fillStyle = 'rgba(255,255,255,0.3)';
    c.fillRect(px + 2, py + 2, size - 4, 3);
    c.fillRect(px + 2, py + 2, 3, size - 4);

    // 阴影
    c.fillStyle = 'rgba(0,0,0,0.3)';
    c.fillRect(px + 2, py + size - 5, size - 4, 3);
    c.fillRect(px + size - 5, py + 2, 3, size - 4);

    // 边框
    c.strokeStyle = darken(color, 0.5);
    c.lineWidth = 1;
    c.strokeRect(px + 0.5, py + 0.5, size - 1, size - 1);
}

function drawNext() {
    nextCtx.clearRect(0, 0, nextCanvas.width, nextCanvas.height);
    if (!nextPiece) return;

    var bs = nextPiece.blocks;
    var size = 22;
    var offX = (nextCanvas.width - bs[0].length * size) / 2;
    var offY = (nextCanvas.height - bs.length * size) / 2;

    for (var y = 0; y < bs.length; y++) {
        for (var x = 0; x < bs[y].length; x++) {
            if (bs[y][x]) {
                drawBlock(nextCtx, offX + x * size, offY + y * size, size, nextPiece.color);
            }
        }
    }
}

function lighten(hex, amt) {
    var r = parseInt(hex.substr(1,2), 16);
    var g = parseInt(hex.substr(3,2), 16);
    var b = parseInt(hex.substr(5,2), 16);
    r = Math.min(255, r + (255 - r) * amt);
    g = Math.min(255, g + (255 - g) * amt);
    b = Math.min(255, b + (255 - b) * amt);
    return 'rgb(' + Math.round(r) + ',' + Math.round(g) + ',' + Math.round(b) + ')';
}

function darken(hex, amt) {
    var r = parseInt(hex.substr(1,2), 16);
    var g = parseInt(hex.substr(3,2), 16);
    var b = parseInt(hex.substr(5,2), 16);
    return 'rgb(' + Math.round(r*(1-amt)) + ',' + Math.round(g*(1-amt)) + ',' + Math.round(b*(1-amt)) + ')';
}

// ============ 输入 ============

document.addEventListener('keydown', function(e) {
    if (state !== 'playing') return;
    if (isPaused && e.code !== 'KeyP') return;

    switch(e.code) {
        case 'ArrowLeft': case 'KeyA': e.preventDefault(); move(-1); break;
        case 'ArrowRight': case 'KeyD': e.preventDefault(); move(1); break;
        case 'ArrowDown': case 'KeyS': e.preventDefault(); moveDown(); score += 1; updateUI(); break;
        case 'ArrowUp': case 'KeyW': e.preventDefault(); rotate(); break;
        case 'Space': e.preventDefault(); hardDrop(); break;
        case 'KeyP': e.preventDefault(); togglePause(); break;
    }
    draw();
});

// 初始画面
draw();
</script>
</body>
</html>
Logo

AtomGit AI 社区提供模型库、数据集、Agent、Token等资源

更多推荐