【HTML】俄罗斯方块(精美版)
目录
一、页面效果
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 控制:实现游戏菜单交互与速度控制
📌 附加建议
若你希望继续扩展功能,可以考虑加入:
-
⏱️ 倒计时模式 / 挑战模式
-
🌐 排行榜或最高分记录
-
🔊 音效与背景音乐
-
🖱️ 支持鼠标 / 触控操作(移动端支持)