学习 Canvas:制作贪吃蛇游戏

Avatar of Chris Coyier
Chris Coyier

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 $200 免费积分!

编辑注:以下是 Nick Morgan,又名 Skilldrick 的客座文章。 我对 canvas 不太了解,所以很高兴能够分享一篇关于我目前知识范围之外的当前网络技术的文章。

我认为,在 HTML5 中添加的所有新元素中,canvas 是最令人兴奋的元素之一。 Canvas 为您提供了一个固定尺寸的绘图表面和一些用于在该表面上绘制的基本命令。

在本教程中,我将向您展示如何使用 canvas 制作一个简单的贪吃蛇游戏。 您知道,就像您过去在诺基亚手机上玩的那样。

制作画布和第一个矩形

让我们创建一个 <canvas> 元素,并在上面绘制一些东西。 在此代码片段中,我创建了 canvas 元素,设置了其尺寸,并在其上绘制了一个粉红色的矩形。

查看 Chris Coyier 在 CodePen 上的笔 5474d4c9b7f26b9c792b82d1d34ec57d (@chriscoyier)。

这里首先要注意的是,我们不调用 canvas 上的绘图方法,而是调用其绘图上下文。 当前,canvas 只有一个上下文,我们通过调用 .getContext('2d') 来获取它。

首先,我们设置上下文的填充样式,它采用 CSS 颜色的形式,例如 #f00rgba(100, 100, 100, 0.5)。 然后,我们调用 fillRect,它接受矩形的左上角的坐标,它的宽度和它的高度。 请注意,canvas 中的 x/y 坐标从画布的左上角开始 - x 向右增加,y 向下增加。

制作游戏循环

现在,如果你不能让东西移动,canvas 不会太有趣。 在游戏中,制作移动的标准方法是使用游戏循环。 这是一个以固定间隔调用的函数,用于更新游戏的当前状态并显示更改。

查看 Chris Coyier 在 CodePen 上的笔 4156c10284ec71ff53c0bbeb45437af0 (@chriscoyier)。

(编辑注:这些以前是来自 JSFiddle 的嵌入示例,但我们在周围使用 HTTPS 时它们已损坏。 我将它们移植到 CodePen,但它们仍然是 Nick Morgan 的原创作品。)

我现在开始在代码中添加一些组织。 为了保持全局范围清晰,我只使用一个全局变量,称为 JS_SNAKE。 然后,我将创建一个名为 game 的对象,该对象将保存所有与游戏相关的函数。 game 是一个对象,它有一个公共方法 init,以及一些私有变量和函数。

循环基本上做了三件事。 首先,它更新游戏的当前状态(在本例中,将 x 位置加 2,将 y 位置加 4)。 然后,它更新显示,同时考虑到更新后的状态。 最后,它调用 setTimeout,传递 gameLoop 函数和调用它之前的等待时间。

这将导致每 500 毫秒调用一次函数,该函数每次都会在不同的位置绘制一个矩形。 我们需要每次都调用 clearRect 来清除画布,否则我们每次都会在上一帧上绘制。

绘制一条蛇

现在是乐趣开始的时候:绘制蛇。 蛇是由一组方形块组成的。 每帧我们都会添加一个新的头部并移除尾部,从而使蛇向前移动。

查看 Chris Coyier 在 CodePen 上的笔 651dad042be85e7453aebe140a6dfd91 (@chriscoyier)。

蛇对象有两个公共方法,advancedrawadvance 使蛇向右移动一格,而 draw 实际上将蛇绘制到画布上。

advance 复制蛇的头部,将其向前移动 1 格,然后将新的头部添加到位置数组的前面并移除尾部(分别使用 unshiftpop)。

draw 调用一个内部函数 drawSection,该函数接受一个块位置并绘制一个正方形。 在 draw 的开头,我们保存上下文,并在结束时恢复上下文。 保存上下文会保存其以前的设置,因此我们所做的任何更改都可以恢复。 在这种情况下,我想将 fillStyle 更改为 #33a,但希望 draw 完成后恢复到之前的状态。

移动蛇

现在我们需要连接键盘,以便我们可以控制蛇。 我不会详细介绍代码,但这非常简单。

查看 Chris Coyier 在 CodePen 上的笔 6f5ebe3df1fffd73a0d74e8035b7474e (@chriscoyier)。

game.init() 中调用了一个名为 setEventHandlers 的新函数,该函数设置事件处理程序。 到目前为止,我们只关心 keydown 事件。 如果按下的键是任何箭头键(键码 37、38、39 或 40),那么我们会告诉蛇设置一个新的方向。

setDirection 检查新的方向是否有效。 这样做是为了防止你退回到自己身上,例如在你向右移动时按下向左键。 如果新的方向有效,那么它会将 nextDirection 设置为新的方向。 然后在 advance 中使用它,这样我们就知道新的头部位置应该在哪里。

添加一个苹果

现在蛇需要一些东西来吃,所以让我们给它一个苹果。

查看 Chris Coyier 在 CodePen 上的笔 1cf0319d740962633a5b19014f3ffd4d (@chriscoyier)。

这里没什么特别的,除了我们第一次绘制圆形。 为了在 canvas 中绘制圆形,你需要启动一个新的路径,然后绘制一个圆形,最后填充路径。 为了启动路径,在上下文中调用 beginPath()。 为了绘制圆形,在上下文中调用 arc()arc 接受 6 个参数:中心的 x 坐标,中心的 y 坐标,起始角度,结束角度,以及一个布尔值,指示是否顺时针绘制。 角度以弧度为单位,360° 等于 2 * pi 弧度。

收尾

现在你已经了解了基础知识,你可以查看最终贪吃蛇游戏的源代码,看看发生了什么。
它添加了碰撞检测和记分的技术细节。

查看 Chris Coyier 在 CodePen 上的笔 91b9fbfb55ca36e99029297a6f452e07 (@chriscoyier)。

(对于在家玩的人来说,请记住点击画布区域,以便它可以捕获你的键盘事件。 它在这里在一个 iframe 中呈现,该 iframe 无法接收来自外部页面的事件。)