简介
俄罗斯方块是史上最经典的益智游戏之一。玩家操控下落的七种方块,填满横行即可消除得分。游戏厅版本完整实现了标准方块旋转系统、消行判定、预览下一个方块和速度递增。
数据结构
棋盘
使用二维数组表示 10x20 的游戏区域:
1var COLS = 10, ROWS = 20;
2var board = [];
3for (var r = 0; r < ROWS; r++) {
4 board[r] = [];
5 for (var c = 0; c < COLS; c++) board[r][c] = 0; // 0 = 空, 1-7 = 不同方块颜色
6}
七种方块定义
每种方块用 4x4 矩阵表示其形状:
1var PIECES = [
2 // I 型
3 [[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]],
4 // O 型
5 [[1,1],[1,1]],
6 // T 型
7 [[0,1,0],[1,1,1],[0,0,0]],
8 // S 型
9 [[0,1,1],[1,1,0],[0,0,0]],
10 // Z 型
11 [[1,1,0],[0,1,1],[0,0,0]],
12 // J 型
13 [[1,0,0],[1,1,1],[0,0,0]],
14 // L 型
15 [[0,0,1],[1,1,1],[0,0,0]]
16];
17
18var PIECE_COLORS = [
19 '#00f0f0', // I - 青色
20 '#f0f000', // O - 黄色
21 '#a000f0', // T - 紫色
22 '#00f000', // S - 绿色
23 '#f00000', // Z - 红色
24 '#0000f0', // J - 蓝色
25 '#f0a000' // L - 橙色
26];
当前方块状态
1var currentPiece = {
2 type: 0, // 方块类型索引
3 shape: null, // 当前形状矩阵(旋转后)
4 x: 3, // 左上角 X 坐标
5 y: 0 // 左上角 Y 坐标
6};
核心算法
方块旋转
将方块矩阵顺时针旋转 90 度:
1function rotate(shape) {
2 var n = shape.length;
3 var rotated = [];
4 for (var r = 0; r < n; r++) {
5 rotated[r] = [];
6 for (var c = 0; c < n; c++) {
7 rotated[r][c] = shape[n - 1 - c][r];
8 }
9 }
10 return rotated;
11}
旋转后检查是否与棋盘或边界碰撞,如果碰撞则尝试左右平移(墙踢):
1function tryRotate() {
2 var rotated = rotate(currentPiece.shape);
3 var offsets = [0, -1, 1, -2, 2]; // 墙踢偏移量
4 for (var i = 0; i < offsets.length; i++) {
5 if (!collides(rotated, currentPiece.x + offsets[i], currentPiece.y)) {
6 currentPiece.shape = rotated;
7 currentPiece.x += offsets[i];
8 return;
9 }
10 }
11}
碰撞检测
检查方块是否与棋盘边界或已有方块重叠:
1function collides(shape, px, py) {
2 for (var r = 0; r < shape.length; r++)
3 for (var c = 0; c < shape[r].length; c++) {
4 if (!shape[r][c]) continue;
5 var newX = px + c;
6 var newY = py + r;
7 if (newX < 0 || newX >= COLS || newY >= ROWS) return true;
8 if (newY >= 0 && board[newY][newX]) return true;
9 }
10 return false;
11}
方块锁定与消行
方块落到底部后锁定到棋盘,然后检查消行:
1function lockPiece() {
2 // 将方块写入棋盘
3 for (var r = 0; r < currentPiece.shape.length; r++)
4 for (var c = 0; c < currentPiece.shape[r].length; c++) {
5 if (!currentPiece.shape[r][c]) continue;
6 var y = currentPiece.y + r;
7 var x = currentPiece.x + c;
8 if (y >= 0) board[y][x] = currentPiece.type + 1;
9 }
10 clearLines();
11 spawnPiece();
12}
13
14function clearLines() {
15 var linesCleared = 0;
16 for (var r = ROWS - 1; r >= 0; r--) {
17 if (board[r].every(function(cell) { return cell !== 0; })) {
18 // 移除该行,上方所有行下移
19 board.splice(r, 1);
20 board.unshift(new Array(COLS).fill(0));
21 linesCleared++;
22 r++; // 重新检查当前行
23 }
24 }
25 if (linesCleared > 0) {
26 score += [0, 100, 300, 500, 800][linesCleared];
27 }
28}
预览下一个方块
生成随机方块预览,让玩家提前规划:
1var nextPiece = Math.floor(Math.random() * PIECES.length);
2
3function spawnPiece() {
4 currentPiece.type = nextPiece;
5 currentPiece.shape = PIECES[currentPiece.type].map(function(row) { return row.slice(); });
6 currentPiece.x = Math.floor((COLS - currentPiece.shape[0].length) / 2);
7 currentPiece.y = 0;
8 nextPiece = Math.floor(Math.random() * PIECES.length);
9 renderNextPiece();
10 if (collides(currentPiece.shape, currentPiece.x, currentPiece.y)) {
11 gameOver();
12 }
13}
Canvas 渲染
绘制棋盘
1function drawBoard() {
2 for (var r = 0; r < ROWS; r++)
3 for (var c = 0; c < COLS; c++) {
4 var x = c * CELL_SIZE, y = r * CELL_SIZE;
5 if (board[r][c]) {
6 ctx.fillStyle = PIECE_COLORS[board[r][c] - 1];
7 ctx.fillRect(x + 1, y + 1, CELL_SIZE - 2, CELL_SIZE - 2);
8 // 高光效果
9 ctx.fillStyle = 'rgba(255,255,255,0.2)';
10 ctx.fillRect(x + 1, y + 1, CELL_SIZE - 2, 3);
11 } else {
12 ctx.fillStyle = '#1a1a2e';
13 ctx.fillRect(x, y, CELL_SIZE, CELL_SIZE);
14 }
15 }
16}
绘制当前方块
1function drawCurrentPiece() {
2 var shape = currentPiece.shape;
3 var color = PIECE_COLORS[currentPiece.type];
4 for (var r = 0; r < shape.length; r++)
5 for (var c = 0; c < shape[r].length; c++) {
6 if (!shape[r][c]) continue;
7 var x = (currentPiece.x + c) * CELL_SIZE;
8 var y = (currentPiece.y + r) * CELL_SIZE;
9 if (y < 0) continue; // 超出顶部不绘制
10 ctx.fillStyle = color;
11 ctx.fillRect(x + 1, y + 1, CELL_SIZE - 2, CELL_SIZE - 2);
12 ctx.fillStyle = 'rgba(255,255,255,0.2)';
13 ctx.fillRect(x + 1, y + 1, CELL_SIZE - 2, 3);
14 }
15}
操作控制
- ← →:左右移动
- ↓:软降(加速下落)
- ↑:旋转
- 空格:硬降(直接落到底部)
1document.addEventListener('keydown', function(e) {
2 if (!isActive()) return;
3 switch(e.key) {
4 case 'ArrowLeft': movePiece(-1, 0); break;
5 case 'ArrowRight': movePiece(1, 0); break;
6 case 'ArrowDown': movePiece(0, 1); break;
7 case 'ArrowUp': tryRotate(); break;
8 case ' ': hardDrop(); break;
9 }
10 e.preventDefault();
11});
速度递增
随分数提高,方块下落速度加快:
1function getDropInterval() {
2 if (score >= 2000) return 100;
3 if (score >= 1500) return 200;
4 if (score >= 1000) return 300;
5 if (score >= 500) return 500;
6 return 800;
7}
总结
俄罗斯方块的核心在于方块旋转矩阵变换、墙踢偏移补偿和消行逻辑。Canvas 渲染提供了灵活的绘制控制,预览下一个方块增加了策略性。速度递增机制让游戏难度曲线平滑上升。
留言评论
期待你的想法评论加载中