<!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>
index.html