【HTML】俄罗斯方块(精美版)

发布于:2025-07-12 ⋅ 阅读:(16) ⋅ 点赞:(0)

目录

一、页面效果

1.1 开始界面

1.2 游戏界面

1.3 游戏结束界面 

二、怎么运行

三、执行代码

四、游戏主要功能

4.1 开始游戏前设置

4.2 游戏界面与内容

4.3 操作方式

4.4 游戏结束

五、操作说明表


摘要:俄罗斯方块网页版游戏

本项目是一款使用 p5.js 开发的俄罗斯方块网页版小游戏,提供完整的游戏功能与良好用户体验。玩家可通过浏览器直接运行游戏,无需安装插件。

主要特性包括:

  • 🕹️ 经典操作:支持方向键移动、旋转、快速下落

  • 🧭 菜单系统:带有开始菜单与暂停菜单,支持调整游戏速度

  • 💡 得分统计:游戏过程中实时显示得分

  • 🎨 炫酷视觉:渐变背景+高亮方块+阴影效果

  • 💻 跨平台支持:HTML + JS 实现,可直接在浏览器中运行

适合用于娱乐、教学演示或 Web 游戏开发学习项目。


一、页面效果

1.1 开始界面

滑竿:调节下落速度

开始游戏按钮:弹窗隐藏,游戏开始


1.2 游戏界面

“←”键:向左移动;

“→”键:向右移动;

“↑”键:旋转方块;

“↓”:方块直接下落到最底部;

“空格”键:暂停弹窗(继续,重新开始,退出)

 


1.3 游戏结束界面 

重新开始:回到游戏开始页面

退出:关闭窗口


二、怎么运行

1、新建一个txt文本;

2、代码复制进文本; 

 

3、txt文件 重命名为html格式即可,如index.html

4、点击即可在浏览器中开始游戏。


三、执行代码

<!DOCTYPE html>
<html>
<head>
  <title>俄罗斯方块</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js"></script>
  <style>
    body {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      height: 100vh;
      margin: 0;
      background: linear-gradient(135deg, #1e3c72, #2a5298);
      font-family: Arial, sans-serif;
    }
    canvas {
      border: 3px solid #fff;
      border-radius: 10px;
      box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
      display: none;
    }
    #scoreDisplay {
      margin-top: 20px;
      background: rgba(255, 255, 255, 0.1);
      padding: 15px;
      border-radius: 10px;
      color: white;
      font-size: 1.2em;
      font-weight: bold;
    }
    #startModal, #pauseModal {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: rgba(0, 0, 0, 0.7);
      display: flex;
      justify-content: center;
      align-items: center;
    }
    .modal-content {
      background: #fff;
      padding: 20px;
      border-radius: 10px;
      text-align: center;
      box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
    }
    .modal-content h2 {
      margin: 0 0 20px;
      color: #333;
    }
    .modal-content button {
      margin: 10px;
      padding: 10px 20px;
      font-size: 1em;
      cursor: pointer;
      border: none;
      border-radius: 5px;
      background: #ff6b6b;
      color: white;
      transition: background 0.3s;
    }
    .modal-content button:hover {
      background: #ff8787;
    }
    .modal-content input[type="range"] {
      width: 200px;
      margin: 20px 0;
      accent-color: #ff6b6b;
    }
  </style>
</head>
<body>
  <div id="startModal">
    <div class="modal-content">
      <h2>俄罗斯方块</h2>
      <p>调整速度:</p>
      <input type="range" id="startSpeedSlider" min="5" max="30" value="10">
      <button id="startButton">开始游戏</button>
    </div>
  </div>
  <div id="pauseModal" style="display: none;">
    <div class="modal-content">
      <h2 id="pauseModalTitle">暂停</h2>
      <p>调整速度:</p>
      <input type="range" id="pauseSpeedSlider" min="5" max="30" value="10">
      <button id="resumeButton">继续</button>
      <button id="restartButton">重新开始</button>
      <button id="exitButton">退出</button>
    </div>
  </div>
  <div id="scoreDisplay" style="display: none;">得分:0</div>
<script>
let board;
let currentPiece;
let score = 0;
let gameOver = false;
let paused = false;
let frameRateValue = 10;
let gameStarted = false;

function setup() {
  let canvas = createCanvas(300, 600);
  canvas.elt.style.display = 'none';
  board = new Board(10, 20);
  currentPiece = new Piece();
  frameRate(frameRateValue);
  
  document.getElementById('startButton').addEventListener('click', () => {
    document.getElementById('startModal').style.display = 'none';
    canvas.elt.style.display = 'block';
    document.getElementById('scoreDisplay').style.display = 'block';
    gameStarted = true;
    frameRateValue = parseInt(document.getElementById('startSpeedSlider').value);
  });
  
  document.getElementById('resumeButton').addEventListener('click', () => {
    if (!gameOver) {
      document.getElementById('pauseModal').style.display = 'none';
      paused = false;
      frameRateValue = parseInt(document.getElementById('pauseSpeedSlider').value);
    }
  });
  
  document.getElementById('restartButton').addEventListener('click', () => {
    window.location.reload();
  });
  
  document.getElementById('exitButton').addEventListener('click', () => {
    window.close();
  });
  
  document.getElementById('startSpeedSlider').addEventListener('input', () => {
    frameRateValue = parseInt(document.getElementById('startSpeedSlider').value);
    document.getElementById('pauseSpeedSlider').value = frameRateValue;
  });
  
  document.getElementById('pauseSpeedSlider').addEventListener('input', () => {
    frameRateValue = parseInt(document.getElementById('pauseSpeedSlider').value);
    document.getElementById('startSpeedSlider').value = frameRateValue;
  });
}

function draw() {
  let gradient = drawingContext.createLinearGradient(0, 0, 0, height);
  gradient.addColorStop(0, '#4facfe');
  gradient.addColorStop(1, '#00f2fe');
  drawingContext.fillStyle = gradient;
  rect(0, 0, width, height);
  
  if (gameStarted && !paused && !gameOver) {
    frameRate(frameRateValue);
    board.show();
    currentPiece.show();
    currentPiece.update();
  } else if (gameStarted) {
    board.show();
    currentPiece.show();
  }
  
  document.getElementById('scoreDisplay').innerText = `得分:${score}`;
  
  if (gameOver) {
    textSize(32);
    textAlign(CENTER);
    fill(255, 50, 50);
    text("游戏结束", width/2, height/2);
    drawingContext.shadowBlur = 20;
    drawingContext.shadowColor = 'rgba(255, 0, 0, 0.5)';
  } else {
    drawingContext.shadowBlur = 0;
  }
}

function keyPressed() {
  if (keyCode === 32) {
    if (gameStarted && !gameOver) {
      paused = !paused;
      document.getElementById('pauseModal').style.display = paused ? 'flex' : 'none';
      document.getElementById('pauseModalTitle').innerText = '暂停';
      document.getElementById('resumeButton').style.display = 'inline-block';
    }
    return;
  }
  
  if (gameOver || paused || !gameStarted) return;
  
  if (keyCode === LEFT_ARROW) {
    currentPiece.move(-1);
  } else if (keyCode === RIGHT_ARROW) {
    currentPiece.move(1);
  } else if (keyCode === DOWN_ARROW) {
    currentPiece.dropToBottom();
  } else if (keyCode === UP_ARROW) {
    currentPiece.rotate();
  }
}

class Board {
  constructor(w, h) {
    this.width = w;
    this.height = h;
    this.grid = [];
    for (let i = 0; i < h; i++) {
      this.grid[i] = [];
      for (let j = 0; j < w; j++) {
        this.grid[i][j] = 0;
      }
    }
  }
  
  show() {
    let cellWidth = width / this.width;
    let cellHeight = height / this.height;
    
    for (let i = 0; i < this.height; i++) {
      for (let j = 0; j < this.width; j++) {
        if (this.grid[i][j] === 1) {
          fill(0, 128, 255);
          stroke(255);
          strokeWeight(2);
          drawingContext.shadowBlur = 10;
          drawingContext.shadowColor = 'rgba(0, 128, 255, 0.5)';
          rect(j * cellWidth, i * cellHeight, cellWidth, cellHeight, 5);
          drawingContext.shadowBlur = 0;
        }
      }
    }
  }
  
  addPiece(piece) {
    for (let i = 0; i < piece.shape.length; i++) {
      for (let j = 0; j < piece.shape[i].length; j++) {
        if (piece.shape[i][j] === 1) {
          let boardX = piece.x + j;
          let boardY = piece.y + i;
          if (boardY >= 0 && boardY < this.height && boardX >= 0 && boardX < this.width) {
            this.grid[boardY][boardX] = 1;
          }
        }
      }
    }
    this.clearLines();
  }
  
  checkCollision(piece) {
    for (let i = 0; i < piece.shape.length; i++) {
      for (let j = 0; j < piece.shape[i].length; j++) {
        if (piece.shape[i][j] === 1) {
          let boardX = piece.x + j;
          let boardY = piece.y + i;
          
          if (boardY >= this.height) return true;
          if (boardX < 0 || boardX >= this.width) return true;
          if (boardY >= 0 && this.grid[boardY][boardX] === 1) return true;
        }
      }
    }
    return false;
  }
  
  clearLines() {
    let linesCleared = 0;
    for (let i = this.height - 1; i >= 0; i--) {
      let full = true;
      for (let j = 0; j < this.width; j++) {
        if (this.grid[i][j] === 0) {
          full = false;
          break;
        }
      }
      if (full) {
        this.grid.splice(i, 1);
        this.grid.unshift(new Array(this.width).fill(0));
        linesCleared++;
        i++;
      }
    }
    score += linesCleared * 100;
  }
}

class Piece {
  constructor() {
    this.shapes = [
      [[1, 1, 1, 1]], // I
      [[1, 1], [1, 1]], // O
      [[1, 1, 1], [0, 1, 0]], // T
      [[1, 1, 1], [1, 0, 0]], // L
      [[1, 1, 1], [0, 0, 1]], // J
      [[1, 1, 0], [0, 1, 1]], // S
      [[0, 1, 1], [1, 1, 0]] // Z
    ];
    this.shape = random(this.shapes);
    this.x = floor(board.width / 2) - floor(this.shape[0].length / 2);
    this.y = -this.shape.length;
    this.colors = [
      [0, 255, 255], // I: 青色
      [255, 255, 0], // O: 黄色
      [128, 0, 128], // T: 紫色
      [255, 165, 0], // L: 橙色
      [0, 0, 255],   // J: 蓝色
      [0, 255, 0],   // S: 绿色
      [255, 0, 0]    // Z: 红色
    ];
    this.color = this.colors[this.shapes.indexOf(this.shape)];
  }
  
  show() {
    let cellWidth = width / board.width;
    let cellHeight = height / board.height;
    
    for (let i = 0; i < this.shape.length; i++) {
      for (let j = 0; j < this.shape[i].length; j++) {
        if (this.shape[i][j] === 1) {
          fill(this.color[0], this.color[1], this.color[2]);
          stroke(255);
          strokeWeight(2);
          drawingContext.shadowBlur = 10;
          drawingContext.shadowColor = `rgba(${this.color[0]}, ${this.color[1]}, ${this.color[2]}, 0.5)`;
          rect((this.x + j) * cellWidth, (this.y + i) * cellHeight, cellWidth, cellHeight, 5);
          drawingContext.shadowBlur = 0;
        }
      }
    }
  }
  
  update() {
    let nextPiece = new Piece();
    Object.assign(nextPiece, this);
    nextPiece.y++;
    
    if (board.checkCollision(nextPiece)) {
      if (this.y < 0) {
        gameOver = true;
        document.getElementById('pauseModal').style.display = 'flex';
        document.getElementById('pauseModalTitle').innerText = '游戏结束';
        document.getElementById('resumeButton').style.display = 'none';
      } else {
        board.addPiece(this);
        currentPiece = new Piece();
      }
    } else {
      this.y++;
    }
  }
  
  move(dir) {
    let nextPiece = new Piece();
    Object.assign(nextPiece, this);
    nextPiece.x += dir;
    
    if (!board.checkCollision(nextPiece)) {
      this.x = nextPiece.x;
    }
  }
  
  rotate() {
    let nextPiece = new Piece();
    Object.assign(nextPiece, this);
    let newShape = [];
    
    for (let i = 0; i < this.shape[0].length; i++) {
      newShape[i] = [];
      for (let j = 0; j < this.shape.length; j++) {
        newShape[i][j] = this.shape[this.shape.length - 1 - j][i];
      }
    }
    
    nextPiece.shape = newShape;
    
    if (!board.checkCollision(nextPiece)) {
      this.shape = newShape;
    }
  }
  
  dropToBottom() {
    let nextPiece = new Piece();
    Object.assign(nextPiece, this);
    
    while (!board.checkCollision(nextPiece)) {
      this.y = nextPiece.y;
      nextPiece.y++;
    }
    
    if (this.y < 0 && board.checkCollision(nextPiece)) {
      gameOver = true;
      document.getElementById('pauseModal').style.display = 'flex';
      document.getElementById('pauseModalTitle').innerText = '游戏结束';
      document.getElementById('resumeButton').style.display = 'none';
    } else {
      board.addPiece(this);
      currentPiece = new Piece();
    }
  }
}
</script>
</body>
</html>

四、游戏主要功能

4.1 开始游戏前设置

  • 初始弹出「开始菜单」,可通过滑块选择游戏速度(帧率 5~30)。

  • 点击 “开始游戏” 按钮进入游戏界面。

4.2 游戏界面与内容

  • 主界面居中展示游戏区域(10 列 × 20 行)。

  • 每个方块都有高亮颜色 + 阴影效果,视觉体验良好。

  • 屏幕上方显示当前 得分

4.3 操作方式

  • ⬅️ 左方向键:方块左移

  • ➡️ 右方向键:方块右移

  • ⬆️ 上方向键:旋转当前方块

  • ⬇️ 下方向键:直接下落到底部

  • 空格键(Space):暂停/恢复游戏

    • 暂停时弹出「暂停菜单」,可继续游戏、调整速度、重新开始、退出窗口

4.4 游戏结束

  • 当新方块生成时碰到已有方块(顶部被填满)→ 游戏结束

  • 弹出“游戏结束”提示框,提供:

    • 🔄 重新开始

    • 退出页面


五、操作说明表

功能 操作方式
移动方块(左右) ← → 键盘方向键
旋转方块 ↑ 键盘方向键
快速下落到底 ↓ 键盘方向键
暂停 / 恢复游戏 空格键(Space)
调整游戏速度 菜单滑块
重新开始游戏 暂停菜单 → 重新开始按钮
退出游戏 暂停菜单 → 退出按钮

 六、技术栈

  • 🔸 HTML + CSS + JavaScript

  • 🔸 p5.js 图形库:用于渲染游戏画面

  • 🔸 DOM 控制:实现游戏菜单交互与速度控制


📌 附加建议

若你希望继续扩展功能,可以考虑加入:

  • ⏱️ 倒计时模式 / 挑战模式

  • 🌐 排行榜或最高分记录

  • 🔊 音效与背景音乐

  • 🖱️ 支持鼠标 / 触控操作(移动端支持)


网站公告

今日签到

点亮在社区的每一天
去签到