完成

这是 Gamedev Canvas 教程第 10 步,也是最后一步。您可以在 Gamedev-Canvas-workshop/lesson10.html 找到完成本课程后应有的源代码。

我们编写的任何游戏总有改进的空间。例如,我们可以给玩家提供不止一条命。他们可以犯一两个错误,仍然能够完成游戏。我们还可以改进我们的代码渲染。

给予玩家一些生命

实现生命值相当直接。让我们首先在声明其他变量的地方添加一个变量来存储生命的数量。

js
let lives = 3;

绘制生命计数器看起来与绘制得分计数器几乎相同——在代码中的 drawScore() 函数下方添加以下函数。

js
function drawLives() {
  ctx.font = "16px Arial";
  ctx.fillStyle = "#0095DD";
  ctx.fillText(`Lives: ${lives}`, canvas.width - 65, 20);
}

与其立即结束游戏,不如在生命值用完之前减少生命的数量。当玩家开始下一条生命时,我们还可以重置球和挡板的位置。因此,在 draw() 函数中,用以下三行替换

js
alert("GAME OVER");
document.location.reload();
clearInterval(interval); // Needed for Chrome to end game

通过这一点,我们可以为其添加稍微复杂一些的逻辑,如下所示:

js
lives--;
if (!lives) {
  alert("GAME OVER");
  document.location.reload();
  clearInterval(interval); // Needed for Chrome to end game
} else {
  x = canvas.width / 2;
  y = canvas.height - 30;
  dx = 2;
  dy = -2;
  paddleX = (canvas.width - paddleWidth) / 2;
}

现在,当球击中屏幕底部边缘时,我们会从 lives 变量中减去一条生命。如果没有剩余生命,游戏就失败了;如果还有剩余生命,则重置球和挡板的位置,并随之重置球的移动。

渲染生命显示

现在您需要在 draw() 函数中调用 drawLives(),并将其放在 drawScore() 调用下方。

js
drawLives();

使用 requestAnimationFrame() 改进渲染

现在让我们处理一些与游戏机制无关,但与游戏渲染方式相关的内容。 requestAnimationFrame() 帮助浏览器比我们目前使用 setInterval() 实现的固定帧率更好地渲染游戏。替换以下行

js
interval = setInterval(draw, 10);

with

js
draw();

并删除所有出现的

js
clearInterval(interval); // Needed for Chrome to end game

然后,在 draw() 函数的最底部(就在闭合花括号之前),添加以下行,这会导致 draw() 函数一遍又一遍地调用自身:

js
requestAnimationFrame(draw);

draw() 函数现在在一个 requestAnimationFrame() 循环中一遍又一遍地执行,但我们不再使用固定的 10 毫秒帧率,而是将帧率控制权交还给浏览器。它将相应地同步帧率,并仅在需要时渲染形状。这比旧的 setInterval() 方法产生了更高效、更流畅的动画循环。

Compare your code

就是这样——游戏的最终版本已经准备就绪,可以开始使用了!

注意:尝试更改生命数量和球从挡板反弹的角度。

游戏结束 - 暂时!

您已经完成了所有课程——恭喜!至此,您应该已经掌握了 Canvas 操作的基础知识和 2D 游戏的逻辑。现在是学习一些框架并继续游戏开发的绝佳时机。您可以查看本系列配套的 使用 Phaser 进行 2D 弹球游戏使用 Phaser 构建的 Cyber Orb 教程。您还可以查看 MDN 上的 游戏部分 以获取灵感和更多知识。

您也可以返回 本教程系列的索引页。祝您编码愉快!