<!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>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background: linear-gradient(135deg, #1a1a2e, #16213e);
color: #fff;
}
.game-container {
display: flex;
gap: 20px;
}
#game-board {
border: 2px solid #4cc9f0;
background-color: #0f3460;
box-shadow: 0 0 20px rgba(76, 201, 240, 0.5);
}
.info-panel {
display: flex;
flex-direction: column;
gap: 20px;
}
.next-piece-container {
width: 120px;
height: 120px;
border: 2px solid #4cc9f0;
background-color: #0f3460;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 0 10px rgba(76, 201, 240, 0.3);
}
#next-piece {
display: grid;
grid-template-columns: repeat(4, 20px);
grid-template-rows: repeat(4, 20px);
}
.stats {
background-color: #0f3460;
padding: 15px;
border-radius: 5px;
border: 2px solid #4cc9f0;
box-shadow: 0 0 10px rgba(76, 201, 240, 0.3);
}
.controls {
background-color: #0f3460;
padding: 15px;
border-radius: 5px;
border: 2px solid #4cc9f0;
box-shadow: 0 0 10px rgba(76, 201, 240, 0.3);
}
button {
background-color: #4361ee;
color: white;
border: none;
padding: 10px 15px;
margin: 5px;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s;
}
button:hover {
background-color: #3a56d4;
transform: translateY(-2px);
}
.game-over {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(15, 52, 96, 0.95);
padding: 30px;
border-radius: 10px;
text-align: center;
border: 2px solid #f72585;
box-shadow: 0 0 30px rgba(247, 37, 133, 0.7);
}
.game-over h2 {
color: #f72585;
margin-top: 0;
}
.cell {
width: 20px;
height: 20px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.I { background-color: #00f0f0; }
.J { background-color: #0000f0; }
.L { background-color: #f0a000; }
.O { background-color: #f0f000; }
.S { background-color: #00f000; }
.T { background-color: #a000f0; }
.Z { background-color: #f00000; }
.grid-cell {
background-color: #1b3a61;
}
.instructions {
margin-top: 20px;
font-size: 14px;
line-height: 1.5;
color: #a9d6e5;
}
</style>
</head>
<body>
<div class="game-container">
<canvas id="game-board" width="300" height="600"></canvas>
<div class="info-panel">
<div class="next-piece-container">
<canvas id="next-piece" width="80" height="80"></canvas>
</div>
<div class="stats">
<h3>得分: <span id="score">0</span></h3>
<h3>等级: <span id="level">1</span></h3>
<h3>消除行数: <span id="lines">0</span></h3>
</div>
<div class="controls">
<button id="start-btn">开始/暂停</button>
<button id="reset-btn">重置</button>
<div class="instructions">
<p>← → 移动</p>
<p>↑ 旋转</p>
<p>↓ 加速下降</p>
<p>空格 瞬间下落</p>
</div>
</div>
</div>
</div>
<script>
// 游戏常量
const COLS = 10;
const ROWS = 20;
const BLOCK_SIZE = 30;
const COLORS = [
null,
'#00f0f0', // I
'#0000f0', // J
'#f0a000', // L
'#f0f000', // O
'#00f000', // S
'#a000f0', // T
'#f00000' // Z
];
// 方块形状
const SHAPES = [
null,
[[0,0,0,0], [1,1,1,1], [0,0,0,0], [0,0,0,0]], // I
[[1,0,0], [1,1,1], [0,0,0]], // J
[[0,0,1], [1,1,1], [0,0,0]], // L
[[1,1], [1,1]], // O
[[0,1,1], [1,1,0], [0,0,0]], // S
[[0,1,0], [1,1,1], [0,0,0]], // T
[[1,1,0], [0,1,1], [0,0,0]] // Z
];
// 游戏变量
let canvas = document.getElementById('game-board');
let ctx = canvas.getContext('2d');
let nextCanvas = document.getElementById('next-piece');
let nextCtx = nextCanvas.getContext('2d');
let scoreElement = document.getElementById('score');
let levelElement = document.getElementById('level');
let linesElement = document.getElementById('lines');
let startButton = document.getElementById('start-btn');
let resetButton = document.getElementById('reset-btn');
let board = [];
let score = 0;
let level = 1;
let lines = 0;
let gameOver = false;
let isPaused = false;
let dropCounter = 0;
let dropInterval = 1000;
let lastTime = 0;
let currentPiece = null;
let nextPiece = null;
// 初始化游戏板
function createBoard() {
return Array.from(Array(ROWS), () => Array(COLS).fill(0));
}
// 创建随机方块
function createPiece() {
const rand = Math.floor(Math.random() * 7) + 1;
return {
shape: SHAPES[rand],
color: COLORS[rand],
pos: {x: Math.floor(COLS / 2) - 1, y: 0}
};
}
// 绘制单个方块
function drawBlock(x, y, color) {
ctx.fillStyle = color;
ctx.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
ctx.strokeRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
// 添加3D效果
ctx.fillStyle = 'rgba(255, 255, 255, 0.2)';
ctx.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE, 4, BLOCK_SIZE);
ctx.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, 4);
}
// 绘制下一个方块
function drawNextPiece() {
nextCtx.clearRect(0, 0, nextCanvas.width, nextCanvas.height);
if (!nextPiece) return;
const shape = nextPiece.shape;
const offsetX = (4 - shape[0].length) / 2;
const offsetY = (4 - shape.length) / 2;
for (let y = 0; y < shape.length; y++) {
for (let x = 0; x < shape[y].length; x++) {
if (shape[y][x]) {
nextCtx.fillStyle = nextPiece.color;
nextCtx.fillRect(
(offsetX + x) * 20,
(offsetY + y) * 20,
20, 20
);
nextCtx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
nextCtx.strokeRect(
(offsetX + x) * 20,
(offsetY + y) * 20,
20, 20
);
}
}
}
}
// 绘制游戏板
function drawBoard() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制网格背景
for (let y = 0; y < ROWS; y++) {
for (let x = 0; x < COLS; x++) {
if (board[y][x]) {
drawBlock(x, y, board[y][x]);
} else {
ctx.fillStyle = '#1b3a61';
ctx.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
ctx.strokeStyle = 'rgba(255, 255, 255, 0.05)';
ctx.strokeRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
}
}
}
// 绘制当前方块
if (currentPiece) {
const shape = currentPiece.shape;
for (let y = 0; y < shape.length; y++) {
for (let x = 0; x < shape[y].length; x++) {
if (shape[y][x]) {
drawBlock(
currentPiece.pos.x + x,
currentPiece.pos.y + y,
currentPiece.color
);
}
}
}
}
}
// 检查碰撞
function collision(piece, board, offset = {}) {
const pos = piece.pos;
const shape = piece.shape;
const offsetX = offset.x || 0;
const offsetY = offset.y || 0;
for (let y = 0; y < shape.length; y++) {
for (let x = 0; x < shape[y].length; x++) {
if (shape[y][x] !== 0) {
const newX = pos.x + x + offsetX;
const newY = pos.y + y + offsetY;
if (
newX < 0 ||
newX >= COLS ||
newY >= ROWS ||
(newY >= 0 && board[newY][newX])
) {
return true;
}
}
}
}
return false;
}
// 合并方块到游戏板
function mergePiece() {
const shape = currentPiece.shape;
for (let y = 0; y < shape.length; y++) {
for (let x = 0; x < shape[y].length; x++) {
if (shape[y][x]) {
const boardY = currentPiece.pos.y + y;
const boardX = currentPiece.pos.x + x;
if (boardY >= 0) {
board[boardY][boardX] = currentPiece.color;
}
}
}
}
}
// 旋转方块
function rotatePiece() {
const originalShape = currentPiece.shape;
const rows = originalShape.length;
const cols = originalShape[0].length;
// 创建新数组来存储旋转后的形状
const rotated = Array.from({ length: cols }, () => Array(rows).fill(0));
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
rotated[x][rows - 1 - y] = originalShape[y][x];
}
}
// 检查旋转后是否有碰撞
const originalPiece = currentPiece.shape;
currentPiece.shape = rotated;
if (collision(currentPiece, board)) {
// 如果有碰撞,恢复原形状
currentPiece.shape = originalPiece;
}
}
// 清除完整行
function clearLines() {
let linesCleared = 0;
outer: for (let y = ROWS - 1; y >= 0; y--) {
for (let x = 0; x < COLS; x++) {
if (board[y][x] === 0) {
continue outer;
}
}
// 移除这一行
const row = board.splice(y, 1)[0].fill(0);
board.unshift(row);
linesCleared++;
y++; // 重新检查同一行(因为上面移除了)
}
if (linesCleared > 0) {
// 更新分数
const points = [40, 100, 300, 1200]; // 不同行数的得分
score += points[linesCleared - 1] * level;
lines += linesCleared;
// 更新等级
level = Math.floor(lines / 10) + 1;
// 更新速度
dropInterval = Math.max(100, 1000 - (level - 1) * 100);
// 更新显示
updateStats();
}
}
// 更新统计信息
function updateStats() {
scoreElement.textContent = score;
levelElement.textContent = level;
linesElement.textContent = lines;
}
// 生成新的方块
function newPiece() {
currentPiece = nextPiece || createPiece();
nextPiece = createPiece();
drawNextPiece();
// 检查游戏是否结束
if (collision(currentPiece, board)) {
gameOver = true;
gameLoop = null;
alert("游戏结束!最终得分:" + score);
}
}
// 主游戏循环
function update(time = 0) {
if (gameOver || isPaused) return;
const deltaTime = time - lastTime;
lastTime = time;
dropCounter += deltaTime;
if (dropCounter > dropInterval) {
dropPiece();
dropCounter = 0;
}
drawBoard();
requestAnimationFrame(update);
}
// 下降方块
function dropPiece() {
currentPiece.pos.y++;
if (collision(currentPiece, board)) {
currentPiece.pos.y--;
mergePiece();
clearLines();
newPiece();
}
}
// 瞬间下落
function hardDrop() {
while (!collision(currentPiece, board, {y: 1})) {
currentPiece.pos.y++;
}
dropPiece();
}
// 左移
function moveLeft() {
currentPiece.pos.x--;
if (collision(currentPiece, board)) {
currentPiece.pos.x++;
}
}
// 右移
function moveRight() {
currentPiece.pos.x++;
if (collision(currentPiece, board)) {
currentPiece.pos.x--;
}
}
// 开始/暂停游戏
function togglePause() {
isPaused = !isPaused;
if (!isPaused && !gameOver) {
lastTime = performance.now();
requestAnimationFrame(update);
}
}
// 重置游戏
function resetGame() {
board = createBoard();
score = 0;
level = 1;
lines = 0;
gameOver = false;
isPaused = false;
dropInterval = 1000;
updateStats();
newPiece();
drawBoard();
if (gameLoop) {
cancelAnimationFrame(gameLoop);
}
gameLoop = requestAnimationFrame(update);
}
// 键盘控制
document.addEventListener('keydown', event => {
if (gameOver || isPaused) return;
switch (event.keyCode) {
case 37: // 左箭头
moveLeft();
break;
case 39: // 右箭头
moveRight();
break;
case 40: // 下箭头
dropPiece();
break;
case 38: // 上箭头
rotatePiece();
break;
case 32: // 空格
hardDrop();
break;
}
});
// 按钮事件
startButton.addEventListener('click', togglePause);
resetButton.addEventListener('click', resetGame);
// 初始化游戏
let gameLoop;
function init() {
board = createBoard();
score = 0;
level = 1;
lines = 0;
gameOver = false;
isPaused = false;
dropInterval = 1000;
updateStats();
newPiece();
drawBoard();
gameLoop = requestAnimationFrame(update);
}
// 启动游戏
init();
</script>
</body>
</html>
index.html
index.html