未命名 DlY0CCedit icon

作者:
时光
Fork(复制)
下载
嵌入
设置
BUG反馈
index.html
现在支持上传本地图片了!
            
            <!DOCTYPE html>
<html lang="zh">
<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;
      font-family: Arial, sans-serif;
    }

    .game-container {
      display: flex;
      gap: 20px;
      align-items: flex-start;
    }

    #game-board {
      border: 2px solid #333;
      background-color: #111;
    }

    .side-panel {
      display: flex;
      flex-direction: column;
      gap: 20px;
    }

    #next-piece {
      width: 120px;
      height: 120px;
      border: 2px solid #333;
      background-color: #111;
    }

    .stats {
      background-color: #333;
      color: white;
      padding: 15px;
      border-radius: 8px;
      text-align: center;
    }

    .stats h3 {
      margin-top: 0;
      font-size: 18px;
    }

    .stats p {
      margin: 5px 0;
      font-size: 16px;
    }

    .controls {
      background-color: #333;
      color: white;
      padding: 15px;
      border-radius: 8px;
      text-align: center;
    }

    .controls p {
      margin: 5px 0;
      font-size: 14px;
    }
  </style>
</head>
<body>
  <div class="game-container">
    <!-- 游戏主区域 -->
    <canvas id="game-board" width="300" height="600"></canvas>

    <!-- 右侧控制面板 -->
    <div class="side-panel">
      <div>
        <h3>下一个方块</h3>
        <canvas id="next-piece" width="120" height="120"></canvas>
      </div>

      <div class="stats">
        <h3>得分</h3>
        <p id="score">0</p>
        <h3>等级</h3>
        <p id="level">1</p>
        <h3>行数</h3>
        <p id="lines">0</p>
      </div>

      <div class="controls">
        <p>↑: 旋转</p>
        <p>← →: 移动</p>
        <p>↓: 快速下落</p>
        <p>空格: 硬降</p>
        <p>P: 暂停</p>
      </div>
    </div>
  </div>

  <script>
    // 游戏常量
    const COLS = 10;
    const ROWS = 20;
    const BLOCK_SIZE = 30;
    const COLORS = [
      null,
      '#FF0000', // 红
      '#00FF00', // 绿
      '#0000FF', // 蓝
      '#FFFF00', // 黄
      '#FF00FF', // 品红
      '#00FFFF', // 青
      '#FFA500'  // 橙
    ];

    // 方块形状(I, O, T, S, Z, J, L)
    const PIECES = [
      null,
      // I
      [
        [0, 0, 0, 0],
        [1, 1, 1, 1],
        [0, 0, 0, 0],
        [0, 0, 0, 0]
      ],
      // O
      [
        [2, 2],
        [2, 2]
      ],
      // T
      [
        [0, 3, 0],
        [3, 3, 3],
        [0, 0, 0]
      ],
      // S
      [
        [0, 4, 4],
        [4, 4, 0],
        [0, 0, 0]
      ],
      // Z
      [
        [5, 5, 0],
        [0, 5, 5],
        [0, 0, 0]
      ],
      // J
      [
        [6, 0, 0],
        [6, 6, 6],
        [0, 0, 0]
      ],
      // L
      [
        [0, 0, 7],
        [7, 7, 7],
        [0, 0, 0]
      ]
    ];

    // 获取 canvas 和上下文
    const canvas = document.getElementById('game-board');
    const ctx = canvas.getContext('2d');
    const nextCanvas = document.getElementById('next-piece');
    const nextCtx = nextCanvas.getContext('2d');

    // 游戏状态
    let board = createMatrix(COLS, ROWS);
    let score = 0;
    let level = 1;
    let lines = 0;
    let dropCounter = 0;
    let lastTime = 0;
    let dropInterval = 1000; // 初始下落间隔(毫秒)
    let gameInterval;
    let gameOver = false;
    let isPaused = false;

    // 当前方块
    let currentPiece = null;
    let nextPiece = null;

    // 创建空矩阵
    function createMatrix(w, h) {
      const matrix = [];
      while (h--) {
        matrix.push(new Array(w).fill(0));
      }
      return matrix;
    }

    // 生成随机方块
    function getRandomPiece() {
      const pieceIndex = Math.floor(Math.random() * 7) + 1;
      return {
        matrix: PIECES[pieceIndex],
        color: pieceIndex
      };
    }

    // 初始化游戏
    function initGame() {
      board = createMatrix(COLS, ROWS);
      score = 0;
      level = 1;
      lines = 0;
      dropCounter = 0;
      lastTime = performance.now(); // 初始化为当前时间
      dropInterval = 1000;
      gameOver = false;
      isPaused = false;
      updateScore();
    
      currentPiece = getRandomPiece();
      nextPiece = getRandomPiece();
      currentPiece.position = {
        x: Math.floor(COLS / 2) - Math.floor(currentPiece.matrix[0].length / 2),
        y: 0
      };
    
      // 启动游戏循环
      requestAnimationFrame(gameLoop);
    }

    // 游戏主循环
    function gameLoop() {
      const time = performance.now();
      const deltaTime = time - lastTime;
      lastTime = time;
    
      dropCounter += deltaTime;
      if (dropCounter > dropInterval) {
        dropPiece();
      }
    
      draw();
      if (!gameOver && !isPaused) {
        requestAnimationFrame(gameLoop);
      }
    }

    // 绘制游戏画面
    function draw() {
      // 清空主画布
      ctx.fillStyle = '#111';
      ctx.fillRect(0, 0, canvas.width, canvas.height);

      // 绘制背景网格(可选,用于调试)
      // drawGrid();

      // 绘制已固定的方块
      drawMatrix(board, { x: 0, y: 0 });

      // 绘制当前方块
      if (currentPiece) {
        drawMatrix(currentPiece.matrix, currentPiece.position);
      }

      // 绘制下一个方块
      drawNextPiece();
    }

    // 绘制矩阵(用于方块)
    function drawMatrix(matrix, offset) {
      matrix.forEach((row, y) => {
        row.forEach((value, x) => {
          if (value !== 0) {
            ctx.fillStyle = COLORS[value];
            ctx.fillRect(
              (x + offset.x) * BLOCK_SIZE,
              (y + offset.y) * BLOCK_SIZE,
              BLOCK_SIZE,
              BLOCK_SIZE
            );
            ctx.strokeStyle = '#000';
            ctx.lineWidth = 1;
            ctx.strokeRect(
              (x + offset.x) * BLOCK_SIZE,
              (y + offset.y) * BLOCK_SIZE,
              BLOCK_SIZE,
              BLOCK_SIZE
            );
          }
        });
      });
    }

    // 绘制下一个方块
    function drawNextPiece() {
      nextCtx.clearRect(0, 0, nextCanvas.width, nextCanvas.height);
      const pieceWidth = nextPiece.matrix[0].length;
      const pieceHeight = nextPiece.matrix.length;
      const offsetX = (nextCanvas.width / BLOCK_SIZE - pieceWidth) / 2;
      const offsetY = (nextCanvas.height / BLOCK_SIZE - pieceHeight) / 2;
    
      nextPiece.matrix.forEach((row, y) => {
        row.forEach((value, x) => {
          if (value !== 0) {
            nextCtx.fillStyle = COLORS[value];
            nextCtx.fillRect(
              (x + offsetX) * BLOCK_SIZE,
              (y + offsetY) * BLOCK_SIZE,
              BLOCK_SIZE,
              BLOCK_SIZE
            );
            nextCtx.strokeStyle = '#000';
            nextCtx.lineWidth = 1;
            nextCtx.strokeRect(
              (x + offsetX) * BLOCK_SIZE,
              (y + offsetY) * BLOCK_SIZE,
              BLOCK_SIZE,
              BLOCK_SIZE
            );
          }
        });
      });
    }

    // 绘制网格(调试用)
    function drawGrid() {
      ctx.strokeStyle = '#333';
      ctx.lineWidth = 0.5;
      for (let x = 0; x <= canvas.width; x += BLOCK_SIZE) {
        ctx.beginPath();
        ctx.moveTo(x, 0);
        ctx.lineTo(x, canvas.height);
        ctx.stroke();
      }
      for (let y = 0; y <= canvas.height; y += BLOCK_SIZE) {
        ctx.beginPath();
        ctx.moveTo(0, y);
        ctx.lineTo(canvas.width, y);
        ctx.stroke();
      }
    }

    // 检查是否碰撞
    function collide(board, piece) {
      const [m, o] = [piece.matrix, piece.position];
      for (let y = 0; y < m.length; y++) {
        for (let x = 0; x < m[y].length; x++) {
          if (m[y][x] !== 0) {
            if (
              o.y + y >= board.length ||
              o.x + x < 0 ||
              o.x + x >= board[0].length ||
              (o.y + y >= 0 && board[o.y + y][o.x + x] !== 0)
            ) {
              return true;
            }
          }
        }
      }
      return false;
    }

    // 锁定当前方块
    function merge(board, piece) {
      piece.matrix.forEach((row, y) => {
        row.forEach((value, x) => {
          if (value !== 0) {
            board[y + piece.position.y][x + piece.position.x] = value;
          }
        });
      });
    }

    // 移动方块
    function movePiece(dir) {
      currentPiece.position.x += dir;
      if (collide(board, currentPiece)) {
        currentPiece.position.x -= dir;
      }
    }

    // 下落方块
    function dropPiece() {
      currentPiece.position.y++;
      if (collide(board, currentPiece)) {
        currentPiece.position.y--;
        merge(board, currentPiece);
        clearLines();
        resetPiece();
      }
      dropCounter = 0;
    }

    // 硬降(直接落到最底部)
    function hardDrop() {
      while (!collide(board, currentPiece)) {
        currentPiece.position.y++;
      }
      currentPiece.position.y--;
      merge(board, currentPiece);
      clearLines();
      resetPiece();
      dropCounter = 0;
    }

    // 旋转方块
    function rotatePiece() {
      const matrix = currentPiece.matrix;
      const N = matrix.length;
      const rotated = createMatrix(N, N);
    
      // 旋转逻辑
      for (let i = 0; i < N; i++) {
        for (let j = 0; j < N; j++) {
          rotated[j][N - 1 - i] = matrix[i][j];
        }
      }
    
      const oldMatrix = currentPiece.matrix;
      currentPiece.matrix = rotated;
    
      // 检查碰撞并尝试调整位置
      if (collide(board, currentPiece)) {
        // 尝试向左移动
        currentPiece.position.x -= 1;
        if (collide(board, currentPiece)) {
          currentPiece.position.x += 2;
          if (collide(board, currentPiece)) {
            currentPiece.matrix = oldMatrix; // 恢复原矩阵
          }
        }
      }
    }

    // 重置当前方块
    function resetPiece() {
      currentPiece = nextPiece;
      nextPiece = getRandomPiece();
    
      // ✅ 正确设置初始位置
      currentPiece.position = {
        x: Math.floor(COLS / 2) - Math.floor(currentPiece.matrix[0].length / 2),
        y: 0
      };
    
      // 检查是否立即碰撞(游戏结束)
      if (collide(board, currentPiece)) {
        gameOver = true;
        clearInterval(gameInterval);
        alert('游戏结束!得分:' + score);
      }
    }

    // 清除完成的行
    function clearLines() {
      let linesCleared = 0;
      outer: for (let y = board.length - 1; y >= 0; y--) {
        for (let x = 0; x < board[y].length; x++) {
          if (board[y][x] === 0) continue;
          continue outer;
        }

        // 清除该行
        const row = board.splice(y, 1)[0].fill(0);
        board.unshift(row);
        y++; // 重新检查这一行
        linesCleared++;
      }

      if (linesCleared > 0) {
        // 根据清除行数加分
        const linePoints = [0, 100, 300, 500, 800];
        linesCleared = Math.min(linesCleared, 4); // 限制最多清除4行
        score += linePoints[linesCleared] * level;
        lines += linesCleared;
        level = Math.floor(lines / 10) + 1;
        dropInterval = Math.max(100, 1000 - (level - 1) * 100); // 速度随等级提升
        updateScore();
      }
    }

    // 更新得分显示
    function updateScore() {
      document.getElementById('score').textContent = score;
      document.getElementById('level').textContent = level;
      document.getElementById('lines').textContent = lines;
    }

    // 键盘控制
    document.addEventListener('keydown', event => {
      if (gameOver || isPaused) return;

      switch (event.key) {
        case 'ArrowLeft':
          movePiece(-1);
          break;
        case 'ArrowRight':
          movePiece(1);
          break;
        case 'ArrowDown':
          dropPiece();
          break;
        case 'ArrowUp':
          rotatePiece();
          break;
        case ' ':
          hardDrop();
          break;
        case 'p':
        case 'P':
          togglePause();
          break;
      }
    });

    // 暂停/继续
    function togglePause() {
      isPaused = !isPaused;
      if (!isPaused) {
        lastTime = performance.now(); // 恢复为当前时间
        gameInterval = setInterval(gameLoop, 1000 / 60);
      } else {
        clearInterval(gameInterval);
      }
    }

    // 开始游戏
    initGame();
  </script>
</body>
</html>

        
预览
控制台