342 lines
11 KiB
HTML
342 lines
11 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 {
|
|
background: #0a0a1a;
|
|
color: white;
|
|
font-family: monospace;
|
|
padding: 20px;
|
|
}
|
|
canvas {
|
|
border: 2px solid white;
|
|
background: #111827;
|
|
display: block;
|
|
margin: 20px auto;
|
|
}
|
|
.info {
|
|
text-align: center;
|
|
margin: 20px;
|
|
}
|
|
.debug {
|
|
background: black;
|
|
color: lime;
|
|
padding: 10px;
|
|
border-radius: 5px;
|
|
margin: 10px auto;
|
|
max-width: 600px;
|
|
max-height: 200px;
|
|
overflow-y: auto;
|
|
}
|
|
button {
|
|
padding: 10px 20px;
|
|
background: #00d4ff;
|
|
border: none;
|
|
border-radius: 5px;
|
|
color: black;
|
|
font-weight: bold;
|
|
cursor: pointer;
|
|
margin: 5px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="info">
|
|
<h1>🎮 俄罗斯方块 - 游戏循环测试</h1>
|
|
<p>测试游戏循环是否正常工作</p>
|
|
</div>
|
|
|
|
<canvas id="game-canvas" width="240" height="480"></canvas>
|
|
|
|
<div class="info">
|
|
<button onclick="startGame()">▶️ 开始游戏</button>
|
|
<button onclick="pauseGame()">⏸️ 暂停</button>
|
|
<button onclick="resetGame()">🔄 重置</button>
|
|
</div>
|
|
|
|
<div class="debug" id="debug">
|
|
<div>调试信息:</div>
|
|
</div>
|
|
|
|
<script>
|
|
// 调试日志
|
|
function debugLog(message) {
|
|
const debugEl = document.getElementById('debug');
|
|
const logEntry = document.createElement('div');
|
|
logEntry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
|
|
debugEl.appendChild(logEntry);
|
|
debugEl.scrollTop = debugEl.scrollHeight;
|
|
console.log(message);
|
|
}
|
|
|
|
// 游戏配置
|
|
const canvas = document.getElementById('game-canvas');
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
const gridWidth = 10;
|
|
const gridHeight = 20;
|
|
const cellSize = 24;
|
|
|
|
let board = Array(gridHeight).fill().map(() => Array(gridWidth).fill(0));
|
|
let currentPiece = null;
|
|
let gameLoop = null;
|
|
let dropTimer = 0;
|
|
let lastFrameTime = 0;
|
|
let isPaused = false;
|
|
let isGameOver = false;
|
|
|
|
// 方块类型
|
|
const pieceTypes = [
|
|
{ type: 'I', shape: [[1,1,1,1]], color: '#00f5ff' },
|
|
{ type: 'J', shape: [[1,0,0],[1,1,1]], color: '#0000ff' },
|
|
{ type: 'L', shape: [[0,0,1],[1,1,1]], color: '#ff8000' },
|
|
{ type: 'O', shape: [[1,1],[1,1]], color: '#ffff00' },
|
|
{ type: 'S', shape: [[0,1,1],[1,1,0]], color: '#00ff00' },
|
|
{ type: 'T', shape: [[0,1,0],[1,1,1]], color: '#8000ff' },
|
|
{ type: 'Z', shape: [[1,1,0],[0,1,1]], color: '#ff0000' }
|
|
];
|
|
|
|
// 游戏函数
|
|
function spawnNewPiece() {
|
|
const randomType = pieceTypes[Math.floor(Math.random() * pieceTypes.length)];
|
|
currentPiece = {
|
|
type: randomType.type,
|
|
shape: randomType.shape,
|
|
color: randomType.color,
|
|
x: Math.floor(gridWidth / 2) - Math.floor(randomType.shape[0].length / 2),
|
|
y: 0
|
|
};
|
|
debugLog(`生成新方块: ${currentPiece.type}`);
|
|
}
|
|
|
|
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 >= gridWidth || newY >= gridHeight) {
|
|
return false;
|
|
}
|
|
|
|
if (newY >= 0 && board[newY][newX]) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function movePiece(dx, dy) {
|
|
if (!currentPiece || isPaused || isGameOver) return;
|
|
|
|
const newX = currentPiece.x + dx;
|
|
const newY = currentPiece.y + dy;
|
|
|
|
if (isValidMove(currentPiece.shape, newX, newY)) {
|
|
currentPiece.x = newX;
|
|
currentPiece.y = newY;
|
|
} else if (dy > 0) {
|
|
lockPiece();
|
|
spawnNewPiece();
|
|
|
|
if (!isValidMove(currentPiece.shape, currentPiece.x, currentPiece.y)) {
|
|
gameOver();
|
|
}
|
|
}
|
|
}
|
|
|
|
function lockPiece() {
|
|
const { shape, x, y, color } = currentPiece;
|
|
|
|
for (let row = 0; row < shape.length; row++) {
|
|
for (let col = 0; col < shape[row].length; col++) {
|
|
if (shape[row][col]) {
|
|
const boardX = x + col;
|
|
const boardY = y + row;
|
|
|
|
if (boardY >= 0) {
|
|
board[boardY][boardX] = color;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
debugLog(`方块固定在位置: (${x}, ${y})`);
|
|
}
|
|
|
|
function gameOver() {
|
|
isGameOver = true;
|
|
if (gameLoop) {
|
|
cancelAnimationFrame(gameLoop);
|
|
gameLoop = null;
|
|
}
|
|
|
|
debugLog(`游戏结束!`);
|
|
}
|
|
|
|
// 渲染函数
|
|
function render() {
|
|
// 清空画布
|
|
ctx.fillStyle = '#111827';
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
// 绘制网格
|
|
ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
|
|
ctx.lineWidth = 1;
|
|
|
|
for (let x = 0; x <= gridWidth; x++) {
|
|
ctx.beginPath();
|
|
ctx.moveTo(x * cellSize, 0);
|
|
ctx.lineTo(x * cellSize, canvas.height);
|
|
ctx.stroke();
|
|
}
|
|
|
|
for (let y = 0; y <= gridHeight; y++) {
|
|
ctx.beginPath();
|
|
ctx.moveTo(0, y * cellSize);
|
|
ctx.lineTo(canvas.width, y * cellSize);
|
|
ctx.stroke();
|
|
}
|
|
|
|
// 绘制已固定的方块
|
|
for (let y = 0; y < gridHeight; y++) {
|
|
for (let x = 0; x < gridWidth; x++) {
|
|
if (board[y][x]) {
|
|
ctx.fillStyle = board[y][x];
|
|
ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
|
|
|
|
ctx.strokeStyle = 'white';
|
|
ctx.lineWidth = 2;
|
|
ctx.strokeRect(x * cellSize, y * cellSize, cellSize, cellSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 绘制当前方块
|
|
if (currentPiece) {
|
|
const { shape, x, y, color } = currentPiece;
|
|
|
|
ctx.fillStyle = color;
|
|
for (let row = 0; row < shape.length; row++) {
|
|
for (let col = 0; col < shape[row].length; col++) {
|
|
if (shape[row][col]) {
|
|
ctx.fillRect((x + col) * cellSize, (y + row) * cellSize, cellSize, cellSize);
|
|
|
|
ctx.strokeStyle = 'white';
|
|
ctx.lineWidth = 2;
|
|
ctx.strokeRect((x + col) * cellSize, (y + row) * cellSize, cellSize, cellSize);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 游戏循环
|
|
function startGameLoop() {
|
|
if (gameLoop) {
|
|
cancelAnimationFrame(gameLoop);
|
|
}
|
|
|
|
const loop = (timestamp) => {
|
|
if (!lastFrameTime) lastFrameTime = timestamp;
|
|
|
|
const deltaTime = timestamp - lastFrameTime;
|
|
|
|
// 自动下落
|
|
if (!isPaused && !isGameOver) {
|
|
dropTimer += deltaTime;
|
|
|
|
if (dropTimer > 1000) { // 每秒下落一次
|
|
movePiece(0, 1);
|
|
dropTimer = 0;
|
|
debugLog('方块自动下落');
|
|
}
|
|
}
|
|
|
|
// 渲染
|
|
render();
|
|
|
|
// 继续循环
|
|
gameLoop = requestAnimationFrame(loop);
|
|
lastFrameTime = timestamp;
|
|
};
|
|
|
|
gameLoop = requestAnimationFrame(loop);
|
|
debugLog('✅ 游戏循环已启动');
|
|
}
|
|
|
|
// 控制函数
|
|
window.startGame = function() {
|
|
debugLog('🎮 开始游戏...');
|
|
|
|
// 重置游戏状态
|
|
board = Array(gridHeight).fill().map(() => Array(gridWidth).fill(0));
|
|
isPaused = false;
|
|
isGameOver = false;
|
|
dropTimer = 0;
|
|
lastFrameTime = 0;
|
|
|
|
// 生成第一个方块
|
|
spawnNewPiece();
|
|
|
|
// 开始游戏循环
|
|
startGameLoop();
|
|
|
|
debugLog('✅ 游戏开始!');
|
|
};
|
|
|
|
window.pauseGame = function() {
|
|
isPaused = !isPaused;
|
|
debugLog(isPaused ? '⏸️ 游戏暂停' : '▶️ 游戏继续');
|
|
};
|
|
|
|
window.resetGame = function() {
|
|
debugLog('🔄 重置游戏');
|
|
startGame();
|
|
};
|
|
|
|
// 键盘控制
|
|
document.addEventListener('keydown', (e) => {
|
|
if (!currentPiece || isPaused || isGameOver) return;
|
|
|
|
switch(e.key) {
|
|
case 'ArrowLeft':
|
|
movePiece(-1, 0);
|
|
debugLog('← 左移');
|
|
break;
|
|
case 'ArrowRight':
|
|
movePiece(1, 0);
|
|
debugLog('→ 右移');
|
|
break;
|
|
case 'ArrowDown':
|
|
movePiece(0, 1);
|
|
debugLog('↓ 下移');
|
|
break;
|
|
case 'ArrowUp':
|
|
// 旋转
|
|
debugLog('↑ 旋转');
|
|
break;
|
|
}
|
|
});
|
|
|
|
// 页面加载完成
|
|
window.addEventListener('load', () => {
|
|
debugLog('页面加载完成');
|
|
debugLog(`浏览器: ${navigator.userAgent}`);
|
|
debugLog(`平台: ${navigator.platform}`);
|
|
|
|
// 自动开始游戏
|
|
setTimeout(() => {
|
|
startGame();
|
|
}, 1000);
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |