建造砖块区域
这是Gamedev Canvas 教程的第 6 步(共 10 步)。您可以在 Gamedev-Canvas-workshop/lesson6.html 找到完成本课程后代码的源代码。
在修改了游戏玩法机制后,我们现在能够输掉了——这很棒,因为这意味着游戏终于感觉更像一个游戏了。但是,如果玩来玩去只是让球在墙壁和挡板之间弹来弹去,那很快就会变得很无聊。Breakout 游戏真正需要的是一些可以被球击碎的砖块,这正是我们现在要创建的!
设置砖块变量
本课程的总体目标是渲染几行关于砖块的代码,使用一个嵌套循环来处理一个二维数组。但首先,我们需要设置一些变量来定义砖块的信息,例如它们的宽度和高度、行数和列数等。在您之前声明的程序变量下方添加以下行。
const brickRowCount = 3;
const brickColumnCount = 5;
const brickWidth = 75;
const brickHeight = 20;
const brickPadding = 10;
const brickOffsetTop = 30;
const brickOffsetLeft = 30;
这里我们定义了砖块的行数和列数、它们的宽度和高度、砖块之间的填充(这样它们就不会互相接触)以及顶部和左侧的偏移量(这样它们就不会从 Canvas 的边缘开始绘制)。
我们将所有砖块保存在一个二维数组中。它将包含砖块列(c),而砖块列又将包含砖块行(r),而砖块行又将各自包含一个对象,该对象包含绘制每个砖块在屏幕上的 x 和 y 位置。将以下代码添加到您的变量下方。
const bricks = [];
for (let c = 0; c < brickColumnCount; c++) {
bricks[c] = [];
for (let r = 0; r < brickRowCount; r++) {
bricks[c][r] = { x: 0, y: 0 };
}
}
上面的代码将循环遍历行和列并创建新的砖块。请注意,砖块对象也将用于稍后的碰撞检测。
砖块绘制逻辑
现在让我们创建一个函数来遍历数组中的所有砖块并将它们绘制在屏幕上。我们的代码可能如下所示。
function drawBricks() {
for (let c = 0; c < brickColumnCount; c++) {
for (let r = 0; r < brickRowCount; r++) {
bricks[c][r].x = 0;
bricks[c][r].y = 0;
ctx.beginPath();
ctx.rect(0, 0, brickWidth, brickHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
}
}
同样,我们正在循环遍历行和列来设置每个砖块的 x 和 y 位置,并且在每次循环迭代中,我们还在 Canvas 上绘制一个砖块——大小为 brickWidth x brickHeight。问题是我们把它们都绘制在了一个地方,坐标是 (0,0)。我们需要做的是包含一些计算,这些计算将计算出每次循环迭代中每个砖块的 x 和 y 位置。
const brickX = c * (brickWidth + brickPadding) + brickOffsetLeft;
const brickY = r * (brickHeight + brickPadding) + brickOffsetTop;
每个 brickX 位置的计算方法是 brickWidth + brickPadding,乘以列号 c,再加上 brickOffsetLeft;brickY 的逻辑是相同的,只是它使用了行号 r、brickHeight 和 brickOffsetTop 的值。现在,每一块砖都可以放置在正确的行和列位置,砖块之间有填充,并从画布的左侧和顶部边缘偏移绘制。
完成 drawBricks() 函数后,将 brickX 和 brickY 值作为坐标而不是每次都使用 (0,0),最终版本将如下所示——将此添加到您代码中的 drawPaddle() 函数下方。
function drawBricks() {
for (let c = 0; c < brickColumnCount; c++) {
for (let r = 0; r < brickRowCount; r++) {
const brickX = c * (brickWidth + brickPadding) + brickOffsetLeft;
const brickY = r * (brickHeight + brickPadding) + brickOffsetTop;
bricks[c][r].x = brickX;
bricks[c][r].y = brickY;
ctx.beginPath();
ctx.rect(brickX, brickY, brickWidth, brickHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
}
}
实际绘制砖块
在本课中要做的最后一件事是在 draw() 函数中添加对 drawBricks() 的调用,最好是在开头,放在清除 Canvas 和绘制球之间。将以下代码添加到 drawBall() 调用上方。
drawBricks();
Compare your code
此时,游戏又变得更有趣了一些。
注意:尝试更改每行或每列的砖块数量,或者它们的位
后续步骤
所以现在我们有砖块了!但是球根本没有与它们交互——我们将在继续第七章“碰撞检测”时改变这一点。