碰撞检测
在本节课中,你将学习如何检测碰撞,以确保俄罗斯方块的方块始终在游戏边界内。
如果所有方块都能彼此穿过,或者墙壁和地板无法阻挡它们,那么俄罗斯方块将不会是一个有趣的游戏。因此,我们不会随机移动方块,而是先检查可能的碰撞情况,只有在安全的情况下才移动方块。我们需要考虑几种不同的碰撞情况。
当方块出现以下情况时,就会发生碰撞:
- 触碰到地面
- 向左或向右移动时撞到墙壁
- 与棋盘上的已有方块相碰
- 旋转后的新位置碰到墙壁或方块
Canvas 中没有提供检测元素之间碰撞的辅助函数,因此我们需要自己实现这一功能。
边界检测
为了让方块的每个小格子都位于棋盘内部,必须同时满足以下三个条件:
- 小格子的 x 坐标大于左侧墙的 x 坐标
- 小格子的 x 坐标小于右侧墙的 x 坐标
- 小格子的 y 坐标小于地面的 y 坐标
我们可以将这些条件转化为代码,并添加到棋盘类中用于判断是否在边界内的方法中:
1isInsideWalls(x, y) { 2 return ( 3 x >= 0 && // 左侧墙 4 x < COLS && // 右侧墙 5 y < ROWS // 地面 6 ) 7}
现在,我们有了一个方法来检查方块新位置是否合法,再决定是否移动。
碰撞检测
为了检测碰撞,我们需要确保方块的每一个小格子都不会落在禁止的位置上。
适合完成此任务的数组方法是 every(),它会检查数组中的所有元素是否都通过了我们提供的测试条件,返回一个布尔值。
我们在棋盘类中创建一个 valid() 方法,用于检查新位置是否合法,并返回布尔值:
1valid(p) { 2 return p.shape.every((row, dy) => { 3 return row.every((value, dx) => 4 value === 0 || 5 this.isInsideWalls(p.x + dx, p.y + dy) 6 ); 7 }); 8}
dx和dy表示方块在 4x4 网格中的坐标。[p.x, p.y]是该 4x4 网格的左上角位置。- 通过
p.x + dx和p.y + dy,我们得到该方块在棋盘网格中的实际 x 和 y 坐标。
value 是方块网格中对应位置的数值。如果它是 0,说明是空格,无需检查其合法性。
在移动前使用这个方法,可以确保我们不会移动到不允许的位置:
1if (board.valid(p)) { 2 board.piece.move(p); 3}
这样就不再有“幽灵移动”现象了!
硬下落
现在地面已经能阻止方块下落,我们可以添加另一个操作——硬下落。按下空格键会使方块立即下落到不能再下为止:
1const KEY = { 2 SPACE: 32 3} 4 5moves = { 6 [KEY.SPACE]: (p) => ({ ...p, y: p.y + 1 }) 7} 8 9function handleKeyPress(event) { 10 // 阻止事件冒泡 11 event.preventDefault(); 12 13 if (moves[event.keyCode]) { 14 // 获取方块的新状态 15 let p = moves[event.keyCode](board.piece); 16 17 if (event.keyCode === KEY.SPACE) { 18 // 硬下落 19 while (board.valid(p)) { 20 board.piece.move(p); 21 p = moves[KEY.SPACE](board.piece); 22 } 23 } 24 25 if (board.valid(p)) { 26 board.piece.move(p); 27 draw(); 28 } 29 } 30}
让我们尝试一下这个新功能,看看是否还能离开游戏区域。
你会注意到,硬下落并不会更新屏幕,除非你按左右方向键。我们将在下一课中探讨如何解决这个问题。
No configuration found for ID: a6bfd01f40d2431754e8464d185f1c31fcb14971b90b10444f19d6c41a21604e