我没使用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