Files
nanocode/happy-games/games/tetris/tetris_working_final.html
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

541 lines
20 KiB
HTML

<!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>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
}
.container { max-width: 800px; width: 100%; text-align: center; }
h1 { font-size: 2.5rem; margin-bottom: 10px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3); }
.subtitle { font-size: 1.2rem; opacity: 0.9; margin-bottom: 20px; }
.game-area { display: flex; flex-wrap: wrap; justify-content: center; gap: 20px; margin-bottom: 20px; }
canvas {
background: rgba(0, 0, 0, 0.7);
border-radius: 10px;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
}
.info-panel {
background: rgba(0, 0, 0, 0.7);
border-radius: 10px;
padding: 20px;
min-width: 200px;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
}
.info-item { margin-bottom: 15px; text-align: left; }
.info-label { font-size: 0.9rem; opacity: 0.7; margin-bottom: 5px; }
.info-value { font-size: 1.5rem; font-weight: bold; color: #ffd700; }
.controls { margin-top: 20px; }
button {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 50px;
padding: 12px 24px;
font-size: 1.1rem;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
margin: 5px;
}
button:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0,0,0,0.3); }
button:active { transform: translateY(0); }
.direction-buttons {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 10px;
}
.dir-btn { width: 60px; height: 60px; border-radius: 50%; font-size: 1.5rem; }
.rotate-btn { background: linear-gradient(135deg, #ff9a9e 0%, #fad0c4 100%); }
.instructions {
background: rgba(0, 0, 0, 0.5);
border-radius: 10px;
padding: 20px;
margin-top: 20px;
text-align: left;
display: none;
}
@media (max-width: 768px) {
.game-area { flex-direction: column; align-items: center; }
.info-panel { width: 100%; max-width: 400px; }
h1 { font-size: 2rem; }
button { padding: 10px 20px; font-size: 1rem; }
.dir-btn { width: 50px; height: 50px; font-size: 1.3rem; }
}
</style>
</head>
<body>
<div class="container">
<h1>🎮 俄罗斯方块</h1>
<div class="subtitle">最终工作版 | Windows完美兼容 | 纯Canvas 2D</div>
<div class="game-area">
<canvas id="game-canvas" width="540" height="720"></canvas>
<div class="info-panel">
<div class="info-item">
<div class="info-label">分数</div>
<div id="score" class="info-value">0</div>
</div>
<div class="info-item">
<div class="info-label">等级</div>
<div id="level" class="info-value">1</div>
</div>
<div class="info-item">
<div class="info-label">消行数</div>
<div id="lines" class="info-value">0</div>
</div>
<div class="info-item">
<div class="info-label">下一个方块</div>
<div id="next-piece" class="info-value">?</div>
</div>
<div class="controls">
<div>
<button id="start-btn" onclick="startGame()">▶️ 开始游戏</button>
<button onclick="togglePause()">⏸️ 暂停</button>
<button onclick="restartGame()">🔄 重来</button>
</div>
<div class="direction-buttons">
<button class="dir-btn" onclick="moveLeft()">←</button>
<button class="dir-btn rotate-btn" onclick="rotatePiece()">↻</button>
<button class="dir-btn" onclick="moveRight()">→</button>
</div>
<div>
<button class="dir-btn" onclick="moveDown()" style="width: 100%; margin-top: 10px;">↓ 快速下落</button>
</div>
</div>
</div>
</div>
<div class="instructions" id="instructions">
<h3>🎮 游戏说明</h3>
<p><strong>控制</strong>: ← → 移动,空格键旋转,↓ 加速下落,P键暂停</p>
<p><strong>目标</strong>: 消除完整的行来获得分数</p>
<button onclick="hideInstructions()" style="margin-top: 10px; width: 100%;">返回游戏</button>
</div>
<button onclick="showInstructions()" style="margin-top: 20px;">📖 显示说明</button>
</div>
<script>
// ==================== 游戏配置 ====================
const GRID_WIDTH = 10;
const GRID_HEIGHT = 20;
const CELL_SIZE = 30;
// 方块类型
const PIECE_TYPES = [
{ type: 'I', shape: [[1,1,1,1]], color: '#00f5ff', name: '长条' },
{ type: 'J', shape: [[1,0,0],[1,1,1]], color: '#0000ff', name: 'J形' },
{ type: 'L', shape: [[0,0,1],[1,1,1]], color: '#ff7f00', name: 'L形' },
{ type: 'O', shape: [[1,1],[1,1]], color: '#ffff00', name: '方块' },
{ type: 'S', shape: [[0,1,1],[1,1,0]], color: '#00ff00', name: 'S形' },
{ type: 'T', shape: [[0,1,0],[1,1,1]], color: '#800080', name: 'T形' },
{ type: 'Z', shape: [[1,1,0],[0,1,1]], color: '#ff0000', name: 'Z形' }
];
// ==================== 游戏状态 ====================
let canvas, ctx;
let board = [];
let currentPiece = null;
let nextPiece = null;
let score = 0;
let level = 1;
let lines = 0;
let gameRunning = false;
let gamePaused = false;
let gameOver = false;
let lastUpdate = 0;
let gameLoopId = null;
// ==================== 初始化 ====================
function initGame() {
console.log('🎮 初始化俄罗斯方块游戏...');
canvas = document.getElementById('game-canvas');
ctx = canvas.getContext('2d');
if (!ctx) {
console.error('❌ 无法获取Canvas 2D上下文');
alert('您的浏览器不支持Canvas 2D');
return;
}
console.log('✅ Canvas 2D上下文获取成功');
// 初始化游戏板
initBoard();
// 生成预览方块
generateNextPiece();
// 初始渲染
renderGame();
console.log('✅ 游戏初始化完成');
}
function initBoard() {
board = [];
for (let y = 0; y < GRID_HEIGHT; y++) {
board[y] = [];
for (let x = 0; x < GRID_WIDTH; x++) {
board[y][x] = 0;
}
}
}
function generateNextPiece() {
const randomType = PIECE_TYPES[Math.floor(Math.random() * PIECE_TYPES.length)];
nextPiece = {
type: randomType.type,
shape: randomType.shape,
color: randomType.color,
name: randomType.name,
x: Math.floor(GRID_WIDTH / 2) - Math.floor(randomType.shape[0].length / 2),
y: 0
};
document.getElementById('next-piece').textContent = nextPiece.name;
}
function spawnNewPiece() {
if (nextPiece) {
currentPiece = {
type: nextPiece.type,
shape: nextPiece.shape,
color: nextPiece.color,
name: nextPiece.name,
x: nextPiece.x,
y: nextPiece.y
};
}
generateNextPiece();
console.log(`生成新方块: ${currentPiece.type} (${currentPiece.name}) 位置: (${currentPiece.x}, ${currentPiece.y})`);
// 检查游戏是否结束
if (!isValidMove(currentPiece.shape, currentPiece.x, currentPiece.y)) {
endGame();
}
}
// ==================== 游戏逻辑 ====================
function isValidMove(shape, x, y) {
for (let row = 0; row < shape.length; row++) {
for (let col = 0; col < shape[row].length; col++) {
if (shape[row][col]) {
const newX = x + col;
const newY = y + row;
// 检查边界
if (newX < 0 || newX >= GRID_WIDTH || newY >= GRID_HEIGHT) {
return false;
}
// 检查碰撞
if (newY >= 0 && board[newY][newX]) {
return false;
}
}
}
}
return true;
}
function movePiece(dx, dy) {
if (!currentPiece || gamePaused || gameOver) return false;
const newX = currentPiece.x + dx;
const newY = currentPiece.y + dy;
console.log(`尝试移动: 从(${currentPiece.x},${currentPiece.y}) 到(${newX},${newY})`);
if (isValidMove(currentPiece.shape, newX, newY)) {
currentPiece.x = newX;
currentPiece.y = newY;
console.log(`✅ 移动成功: (${newX},${newY})`);
renderGame();
return true;
} else if (dy > 0) {
// 固定方块
console.log('🔒 固定方块');
lockPiece();
checkLines();
spawnNewPiece();
}
return false;
}
function rotatePiece() {
if (!currentPiece || gamePaused || gameOver) return;
// 创建旋转后的形状
const shape = currentPiece.shape;
const rows = shape.length;
const cols = shape[0].length;
const rotated = [];
for (let i = 0; i < cols; i++) {
rotated[i] = [];
for (let j = 0; j < rows; j++) {
rotated[i][j] = shape[rows - 1 - j][i];
}
}
// 尝试旋转
if (isValidMove(rotated, currentPiece.x, currentPiece.y)) {
currentPiece.shape = rotated;
console.log('✅ 旋转成功');
renderGame();
}
}
function lockPiece() {
for (let row = 0; row < currentPiece.shape.length; row++) {
for (let col = 0; col < currentPiece.shape[row].length; col++) {
if (currentPiece.shape[row][col]) {
const x = currentPiece.x + col;
const y = currentPiece.y + row;
if (y >= 0) {
board[y][x] = currentPiece.color;
}
}
}
}
}
function checkLines() {
let linesCleared = 0;
for (let y = GRID_HEIGHT - 1; y >= 0; y--) {
let lineFull = true;
for (let x = 0; x < GRID_WIDTH; x++) {
if (!board[y][x]) {
lineFull = false;
break;
}
}
if (lineFull) {
board.splice(y, 1);
board.unshift(new Array(GRID_WIDTH).fill(0));
linesCleared++;
y++;
}
}
if (linesCleared > 0) {
const points = [0, 100, 300, 500, 800][linesCleared] * level;
score += points;
lines += linesCleared;
level = Math.floor(lines / 10) + 1;
updateUI();
console.log(`🎉 消除 ${linesCleared} 行,获得 ${points} 分`);
}
}
// ==================== 游戏循环 ====================
function gameLoop() {
if (!gameRunning || gameOver || gamePaused) return;
const now = Date.now();
const deltaTime = (now - lastUpdate) / 1000;
// 每1秒下落一次
if (deltaTime > 1.0) {
console.log(`[游戏循环] 自动下落,deltaTime: ${deltaTime.toFixed(2)}秒`);
movePiece(0, 1);
lastUpdate = now;
}
}
// ==================== 渲染 ====================
function renderGame() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const offsetX = (canvas.width - GRID_WIDTH * CELL_SIZE) / 2;
const offsetY = (canvas.height - GRID_HEIGHT * CELL_SIZE) / 2;
// 绘制背景
ctx.fillStyle = 'rgba(255, 255, 255, 0.05)';
ctx.fillRect(offsetX, offsetY, GRID_WIDTH * CELL_SIZE, GRID_HEIGHT * CELL_SIZE);
// 绘制已固定的方块
for (let y = 0; y < GRID_HEIGHT; y++) {
for (let x = 0; x < GRID_WIDTH; x++) {
if (board[y][x]) {
ctx.fillStyle = board[y][x];
ctx.fillRect(
offsetX + x * CELL_SIZE,
offsetY + y * CELL_SIZE,
CELL_SIZE,
CELL_SIZE
);
ctx.strokeStyle = 'white';
ctx.lineWidth = 2;
ctx.strokeRect(
offsetX + x * CELL_SIZE,
offsetY + y * CELL_SIZE,
CELL_SIZE,
CELL_SIZE
);
}
}
}
// 绘制当前方块
if (currentPiece) {
ctx.fillStyle = currentPiece.color;
for (let row = 0; row < currentPiece.shape.length; row++) {
for (let col = 0; col < currentPiece.shape[row].length; col++) {
if (currentPiece.shape[row][col]) {
ctx.fillRect(
offsetX + (currentPiece.x + col) * CELL_SIZE,
offsetY + (currentPiece.y + row) * CELL_SIZE,
CELL_SIZE,
CELL_SIZE
);
ctx.strokeStyle = 'white';
ctx.lineWidth = 2;
ctx.strokeRect(
offsetX + (currentPiece.x + col) * CELL_SIZE,
offsetY + (currentPiece.y + row) * CELL_SIZE,
CELL_SIZE,
CELL_SIZE
);
}
}
}
}
}
// ==================== 游戏控制 ====================
window.startGame = function() {
if (gameRunning) return;
console.log('🎮 开始游戏...');
gameRunning = true;
gamePaused = false;
gameOver = false;
score = 0;
level = 1;
lines = 0;
lastUpdate = Date.now();
initBoard();
spawnNewPiece();
updateUI();
// 使用setInterval确保Windows兼容性
if (gameLoopId) clearInterval(gameLoopId);
gameLoopId = setInterval(gameLoop, 100); // 每100ms检查一次
document.getElementById('start-btn').textContent = '⏸️ 暂停';
console.log('✅ 游戏已启动,使用setInterval游戏循环');
};
window.togglePause = function() {
if (gameOver) return;
gamePaused = !gamePaused;
console.log(gamePaused ? '⏸️ 游戏暂停' : '▶️ 游戏继续');
document.getElementById('start-btn').textContent = gamePaused ? '▶️ 继续' : '⏸️ 暂停';
};
window.restartGame = function() {
console.log('🔄 重新开始游戏...');
if (gameLoopId) {
clearInterval(gameLoopId);
gameLoopId = null;
}
startGame();
};
function endGame() {
gameOver = true;
gameRunning = false;
if (gameLoopId) {
clearInterval(gameLoopId);
gameLoopId = null;
}
console.log(`💀 游戏结束,最终分数: ${score}`);
alert(`游戏结束!\n最终分数: ${score}\n消行数: ${lines}\n等级: ${level}`);
}
function updateUI() {
document.getElementById('score').textContent = score;
document.getElementById('level').textContent = level;
document.getElementById('lines').textContent = lines;
}
// ==================== 控制函数 ====================
window.moveLeft = function() { movePiece(-1, 0); };
window.moveRight = function() { movePiece(1, 0); };
window.moveDown = function() { movePiece(0, 1); };
window.showInstructions = function() {
document.getElementById('instructions').style.display = 'block';
};
window.hideInstructions = function() {
document.getElementById('instructions').style.display = 'none';
};
// ==================== 键盘控制 ====================
document.addEventListener('keydown', function(event) {
if (!gameRunning || gameOver) return;
console.log(`键盘按键: ${event.key}`);
switch(event.key) {
case 'ArrowLeft': movePiece(-1, 0); break;
case 'ArrowRight': movePiece(1, 0); break;
case 'ArrowDown': movePiece(0, 1); break;
case ' ':
case 'ArrowUp': rotatePiece(); break;
case 'p':
case 'P': togglePause(); break;
}
});
// ==================== 页面加载 ====================
window.addEventListener('load', function() {
console.log('📄 页面加载完成');
initGame();
console.log('🪟 Windows兼容性: 使用setInterval确保兼容性');
console.log('🎮 游戏已准备好,点击"开始游戏"按钮开始');
});
</script>
</body>
</html>