我没使用js和css写了一个HTML页面来实现这个俄罗斯方块游戏。以下是完整的代码:
关键功能和设计介绍
HTML 结构
<canvas>
元素:
- 主要的
<canvas>
元素用于绘制游戏区域。 - 辅助的
<canvas>
元素用于显示下一个方块。
<div>
用于显示游戏信息:
- 包含当前分数、下一个方块等信息。
- 使用绝对定位的
<div>
显示游戏说明文字。
CSS 样式
- 页面背景色:
- 页面背景色为深灰色 (
#222
)。
- Flexbox 布局:
- 使用 Flexbox 使游戏区域居中,并且上下左右留白适当。
- 游戏区域样式:
canvas
元素有黑色边框 (1px solid #000
)。- 游戏区域的背景色为黑色 (
#000
)。
- 游戏说明文字:
- 绝对定位在页面顶部,居中显示,文字颜色为浅色 (
#fff
)。
JavaScript 游戏逻辑
- 画布设置:
- 默认画布尺寸为
width = 600px
和height = 800px
。 - 游戏区域的宽度为 15 列,比例为
scale = 40
。
- 方块矩阵和旋转:
- 定义了七种不同的方块类型及其对应的矩阵表示。
- 每种方块对应一个颜色。
- 方块移动与碰撞检测:
- 实现了左右移动、下落加速和旋转功能。
- 检测每次移动或旋转后的碰撞情况。
- 计时器与自动下落:
- 使用
dropCounter
控制方块的自动下落。 - 当
dropCounter
超过dropInterval
,触发playerDrop()
方法进行方块下落。
- 清除满行与得分:
arenaSweep()
方法负责检查并清除所有满行。- 每清除一行,得分增加 1。
- 游戏结束与重置:
- 如果新生成的方块与已放置的方块发生碰撞,则游戏结束。
- 在游戏结束后,游戏区域会重置,得分清零。
- 动画效果:
- 使用
requestAnimationFrame()
平滑过渡方块下落过程。
通过这些设计和功能实现,你可以获得一个流畅且直观的俄罗斯方块游戏体验。
<!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 { display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #222; margin: 0; position: relative; } .game-container { display: flex; flex-direction: column; align-items: center; gap: 20px; } canvas { border: 1px solid #000; background-color: #000; } .info-panel { color: #fff; text-align: center; font-family: 'Arial', sans-serif; } .instructions { position: absolute; top: 10px; left: 50%; transform: translateX(-50%); color: #fff; font-family: 'Arial', sans-serif; } </style> </head> <body> <div class="instructions"> 使用方向键移动和旋转方块。按 Q/W 顺时针/逆时针旋转。 </div> <div class="game-container"> <div class="info-panel"> 得分: <span id="score">0</span><br> 下一个方块:<br> <canvas id="next-canvas" width="160" height="160"></canvas> </div> <canvas id="tetris-canvas" width="600" height="800"></canvas> </div> <script> const canvas = document.getElementById('tetris-canvas'); const context = canvas.getContext('2d'); const nextCanvas = document.getElementById('next-canvas'); const nextContext = nextCanvas.getContext('2d'); const scale = 40; // 每个方块的大小为 40x40 像素 const arenaWidth = 15; // 游戏区域宽度为 15 列 const arenaHeight = 20; // 游戏区域高度为 20 行 const colors = [ null, '#FF0D72', // T 形方块颜色 '#0DC2FF', // O 形方块颜色 '#0DFF72', // L 形方块颜色 '#F538FF', // J 形方块颜色 '#FF8E0D', // I 形方块颜色 '#FFE138', // S 形方块颜色 '#3877FF' // Z 形方块颜色 ]; const shapes = { T: [[0, 1, 0], [1, 1, 1], [0, 0, 0]], // T 形方块矩阵 O: [[2, 2], [2, 2]], // O 形方块矩阵 L: [[0, 0, 3], [3, 3, 3], [0, 0, 0]], // L 形方块矩阵 J: [[4, 0, 0], [4, 4, 4], [0, 0, 0]], // J 形方块矩阵 I: [[0, 5, 0, 0], [0, 5, 0, 0], [0, 5, 0, 0], [0, 5, 0, 0]], // I 形方块矩阵 S: [[0, 6, 6], [6, 6, 0], [0, 0, 0]], // S 形方块矩阵 Z: [[7, 7, 0], [0, 7, 7], [0, 0, 0]] // Z 形方块矩阵 }; function createMatrix(w, h) { const matrix = []; while (h--) { matrix.push(new Array(w).fill(0)); // 创建一个 w x h 的矩阵,初始值为 0 } return matrix; } function draw(matrix, offset, ctx) { ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // 清除画布 matrix.forEach((row, y) => { row.forEach((value, x) => { if (value !== 0) { ctx.fillStyle = colors[value]; // 设置填充颜色 ctx.fillRect((x + offset.x) * scale, (y + offset.y) * scale, scale, scale); // 绘制方块 ctx.strokeStyle = '#000'; // 设置描边颜色 ctx.strokeRect((x + offset.x) * scale, (y + offset.y) * scale, scale, scale); // 描绘方块边框 } }); }); } function merge(arena, player) { player.matrix.forEach((row, y) => { row.forEach((value, x) => { if (value !== 0) { arena[y + player.pos.y][x + player.pos.x] = value; // 合并玩家方块到游戏区域 } }); }); } function collide(arena, player) { const [m, o] = [player.matrix, player.pos]; for (let y = 0; y < m.length; ++y) { for (let x = 0; x < m[y].length; ++x) { if (m[y][x] !== 0 && (arena[y + o.y] && arena[y + o.y][x + o.x]) !== 0) { return true; // 发生碰撞 } } } return false; // 未发生碰撞 } function rotate(matrix, dir) { for (let y = 0; y < matrix.length; ++y) { for (let x = 0; x < y; ++x) { [matrix[x][y], matrix[y][x]] = [matrix[y][x], matrix[x][y]]; // 转置矩阵 } } if (dir > 0) { matrix.forEach(row => row.reverse()); // 顺时针旋转 } else { matrix.reverse(); // 逆时针旋转 } } let dropCounter = 0; let dropInterval = 1000; // 方块自动下落的时间间隔 let lastTime = 0; function update(time = 0) { const deltaTime = time - lastTime; lastTime = time; dropCounter += deltaTime; if (dropCounter > dropInterval) { playerDrop(); } draw(arena, { x: 0, y: 0 }, context); // 绘制游戏区域 draw(player.matrix, player.pos, context); // 绘制玩家方块 requestAnimationFrame(update); // 请求下一帧动画 } function playerReset() { const pieces = Object.keys(shapes); player.matrix = shapes[pieces[Math.floor(Math.random() * pieces.length)]]; // 随机选择一个方块 nextPlayer.matrix = shapes[pieces[Math.floor(Math.random() * pieces.length)]]; // 初始化下一个方块 showNextPiece(); // 显示新的下一个方块 player.pos.y = 0; // 初始化位置 player.pos.x = ((arena[0].length / 2) | 0) - ((player.matrix[0].length / 2) | 0); // 居中对齐 if (collide(arena, player)) { arena.forEach(row => row.fill(0)); // 清空游戏区域 scoreElement.innerText = score = 0; // 重置得分 } } function playerMove(offset) { player.pos.x += offset; // 移动方块 if (collide(arena, player)) { player.pos.x -= offset; // 如果发生碰撞,则回退 } } function playerRotate(dir) { const pos = player.pos.x; let offset = 1; rotate(player.matrix, dir); // 旋转方块 while (collide(arena, player)) { player.pos.x += offset; offset = -(offset + (offset > 0 ? 1 : -1)); if (offset > player.matrix[0].length) { rotate(player.matrix, -dir); // 恢复原状 player.pos.x = pos; return; } } } function playerDrop() { player.pos.y++; // 方块下落一行 if (collide(arena, player)) { player.pos.y--; // 回退 merge(arena, player); // 合并方块到游戏区域 playerReset(); // 重置玩家方块 arenaSweep(); // 清除满行 } dropCounter = 0; // 重置计时器 } function arenaSweep() { let rowCount = 1; outer: for (let y = arena.length - 1; y > 0; --y) { for (let x = 0; x < arena[y].length; ++x) { if (arena[y][x] === 0) { continue outer; // 如果该行有空格,则跳过 } } const row = arena.splice(y, 1)[0].fill(0); // 删除满行 arena.unshift(row); // 在顶部插入新行 ++y; score += rowCount * 10; // 更新得分 rowCount *= 2; } scoreElement.innerText = score; // 显示得分 } function createPiece(type) { return shapes[type]; // 根据类型返回对应的矩阵 } document.addEventListener('keydown', event => { if (event.key === 'ArrowLeft') { playerMove(-1); // 左移 } else if (event.key === 'ArrowRight') { playerMove(1); // 右移 } else if (event.key === 'ArrowDown') { playerDrop(); // 加速下落 } else if (event.key === 'Q') { playerRotate(-1); // 逆时针旋转 } else if (event.key === 'W') { playerRotate(1); // 顺时针旋转 } }); const arena = createMatrix(arenaWidth, arenaHeight); // 创建游戏区域矩阵 const player = { pos: { x: 0, y: 0 }, matrix: null }; const nextPlayer = { pos: { x: 0, y: 0 }, matrix: null }; const scoreElement = document.getElementById('score'); // 获取得分元素 let score = 0; // 初始化得分 playerReset(); // 初始化玩家方块 update(); // 开始游戏循环 function showNextPiece() { nextContext.clearRect(0, 0, nextCanvas.width, nextCanvas.height); // 清除下一个方块显示区域 draw(nextPlayer.matrix, { x: 0, y: 0 }, nextContext); // 绘制下一个方块 } setInterval(() => { nextPlayer.matrix = createPiece(Object.keys(shapes)[Math.floor(Math.random() * Object.keys(shapes).length)]); // 更新下一个方块 showNextPiece(); // 显示下一个方块 }, 5000); // 每 5 秒更新一次 showNextPiece(); // 初始化显示下一个方块 </script> </body> </html>
No responses yet