paint-brush
重温怀旧之情:使用 HTML5 的 Canvas API 和 JavaScript 重新创建贪吃蛇游戏by@ssaurel
500
500

重温怀旧之情:使用 HTML5 的 Canvas API 和 JavaScript 重新创建贪吃蛇游戏

Sylvain Saurel8m2024/01/21
Read on Terminal Reader

贪吃蛇游戏是 90 年代诺基亚手机上最伟大的经典游戏之一。本教程向您展示如何在 HTML5 中重新创建它。
featured image - 重温怀旧之情:使用 HTML5 的 Canvas API 和 JavaScript 重新创建贪吃蛇游戏
Sylvain Saurel HackerNoon profile picture
0-item
1-item

如果您从未玩过著名的贪吃蛇游戏,请举手!


我这么说,但我想现在的新一代可能从来没有听说过这个游戏,考虑到当今计算机和网络提供的可能性,它可能看起来有点过时。


然而,曾经有一段时间,贪吃蛇游戏是所有手机用户都花大量时间玩的游戏。是的,当时我们谈论的不是智能手机,而是电话。


俗话说,那是一个美好的时光,诺基亚通过其移动设备普及了这款游戏,这也是一个标杆。如今,诺基亚在移动世界和贪吃蛇游戏中的统治地位已不复存在。


对于那些怀念这款游戏的人(我必须承认,我也是其中之一),本教程将教您如何在网络上重新创建它。为此,我将使用 HTML5 的 Canvas API 和 JavaScript。


这也是学习如何使用 Javascript 中的类以及如何为网页游戏创建有效的游戏循环的机会。

为我们的贪吃蛇游戏创建网页

首先,我们将创建一个网页,使我们能够玩我们将在本教程中开发的贪吃蛇游戏。您很快就会发现该网页没有任何困难。两个 div,一个用于页面标题,另一个用于显示蛇将移动的区域。


为此,我将添加一些CSS ,通过对这些 div 应用固定宽度来将它们居中:

Snake类的设计

正如我在本教程的简介中所解释的,我将使用专用的 Snake 类来对游戏进行建模。这也将使您了解如何在 JavaScript 中操作类。


我们的蛇将具有以下属性:

  • bwbh分别表示板的宽度和高度尺寸。
  • nbxnby代表电路板可用的单元格数量。
  • eltwelth表示蛇元素的宽度和高度大小。
  • dirxdiry代表蛇的位移向量。
  • marginxmarginy为蛇元素的宽度和高度添加一个小边距,以便玩家可以看到每个蛇元素之间的分界线。
  • keyupkeydownkeyleftkeyright存储给定时刻移动箭头的状态。
  • startftps存储游戏开始时移动蛇所需的每秒帧数。


在构造函数的最后,调用 init 方法来在游戏开始时初始化蛇。当要开始新游戏时,只需再次调用该init方法即可。


这为我们的 Snake 类提供了以下代码:

在 init 方法中,您可以定义其他蛇属性,例如指向其头部的指针和指向其尾部的指针。 elements 数组将存储给定时刻蛇的所有元素。 Points 属性用于存储当前游戏的点数,而 Level 属性用于定义累积多少点数以提高游戏的 FPS。 fps 越高,蛇移动的速度越快。


由于 fps 属性表示每秒的帧数,因此我们需要一个 fpsinterval 属性,其值为 1 秒(或 1,000 毫秒)除以所需的 fps 数的结果。

贪吃蛇游戏的原理

贪吃蛇游戏的原理很简单:你必须使用四个方向箭头引导蛇,使其吃掉棋盘上出现的最大数量的苹果。每吃一个苹果,蛇就会增长一个元素。随着蛇的成长,你会发现很难避免触摸自己的尾巴。如果你不这样做,你就输了,分数将从零重新开始。当然,每吃一个苹果,你就得一分。


值得指出的是,我们要实现的 Snake 版本是触摸棋盘边缘不会让你输的版本。它只会让你翻到另一边。诺基亚就是以这种方式实现了贪吃蛇游戏的第二个版本。


由于蛇必须吃一个苹果,我们需要以随机的方式在棋盘上显示这个苹果,注意不要直接在蛇的元素上生成苹果。


这为我们的 Snake 类提供了以下generatefood方法:

蛇在屏幕上的渲染

我们的 Snake 类正在进行中,现在我们需要创建一个在屏幕上渲染蛇的方法。我们还需要显示玩家在给定时间的点数,最后显示要吃的苹果。苹果的位置存储在 Snake 类的 food 属性中。


为了在屏幕上渲染蛇,我使用了 HTML5 的 Canvas API。


我将使用此 API 的绘图基元来绘制代表蛇的各种元素以及要吃的苹果的矩形。我将为苹果和蛇应用不同的颜色,以便玩家可以区分它们。


这为我们的 Snake 类的绘制方法提供了以下代码:

移动蛇

现在我们有了一条可以在屏幕上显示的蛇。这一切都很好,但我们需要添加对蛇运动的支持。如上所述,蛇沿着坐标向量(dirx,diry)移动。因此,每次我们调用我将在此处定义的 move 方法时,蛇都会移动相同的量。


在这个 move 方法中,我们检查蛇头的坐标是否与要吃的苹果的坐标相对应。如果是这样,那么蛇刚刚吃掉了苹果。玩家得分,但更重要的是,我们需要采取四项行动:


  1. 将一个元素添加到蛇的元素中。该元素成为新的尾部。
  2. 通过调用generatefood生成一个新的苹果。
  3. 为玩家添加一个点。
  4. 如果玩家通过了一个关卡,那么我们会更新每秒显示的帧数以加快蛇的移动速度。


尽管如此,在 move 方法中,我们需要检查玩家是否犯了用头接触蛇元素的错误。如果这样的话,比赛就输了,我们重新开始!为此,我们调用上面介绍的 Snake 类的 init 方法。


现在,我们需要通过实际移动蛇来完成 move 方法。为此,我们将 dirx 和 diry 添加到蛇的头部坐标中。这给了我们一个新的头来添加。您还会注意到,移动蛇是通过每次移除尾巴并添加新头来智能地完成的。这避免了必须更新所有蛇元素的位置。


最后记得更新新头。顺便说一句,您还会注意到,当蛇的头部穿过棋盘边界时,我们让它移动到棋盘的另一侧。这适用于宽度和长度。


这为我们提供了以下 move 方法的代码:

我们的贪吃蛇游戏的 GameLoop

我们的蛇可以显示在屏幕上。我们的 Snake 可以通过调用它的 move 方法来移动。我们缺少什么?


我们缺少游戏 GameLoop 的实现!


如果没有这个 GameLoop(它会定期调用 move 和 draw 方法),Snake 将无法移动。接下来,我们将向您展示如何在 JavaScript 中以正确的方式实现 GameLoop,让浏览器在认为合适时调用它,以免阻塞游戏网页的渲染线程。


为此,我们将使用标准 JavaScript 对象窗口的 requestAnimationFrame 方法。然后,浏览器将调整其可以支持的最大 fps 以适应将使用网页的计算机或智能手机。


在我们的 gameloop 方法中,我们将浏览器支持的 fps 数量与我们想要移动蛇的 fps 数量去相关。仅当我们处于之前定义的 fps 范围内时,我们才会调用 move 和 draw 方法。


根据上、下、左、右四个方向键的状态来更新蛇的运动矢量的坐标很重要。


最后,我们调用 GameLoop,将选择最佳时机的任务委托给浏览器。这为我们提供了以下 GameLoop 代码:

处理用户与 Snake 的交互

为了使蛇能够根据玩家按下的方向键移动,我们使用 keydown 和 keyup 事件。对于每个事件,我们将调用 Snake 类的一个方法。从逻辑上讲,对于 keydown 事件,这将是按下;对于 keyup 事件,这将是按下。


我们根据玩家使用这些键执行的操作来更新链接的 Snake 类属性的值。正如您所看到的,我们不会通过直接更新蛇的位置来阻止游戏。相反,我们更新 gameloop 方法中的状态,该方法会定期调用。

组装各种 Snake 组件

为了完成这个贪吃蛇游戏,我们需要组装各种元素。我们通过 ID 检索 Canvas 对象。然后,我们获得链接到该 Canvas 的 2D 上下文。应用所需的尺寸。我们创建一个 Snake 对象,将各种期望值作为参数传递,包括板上的单元格数量。


添加 keydown 和 keyup 事件的事件侦听器。


最后,剩下要做的就是调用一次 Snake 的游戏循环来开始游戏。这为我们提供了以下使用 Canvas API 和地狱般的 HTML5/JavaScript Web 对制作的著名贪吃蛇游戏的完整代码:

我们的贪吃蛇游戏在行动

我们的 Snake 完成了,是时候在 Web 浏览器中测试它,看看 Snake 魔法是否再次发挥作用,就像诺基亚粗暴地统治移动世界时那样:



从这个贪吃蛇游戏中,您可以想象出几种可能的增强功能。例如,您可以在蛇每次吃苹果时添加声音。您可以使用 HTML5 的 Web Storage API 来存储玩家的本地高分。这样,当玩家打破最高分时,您可以显示一条祝贺消息。可能性是无限的,在编程中,你唯一的限制就是你的想象力。


在 YouTube 上观看本教程

还可以在 YouTube 上的 SSaurel 频道上观看本教程:


也发布在这里