Files
tetris-game/script.js
OpenClaw Xiami 867611ab45 初始提交:网页版俄罗斯方块游戏
- 使用 HTML + CSS + JavaScript 实现
- 实现完整的游戏逻辑:方块移动、旋转、消行、计分
- 支持等级系统和加速机制
- 响应式设计,支持移动端
- 下一个方块预览功能
- 游戏暂停/继续功能
2026-02-28 15:56:49 +00:00

387 lines
9.2 KiB
JavaScript

// 游戏常量
const COLS = 10;
const ROWS = 20;
const BLOCK_SIZE = 30;
// 方块形状定义
const SHAPES = [
[[1, 1, 1, 1]], // I
[[1, 1], [1, 1]], // O
[[0, 1, 0], [1, 1, 1]], // T
[[1, 0, 0], [1, 1, 1]], // L
[[0, 0, 1], [1, 1, 1]], // J
[[0, 1, 1], [1, 1, 0]], // S
[[1, 1, 0], [0, 1, 1]] // Z
];
// 方块颜色
const COLORS = [
'#00f0f0', // I - 青色
'#f0f000', // O - 黄色
'#a000f0', // T - 紫色
'#f0a000', // L - 橙色
'#0000f0', // J - 蓝色
'#00f000', // S - 绿色
'#f00000' // Z - 红色
];
// 游戏状态
let canvas, ctx, nextCanvas, nextCtx;
let board = [];
let currentPiece = null;
let nextPiece = null;
let score = 0;
let level = 1;
let lines = 0;
let gameLoop = null;
let isPaused = false;
let isGameOver = false;
let dropInterval = 1000;
// 初始化
function init() {
canvas = document.getElementById('game-canvas');
ctx = canvas.getContext('2d');
nextCanvas = document.getElementById('next-canvas');
nextCtx = nextCanvas.getContext('2d');
// 初始化游戏板
for (let r = 0; r < ROWS; r++) {
board[r] = [];
for (let c = 0; c < COLS; c++) {
board[r][c] = 0;
}
}
// 绑定事件
document.addEventListener('keydown', handleKeyPress);
document.getElementById('start-btn').addEventListener('click', startGame);
document.getElementById('restart-btn').addEventListener('click', startGame);
// 绘制初始游戏板
drawBoard();
}
// 创建新方块
function createPiece() {
const shapeIndex = Math.floor(Math.random() * SHAPES.length);
return {
shape: SHAPES[shapeIndex],
color: COLORS[shapeIndex],
x: Math.floor(COLS / 2) - Math.floor(SHAPES[shapeIndex][0].length / 2),
y: 0
};
}
// 绘制方块
function drawBlock(ctx, x, y, color, size = BLOCK_SIZE) {
ctx.fillStyle = color;
ctx.fillRect(x * size, y * size, size, size);
// 添加3D效果
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
ctx.fillRect(x * size, y * size, size, 2);
ctx.fillRect(x * size, y * size, 2, size);
ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
ctx.fillRect(x * size + size - 2, y * size, 2, size);
ctx.fillRect(x * size, y * size + size - 2, size, 2);
}
// 绘制游戏板
function drawBoard() {
// 清空画布
ctx.fillStyle = '#1a1a2e';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制网格
ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
for (let r = 0; r < ROWS; r++) {
for (let c = 0; c < COLS; c++) {
ctx.strokeRect(c * BLOCK_SIZE, r * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
}
}
// 绘制已放置的方块
for (let r = 0; r < ROWS; r++) {
for (let c = 0; c < COLS; c++) {
if (board[r][c]) {
drawBlock(ctx, c, r, board[r][c]);
}
}
}
}
// 绘制当前方块
function drawPiece() {
if (!currentPiece) return;
for (let r = 0; r < currentPiece.shape.length; r++) {
for (let c = 0; c < currentPiece.shape[r].length; c++) {
if (currentPiece.shape[r][c]) {
drawBlock(ctx, currentPiece.x + c, currentPiece.y + r, currentPiece.color);
}
}
}
}
// 绘制下一个方块
function drawNextPiece() {
if (!nextPiece) return;
nextCtx.fillStyle = '#fff';
nextCtx.fillRect(0, 0, nextCanvas.width, nextCanvas.height);
const size = 20;
const offsetX = (nextCanvas.width - nextPiece.shape[0].length * size) / 2;
const offsetY = (nextCanvas.height - nextPiece.shape.length * size) / 2;
for (let r = 0; r < nextPiece.shape.length; r++) {
for (let c = 0; c < nextPiece.shape[r].length; c++) {
if (nextPiece.shape[r][c]) {
nextCtx.fillStyle = nextPiece.color;
nextCtx.fillRect(offsetX + c * size, offsetY + r * size, size, size);
nextCtx.strokeStyle = 'rgba(0, 0, 0, 0.3)';
nextCtx.strokeRect(offsetX + c * size, offsetY + r * size, size, size);
}
}
}
}
// 检测碰撞
function collide(piece, offsetX = 0, offsetY = 0) {
for (let r = 0; r < piece.shape.length; r++) {
for (let c = 0; c < piece.shape[r].length; c++) {
if (piece.shape[r][c]) {
const newX = piece.x + c + offsetX;
const newY = piece.y + r + offsetY;
if (newX < 0 || newX >= COLS || newY >= ROWS) {
return true;
}
if (newY >= 0 && board[newY][newX]) {
return true;
}
}
}
}
return false;
}
// 锁定方块
function lockPiece() {
for (let r = 0; r < currentPiece.shape.length; r++) {
for (let c = 0; c < currentPiece.shape[r].length; c++) {
if (currentPiece.shape[r][c]) {
if (currentPiece.y + r < 0) {
gameOver();
return;
}
board[currentPiece.y + r][currentPiece.x + c] = currentPiece.color;
}
}
}
clearLines();
currentPiece = nextPiece;
nextPiece = createPiece();
drawNextPiece();
if (collide(currentPiece)) {
gameOver();
}
}
// 消除行
function clearLines() {
let linesCleared = 0;
for (let r = ROWS - 1; r >= 0; r--) {
if (board[r].every(cell => cell !== 0)) {
board.splice(r, 1);
board.unshift(new Array(COLS).fill(0));
linesCleared++;
r++;
}
}
if (linesCleared > 0) {
updateScore(linesCleared);
}
}
// 更新分数
function updateScore(linesCleared) {
const points = [0, 100, 300, 500, 800];
score += points[linesCleared] * level;
lines += linesCleared;
// 升级
if (lines >= level * 10) {
level++;
dropInterval = Math.max(100, 1000 - (level - 1) * 100);
resetGameLoop();
}
updateDisplay();
}
// 更新显示
function updateDisplay() {
document.getElementById('score').textContent = score;
document.getElementById('level').textContent = level;
document.getElementById('lines').textContent = lines;
}
// 移动方块
function movePiece(offsetX, offsetY) {
if (!currentPiece || isPaused || isGameOver) return;
if (!collide(currentPiece, offsetX, offsetY)) {
currentPiece.x += offsetX;
currentPiece.y += offsetY;
draw();
}
}
// 旋转方块
function rotatePiece() {
if (!currentPiece || isPaused || isGameOver) return;
const rotated = currentPiece.shape[0].map((_, index) =>
currentPiece.shape.map(row => row[index]).reverse()
);
const originalShape = currentPiece.shape;
currentPiece.shape = rotated;
// 墙踢 - 尝试调整位置
const kicks = [0, -1, 1, -2, 2];
for (let kick of kicks) {
if (!collide(currentPiece, kick, 0)) {
currentPiece.x += kick;
draw();
return;
}
}
// 无法旋转,恢复原状
currentPiece.shape = originalShape;
}
// 硬降落
function hardDrop() {
if (!currentPiece || isPaused || isGameOver) return;
while (!collide(currentPiece, 0, 1)) {
currentPiece.y++;
score += 2;
}
lockPiece();
updateDisplay();
draw();
}
// 绘制
function draw() {
drawBoard();
drawPiece();
}
// 游戏循环
function gameStep() {
if (!isPaused && !isGameOver) {
if (!collide(currentPiece, 0, 1)) {
currentPiece.y++;
draw();
} else {
lockPiece();
}
}
}
// 开始游戏循环
function startGameLoop() {
gameLoop = setInterval(gameStep, dropInterval);
}
// 重置游戏循环
function resetGameLoop() {
clearInterval(gameLoop);
startGameLoop();
}
// 开始游戏
function startGame() {
// 重置状态
for (let r = 0; r < ROWS; r++) {
for (let c = 0; c < COLS; c++) {
board[r][c] = 0;
}
}
score = 0;
level = 1;
lines = 0;
dropInterval = 1000;
isPaused = false;
isGameOver = false;
// 隐藏游戏结束界面
document.getElementById('game-over').classList.add('hidden');
// 创建方块
currentPiece = createPiece();
nextPiece = createPiece();
updateDisplay();
drawNextPiece();
draw();
startGameLoop();
}
// 游戏结束
function gameOver() {
isGameOver = true;
clearInterval(gameLoop);
document.getElementById('final-score').textContent = score;
document.getElementById('game-over').classList.remove('hidden');
}
// 键盘事件处理
function handleKeyPress(e) {
if (isGameOver) return;
switch (e.key) {
case 'ArrowLeft':
e.preventDefault();
movePiece(-1, 0);
break;
case 'ArrowRight':
e.preventDefault();
movePiece(1, 0);
break;
case 'ArrowDown':
e.preventDefault();
movePiece(0, 1);
score += 1;
updateDisplay();
break;
case 'ArrowUp':
e.preventDefault();
rotatePiece();
break;
case ' ':
e.preventDefault();
hardDrop();
break;
case 'p':
case 'P':
isPaused = !isPaused;
break;
}
}
// 启动
window.onload = init;