编辑注:以下是 Nick Morgan,又名 Skilldrick 的客座文章。 我对 canvas 不太了解,所以很高兴能够分享一篇关于我目前知识范围之外的当前网络技术的文章。
我认为,在 HTML5 中添加的所有新元素中,canvas
是最令人兴奋的元素之一。 Canvas 为您提供了一个固定尺寸的绘图表面和一些用于在该表面上绘制的基本命令。
在本教程中,我将向您展示如何使用 canvas 制作一个简单的贪吃蛇游戏。 您知道,就像您过去在诺基亚手机上玩的那样。
制作画布和第一个矩形
让我们创建一个 <canvas>
元素,并在上面绘制一些东西。 在此代码片段中,我创建了 canvas 元素,设置了其尺寸,并在其上绘制了一个粉红色的矩形。
查看 Chris Coyier 在 CodePen 上的笔 5474d4c9b7f26b9c792b82d1d34ec57d (@chriscoyier)。
这里首先要注意的是,我们不调用 canvas 上的绘图方法,而是调用其绘图上下文。 当前,canvas 只有一个上下文,我们通过调用 .getContext('2d')
来获取它。
首先,我们设置上下文的填充样式,它采用 CSS 颜色的形式,例如 #f00
或 rgba(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)。
蛇对象有两个公共方法,advance
和 draw
。 advance
使蛇向右移动一格,而 draw
实际上将蛇绘制到画布上。
advance
复制蛇的头部,将其向前移动 1 格,然后将新的头部添加到位置数组的前面并移除尾部(分别使用 unshift
和 pop
)。
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 无法接收来自外部页面的事件。)
有趣的实现,我喜欢微妙的加速。 不过,它似乎非常慢,是我个人问题吗(Chrome/Mac)?
以下是我几周前根据 ThinkVitamin 的教程制作的一个 http://red-root.com/sandbox/snake/game.html,供那些感兴趣的人参考。 现在正在尝试实现 requestAnimationFrame,但无法正确实现,所有东西都太快了。
真酷! :)
让人上瘾的游戏! 干得好!
简单但有效 - 尽管实现很棒。
是的,它很慢 - 我将初始速度设置为 2 fps(500 毫秒帧长)。不过,正如夏洛特所说,很容易改变初始速度!
真的很酷,我得了 16 分:D
这太有趣了 :)
我觉得画布没有被 UI 设计师充分利用
… 我得了 15 分,然后才意识到我可以编辑 js 并让它更快!
你应该使用 requestAnimationFrame 而不是 setTimeout。请查看 http://paulirish.com/2011/requestanimationframe-for-smart-animating/
是的,requestAnimationFrame 是这类事情的未来方向。我在这里没有使用它,因为每个人都熟悉 setTimeout。这是一个画布教程,我不想引入太多不同的概念。
不错!这是一个很好的小例子,可以帮助我开始一些事情,谢谢。
我得了 81 分
好游戏!
我知道一个 HTML5 蛇的例子,也很棒!
好文章!感谢分享!
jQuery?真的吗?真的有必要使用它吗?
不,使用 jQuery 对此并不重要。但是,我一直在努力实现最简单的代码,因此没有任何东西会分散对主要应用程序逻辑的注意力。我知道 CSS-Tricks 的大多数读者都熟悉 jQuery,所以我选择了它。
感谢这篇文章。我喜欢蛇!
青苹果?!太可怕了!
看看我的分数!
看看我的分数! 我真的很享受玩这个游戏!我建议举办一场比赛:P
谢谢,非常非常有用、有效,解释得非常清楚的教程!
非常感谢您提供这个很棒的教程。
据我了解,没有办法阻止用户更改 javascript(例如,从 score++ 到 score += 100)。
因此,如果我想在一个服务器上的数据库中保留最高分数的记分牌,有没有办法确保,或者至少使用某种协议来测试,提交到服务器的分数是合法的?
再次感谢。
我听说实际上不可能阻止作弊/垃圾邮件。如果你让 js 代码更难阅读(通过压缩)或者……也许它可以通过 php 执行,你的高分系统会更安全。但我不能确定。
很棒的游戏!我得了 44 分!
非常棒,谢谢您
69,我不是在说色情。必须爱上真正简单上瘾的游戏,我几乎还想玩更多。
我从这个教程中学到的最好的东西是 jsFiddle 的 iFrame 嵌入,这是一个展示一些代码示例的好方法。但也是一个很棒的教程!
很棒的教程,Nick - 谢谢!
我得了 21 分,哈哈
嘿,很棒的教程!我也正在制作一个 javascript canvas 游戏,我对在 js 中创建自己的对象还很陌生。您在这个示例中使用的是什么对象模式?我很好奇,因为我正在更改我的游戏代码以使用对象,从表面上看,我很确定您使用的是 Revealing Module Pattern。我说的对吗?
嗨,Jake,是的,它属于某种 Revealing Module Pattern。我认为这是一个很棒的模式 - 在某些方面与基于类的对象设计非常相似,并且除了您明确将其公开外,所有内容都是私有的。我在博客上写了一些关于这方面的内容:http://skilldrick.co.uk/2011/02/zen-and-the-art-of-statefulness/