四格骨牌与方块绘制
在本节课中,你将了解如何使用类来表示俄罗斯方块的方块,并学习如何将它们绘制在画布上。
俄罗斯方块中的一个方块是由四个方块组成的一个整体形状,通常称为四格骨牌(tetromino),共有七种不同的形状和颜色。这些形状的名称 I、J、L、O、S、T 和 Z 与其外形相似。

四格骨牌矩阵表示
我们将 J 型四格骨牌表示为一个矩阵,其中数字 2 表示有颜色的方块单元格。为了便于旋转,我们在矩阵中添加一行零:
1[2, 0, 0], 2[2, 2, 2], 3[0, 0, 0];
四格骨牌在生成时是横向放置的,其中 J、L 和 T 型是以平边朝前的方式生成的。
由于我们频繁使用四格骨牌,可以将其封装成一个名为 Piece 的类。
Piece 类
Piece 类需要知道它在游戏板上的 位置、颜色 和 形状。为了能够将自己绘制到画布上,该类还需要持有对 canvas 上下文 的引用。
最初,我们可以直接在 Piece 类的构造函数中硬编码这些值:
1class Piece { 2 constructor(ctx) { 3 this.ctx = ctx; 4 5 this.color = 'blue'; 6 this.shape = [ 7 [2, 0, 0], 8 [2, 2, 2], 9 [0, 0, 0] 10 ]; 11 12 // 初始位置 13 this.x = 3; 14 this.y = 0; 15 } 16}
绘制四格骨牌
要将四格骨牌绘制到游戏板上,我们需要遍历其形状中的每个单元格。如果某个单元格的值大于零,则将其涂色:
1 X → 2Y [2, 0, 0] 3↓ [2, 2, 2] 4 [0, 0, 0]
因此,我们可以根据坐标 [x, y] 进行着色,例如:
1[0, 0] 2[0, 1], [1, 1], [2, 1]
但我们也需要记录当前四格骨牌在游戏板上的实际位置。这正是通过 piece 的坐标来实现的。
[this.x, this.y] 表示形状左上角的位置:
1 X → 2Y [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 3↓ [0, 0, 0, [2, 0, 0], 0, 0, 0, 0] 4 [0, 0, 0, [2, 2, 2], 0, 0, 0, 0] 5 [0, 0, 0, [0, 0, 0], 0, 0, 0, 0] 6 7// this.x = 3, this.y = 1
因此,[this.x + x, this.y + y] 就是该方块在游戏板上的实际位置。这正是我们用来确定哪些位置应被涂成蓝色的方法:
1[3, 1] 2[3, 2], [4, 2], [5, 2]
现在我们已经理清了逻辑思路,代码实现就变得简单了:
1class Piece { 2 draw() { 3 this.ctx.fillStyle = this.color; 4 this.shape.forEach((row, y) => { 5 row.forEach((value, x) => { 6 if (value > 0) { 7 this.ctx.fillRect(this.x + x, this.y + y, 1, 1); 8 } 9 }); 10 }); 11 } 12}
初始化
在初始化游戏板时,我们创建一个四格骨牌并显示在画布上。为了使其可见,我们调用 Piece 类的 draw 方法:
1class Board { 2 constructor(ctx) { 3 this.ctx = ctx; 4 this.grid = this.getEmptyBoard(); 5 this.piece = new Piece(ctx); 6 } 7}
我们需要使用 clearRect() 清除画布上旧的绘制内容。利用 canvas 的宽度和高度,可以在每次绘制之间清理画布。
这里我们可以使用 对象解构 来让代码更简洁:
1function draw() { 2 const { width, height } = ctx.canvas; 3 ctx.clearRect(0, 0, width, height); 4 5 board.piece.draw(); 6}
尝试运行代码,按下播放按钮后,你会看到蓝色的四格骨牌出现在屏幕上。