deepseek v3 0324实现俄罗斯方块游戏 (HTML + Canvas 实现)
俄罗斯方块游戏 (HTML + Canvas 实现)
下面是一个完整的俄罗斯方块游戏实现,使用HTML5 Canvas渲染,具有美观的界面和完整的游戏功能。
<!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 {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
font-family: 'Arial', sans-serif;
color: white;
overflow: hidden;
}
.game-container {
display: flex;
gap: 30px;
align-items: flex-start;
}
#game-board {
border: 4px solid rgba(255, 255, 255, 0.3);
border-radius: 8px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
background-color: rgba(0, 0, 0, 0.2);
}
.side-panel {
display: flex;
flex-direction: column;
gap: 20px;
width: 200px;
}
.panel {
background-color: rgba(0, 0, 0, 0.2);
border: 2px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
padding: 15px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
}
h2 {
margin: 0 0 15px 0;
font-size: 20px;
text-align: center;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}
.score {
font-size: 24px;
font-weight: bold;
text-align: center;
margin: 10px 0;
}
#next-piece {
width: 160px;
height: 160px;
margin: 0 auto;
}
.controls {
margin-top: 20px;
}
.controls p {
margin: 5px 0;
font-size: 14px;
}
button {
display: block;
width: 100%;
padding: 10px;
margin-top: 10px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 4px;
color: white;
cursor: pointer;
transition: all 0.3s;
}
button:hover {
background: rgba(255, 255, 255, 0.2);
}
.game-over {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 100;
display: none;
}
.game-over h1 {
font-size: 48px;
margin-bottom: 20px;
color: #ff5555;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.8);
}
.game-over button {
width: 200px;
padding: 15px;
font-size: 18px;
}
</style>
</head>
<body>
<div class="game-container">
<canvas id="game-board" width="300" height="600"></canvas>
<div class="side-panel">
<div class="panel">
<h2>分数</h2>
<div class="score" id="score">0</div>
<h2>等级</h2>
<div class="score" id="level">1</div>
<h2>行数</h2>
<div class="score" id="lines">0</div>
</div>
<div class="panel">
<h2>下一个</h2>
<canvas id="next-piece" width="160" height="160"></canvas>
</div>
<div class="panel controls">
<h2>操作</h2>
<p>← → : 左右移动</p>
<p>↑ : 旋转</p>
<p>↓ : 加速下落</p>
<p>空格 : 直接落下</p>
<button id="pause-btn">暂停 (P)</button>
<button id="new-game-btn">新游戏 (N)</button>
</div>
</div>
</div>
<div class="game-over" id="game-over">
<h1>游戏结束!</h1>
<div class="score" id="final-score">分数: 0</div>
<button id="restart-btn">重新开始</button>
</div>
<script>
// 游戏常量
const COLS = 10;
const ROWS = 20;
const BLOCK_SIZE = 30;
const COLORS = [
null,
'#FF0D72', // I
'#0DC2FF', // J
'#0DFF72', // L
'#F538FF', // O
'#FF8E0D', // S
'#FFE138', // T
'#3877FF' // Z
];
// 方块形状定义
const SHAPES = [
null,
[[0, 0, 0, 0], [1, 1, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0]], // I
[[2, 0, 0], [2, 2, 2], [0, 0, 0]], // J
[[0, 0, 3], [3, 3, 3], [0, 0, 0]], // L
[[0, 4, 4], [0, 4, 4], [0, 0, 0]], // O
[[0, 5, 5], [5, 5, 0], [0, 0, 0]], // S
[[0, 6, 0], [6, 6, 6], [0, 0, 0]], // T
[[7, 7, 0], [0, 7, 7], [0, 0, 0]] // Z
];
// 游戏变量
let canvas, ctx;
let nextCanvas, nextCtx;
let board;
let piece, nextPiece;
let score, level, lines;
let dropCounter, dropInterval;
let lastTime;
let gameOver, paused;
let animationId;
// 初始化游戏
function init() {
canvas = document.getElementById('game-board');
ctx = canvas.getContext('2d');
nextCanvas = document.getElementById('next-piece');
nextCtx = nextCanvas.getContext('2d');
// 设置画布实际大小
canvas.width = COLS * BLOCK_SIZE;
canvas.height = ROWS * BLOCK_SIZE;
// 缩放画布以适应CSS大小
canvas.style.width = `${COLS * BLOCK_SIZE}px`;
canvas.style.height = `${ROWS * BLOCK_SIZE}px`;
ctx.scale(1, 1);
resetGame();
addEventListeners();
lastTime = 0;
animationId = requestAnimationFrame(update);
}
// 重置游戏状态
function resetGame() {
board = createMatrix(COLS, ROWS);
score = 0;
level = 1;
lines = 0;
dropInterval = 1000;
gameOver = false;
paused = false;
updateScore();
// 创建第一个方块和下一个方块
piece = createPiece(Math.floor(Math.random() * 7) + 1);
nextPiece = createPiece(Math.floor(Math.random() * 7) + 1);
document.getElementById('pause-btn').textContent = '暂停 (P)';
document.getElementById('game-over').style.display = 'none';
}
// 创建矩阵
function createMatrix(width, height) {
const matrix = [];
while (height--) {
matrix.push(new Array(width).fill(0));
}
return matrix;
}
// 创建方块
function createPiece(type) {
return {
position: {x: Math.floor(COLS / 2) - 1, y: 0},
shape: SHAPES[type],
type: type
};
}
// 绘制矩阵
function drawMatrix(matrix, offset) {
matrix.forEach((row, y) => {
row.forEach((value, x) => {
if (value !== 0) {
ctx.fillStyle = COLORS[value];
ctx.fillRect(
x + offset.x,
y + offset.y,
1, 1
);
// 添加方块边框和光泽效果
ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
ctx.lineWidth = 0.05;
ctx.strokeRect(
x + offset.x,
y + offset.y,
1, 1
);
// 添加光泽效果
ctx.fillStyle = 'rgba(255, 255, 255, 0.1)';
ctx.fillRect(
x + offset.x + 0.1,
y + offset.y + 0.1,
0.3, 0.3
);
}
});
});
}
// 绘制下一个方块预览
function drawNextPiece() {
nextCtx.clearRect(0, 0, nextCanvas.width, nextCanvas.height);
// 计算居中位置
const offsetX = (nextCanvas.width / BLOCK_SIZE - nextPiece.shape[0].length) / 2;
const offsetY = (nextCanvas.height / BLOCK_SIZE - nextPiece.shape.length) / 2;
nextPiece.shape.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 = 'rgba(255, 255, 255, 0.2)';
nextCtx.lineWidth = 2;
nextCtx.strokeRect(
(x + offsetX) * BLOCK_SIZE,
(y + offsetY) * BLOCK_SIZE,
BLOCK_SIZE, BLOCK_SIZE
);
// 添加光泽效果
nextCtx.fillStyle = 'rgba(255, 255, 255, 0.1)';
nextCtx.fillRect(
(x + offsetX) * BLOCK_SIZE + 3,
(y + offsetY) * BLOCK_SIZE + 3,
BLOCK_SIZE * 0.3, BLOCK_SIZE * 0.3
);
}
});
});
}
// 绘制游戏板
function draw() {
// 清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制游戏板
drawMatrix(board, {x: 0, y: 0});
// 绘制当前方块
drawMatrix(piece.shape, piece.position);
// 绘制网格线
ctx.strokeStyle = 'rgba(255, 255, 255, 0.05)';
ctx.lineWidth = 0.5;
// 垂直线
for (let i = 0; i <= COLS; i++) {
ctx.beginPath();
ctx.moveTo(i * BLOCK_SIZE, 0);
ctx.lineTo(i * BLOCK_SIZE, ROWS * BLOCK_SIZE);
ctx.stroke();
}
// 水平线
for (let i = 0; i <= ROWS; i++) {
ctx.beginPath();
ctx.moveTo(0, i * BLOCK_SIZE);
ctx.lineTo(COLS * BLOCK_SIZE, i * BLOCK_SIZE);
ctx.stroke();
}
// 绘制下一个方块
drawNextPiece();
}
// 合并方块到游戏板
function merge() {
piece.shape.forEach((row, y) => {
row.forEach((value, x) => {
if (value !== 0) {
board[y + piece.position.y][x + piece.position.x] = value;
}
});
});
}
// 碰撞检测
function collide() {
const [m, o] = [piece.shape, piece.position];
for (let y = 0; y < m.length; ++y) {
for (let x = 0; x < m[y].length; ++x) {
if (m[y][x] !== 0 &&
(board[y + o.y] === undefined ||
board[y + o.y][x + o.x] === undefined ||
board[y + o.y][x + o.x] !== 0)) {
return true;
}
}
}
return false;
}
// 旋转方块
function rotate() {
const originalShape = piece.shape;
// 转置矩阵
for (let y = 0; y < piece.shape.length; ++y) {
for (let x = 0; x < y; ++x) {
[piece.shape[x][y], piece.shape[y][x]] =
[piece.shape[y][x], piece.shape[x][y]];
}
}
// 反转每一行
piece.shape.forEach(row => row.reverse());
// 如果旋转后发生碰撞,则恢复原状
if (collide()) {
piece.shape = originalShape;
}
}
// 移动方块
function movePiece(direction) {
piece.position.x += direction;
if (collide()) {
piece.position.x -= direction;
}
}
// 方块下落
function dropPiece() {
piece.position.y++;
if (collide()) {
piece.position.y--;
merge();
removeLines();
// 检查游戏是否结束
if (piece.position.y === 0) {
gameOver = true;
document.getElementById('final-score').textContent = `分数: ${score}`;
document.getElementById('game-over').style.display = 'flex';
return;
}
// 生成新方块
piece = nextPiece;
nextPiece = createPiece(Math.floor(Math.random() * 7) + 1);
}
dropCounter = 0;
}
// 快速下落
function hardDrop() {
while (!collide()) {
piece.position.y++;
}
piece.position.y--;
dropPiece();
}
// 移除已填满的行
function removeLines() {
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 outer;
}
}
// 移除当前行
const row = board.splice(y, 1)[0].fill(0);
board.unshift(row);
++y; // 重新检查当前行,因为所有行都下移了一行
linesCleared++;
}
if (linesCleared > 0) {
// 更新分数
updateScore(linesCleared);
}
}
// 更新分数
function updateScore(linesCleared = 0) {
if (linesCleared > 0) {
// 根据消除的行数计算得分
const points = [0, 100, 300, 500, 800][linesCleared] * level;
score += points;
lines += linesCleared;
// 每消除10行升一级
level = Math.floor(lines / 10) + 1;
// 随着等级提高,下落速度加快
dropInterval = Math.max(100, 1000 - (level - 1) * 100);
}
document.getElementById('score').textContent = score;
document.getElementById('level').textContent = level;
document.getElementById('lines').textContent = lines;
}
// 暂停/继续游戏
function togglePause() {
paused = !paused;
document.getElementById('pause-btn').textContent = paused ? '继续 (P)' : '暂停 (P)';
if (!paused) {
lastTime = 0;
animationId = requestAnimationFrame(update);
} else {
cancelAnimationFrame(animationId);
}
}
// 游戏主循环
function update(time = 0) {
if (gameOver || paused) return;
const deltaTime = time - lastTime;
lastTime = time;
dropCounter += deltaTime;
if (dropCounter > dropInterval) {
dropPiece();
}
draw();
animationId = requestAnimationFrame(update);
}
// 添加事件监听器
function addEventListeners() {
document.addEventListener('keydown', event => {
if (gameOver) return;
if (event.key === 'p' || event.key === 'P') {
togglePause();
return;
}
if (paused) return;
switch (event.key) {
case 'ArrowLeft':
movePiece(-1);
break;
case 'ArrowRight':
movePiece(1);
break;
case 'ArrowDown':
dropPiece();
break;
case 'ArrowUp':
rotate();
break;
case ' ':
hardDrop();
break;
}
});
document.getElementById('pause-btn').addEventListener('click', togglePause);
document.getElementById('new-game-btn').addEventListener('click', () => {
cancelAnimationFrame(animationId);
resetGame();
lastTime = 0;
animationId = requestAnimationFrame(update);
});
document.getElementById('restart-btn').addEventListener('click', () => {
cancelAnimationFrame(animationId);
resetGame();
lastTime = 0;
animationId = requestAnimationFrame(update);
});
}
// 启动游戏
window.addEventListener('load', init);
</script>
</body>
</html>
游戏功能说明
-
基本功能:
- 完整的俄罗斯方块游戏逻辑
- 7种经典方块形状
- 方块旋转、移动、加速下落和直接落下
- 行消除和分数计算
- 等级系统(下落速度随等级提高)
- 下一个方块预览
-
操作方式:
- 左右箭头:移动方块
- 上箭头:旋转方块
- 下箭头:加速下落
- 空格键:直接落下
- P键:暂停/继续游戏
- N键:新游戏
-
界面特点:
- 渐变背景和半透明面板
- 精美的方块渲染(带光泽效果)
- 游戏网格线
- 分数、等级和消除行数显示
- 游戏结束画面
-
响应式设计:
- 游戏界面居中显示
- 适合各种屏幕尺寸
您可以将此代码保存为HTML文件,然后在浏览器中打开即可开始游戏。游戏完全在客户端运行,不需要任何服务器支持。