什么是怪物要糖果?
当我开始制作游戏,首先确定核心玩法,并尝试迅速建立了一个游戏原型。在这个案例中,我们从一个相当简单的演示 发展出来的游戏名字叫 怪物要糖果 。 首先我会告诉你项目的结构,所以你可以理解整个游戏玩法。我们将根据游戏运行的逻辑顺序来讲解:装载图片资源,创建主菜单,实际的游戏循环。你可以试玩怪物要糖果。
编码由Enclave Games 的Andrzej Mazur 编写,(就是我!) ,美术部分由Blackmoon Design的Robert Podgórski完成。
怪物要糖果的故事很简单:邪恶的国王绑架了你的爱人,你必须收集足够多的糖果才能让她回来。玩法很简单:糖果掉落下来,你可以点击吃掉它们。你吃糖果越多,积分就越高,就会有更好的糖果被解锁。如果你让糖果掉出来屏幕,就会减少生命,然后游戏就结束了。
正如你所看到的,这是一个非常简单但结构完整的游戏。你会发现框架的最重要的用途是,载入图像,绘制精灵以及检测用户活动。这是一个很好的开始,你可以复制代码,研究它们,制作你自己的游戏。
项目设置及结构
你可以阅读框架作者写的“如何开始使用Phaser” ,或者可以从GitHub复制 phaser.min.js 文件到你的项目目录。不需要IDE,点击 index.html就可以在浏览器 文件立即看到源代码所做的更改。
我们的项目目录中包含 index.html 文件(包括HTML5结构和所有必要的JS文件)。还有两个子目录: IMG目录,里面是美术资源,SRC目录,里面是游戏的源代码。
这里是目录结构预览:
在 SRC 目录,你会看到JavaScript文件。在本教程中,我将描述在该文件夹中的所有文件的内容和用途。 你可以看到每个文件的源代码。
index.html
我们从index.html 文件开始。它看起来像一个HTML5网站,但代替文本和HTML元素是Phaser框架渲染的Canvas元素。
[HTML] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
我们在<head>: 标签定义文档:字符集编码,网页标题以及CSS样式。通常我们会引用外部CSS文件,但在这里不需要,正如我前面提到的,一切都将在一个canvas元素中呈现,所以我们不会有任何的HTML元素。
要做的最后一件事是:我们所有的JS文件:从 phaser.min.js 文件到所有包含游戏代码的文件。为了减少浏览器的请求数,使游戏的载入速度更快,我们将在游戏需要的时候单独载入它们。
我们来看<body> 标签,在这里初始化框架并开始我们的游戏。第一行代码调用函数;看起来像这样:
[JavaScript] 纯文本查看 复制代码
1 |
|
此代码将初始化Phaser:
640 是游戏的宽度,960 是游戏的高度。
phaser.auto 通知框架我们希望游戏如何渲染到画布上。这里有三个选项: CANVAS, WEBGL 和 AUTO。第一个将我们的游戏渲染在2D Canvas上;第二个使用WebGL在可能的情况下渲染游戏(现在主要用于桌面游戏,但是移动端支持也会越来越好);第三个通知框架,自动检查是否支持WebGL,从而决定游戏如何呈现,如果不支持WebGL,那么2D Canvas将被使用。
该框架初始化将被分配到单个对象称为 game。
下一行的是关于我们的游戏:
[Actionscript3] 纯文本查看 复制代码
1 |
|
'Boot' 是一个状态名, Candy.Boot 是一个对象(在下面代码中定义),我们开始进入状态时将被执行。我们为Boot 添加状态(配置),Preloader (加载资源),MainMenu (你猜对了;这是游戏主菜单)和Game (游戏主循环)。最后一行, game.state.start('Boot'),启动Boot 状态,Candy.Boot 对象将被执行。
你可以看到,一个主要的JavaScript游戏对象已被创建。在游戏中,我们有 Boot, Preloader, MainMenu,和 Game 对象,我们使用原型定义它们。有一些特殊功能的对象名被框架本身保留(preload() ,create() ,update(),和 render()),但我们也可以定义自己的(startgame() ,spawncandy() ,managepause())。如果你不确定你明白这一切,别担心我会使用示例代码解释这一切。
游戏
现在让我们忘记 Boot, Preloader和 MainMenu 。他们将在以后详细解释;此刻你要知道的是Boot 状态决定游戏的基本配置, Preloader 将加载所有的美术资源,并 MainMenu 将开始游戏菜单。
让我们关注游戏本身,看看Game代码。在解释game.js 代码之前,让我们从开发者的角度来谈谈游戏概念本身。
Portrait 模式
游戏是竖屏模式。
在这种模式下,屏幕的高度大于其宽度。有的游戏适合竖屏(像怪物要糖果),有的游戏适合横屏(包括平台游戏, 比如Craigen),甚至某些类型的游戏在两种模式下都可以运行,通常这样的游戏比较难写。
Game.js
在我们开始game.js 文件之前,先看看它的结构。这里有一个为我们创造的世界,有一个玩家角色在里面,它的工作就是吃糖果。
游戏世界:怪物后面的世界是静态的。背景中有一张糖果大陆的图片,我们可以在前景中看到怪物,还有一个用户界面。
玩家角色: 这个演示非常简单,所以小怪物除了等待糖果什么也不做。对于玩家,主要任务就是收集糖果。
糖果:游戏的核心机制是吃尽可能多的糖果。糖果在屏幕的顶部边缘产生,玩家必须在它们掉落的时候点击(或点击)。如果任何糖果掉出屏幕的底部,删除它,玩家将受到伤害。我们还没有生命系统,因此,游戏结束后立即显示适当的消息。
好吧,现在让我们看看game.js:
[JavaScript] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
Candy.Game有三个函数:
· create() 初始化
· managepause() 暂停和继续游戏
· update() 管理游戏主循环
我们将创建一个方便的对象称为 item 代表单一的糖果。它会有一些有用的方法:
· spawncandy() 增加新糖果
· clickcandy() 当用户点击时,糖果消失
· removecandy() 删除糖果
让我们使用这些代码:
[JavaScript] 纯文本查看 复制代码
1 2 3 4 5 6 7 8 9 |
|
在这里,我们声明所有将使用的变量。
通过定义 this._name,我们限制了Candy.Game变量使用的范围。 这意味着他们不能用在其他状态中。
通过定义Candy._name,我们允许这些变量被对象使用,例如, Candy._score 的数值可以被 Candy.item.clickCandy()函数增加。
对象初始化为null,需要的变量初始化为零。
看看candy.game.prototype的代码:
[JavaScript] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
create() 函数开始之前,我们建立了ARCADE 物理系统----Phaser中有现成的,这是最简单的一种。之后,我们添加了重力。然后我们添加三个图片:背景,怪物,得分UI。第四个增加的元素是暂停按钮,注意我们使用的是 candy.game_width和 candy.game_height 变量,定义在 Candy.Preloader() 。
然后我们创建怪物,玩家的虚拟形象。这是一个动画精灵。看起来像是站着在呼吸。
animations.add() 函数创建帧动画,需要四个参数:
· 动画的名称(可以参考它以后)
· 包含所有帧的table(我们可以只使用其中一些)
· 帧速
· 一个决定动画是否循环的标签
我们使用 animations.play() 播放动画。 我们将 spawncandytimer 设为0 ;的怪物生命 health设为10。
文本格式
接下来的两行代码,可以让我们在屏幕上显示文字。this.add.text()函数有四个参数:屏幕左侧和顶部的绝对位置,实际的文本字符串和配置对象。
我们可以使用CSS那样设置文本格式。 代码如下:
[JavaScript] 纯文本查看 复制代码
1 2 3 4 5 6 7 |
|
字体是Arial,40像素高,黄色,可以设置描边(颜色和厚度),文本中心对齐。
之后,我们定义 candygroup 和第一颗糖果。
暂停游戏
暂停功能看起来像这样:
[JavaScript] 纯文本查看 复制代码
1 2 3 4 5 6 7 8 |
|
每次暂停按钮被点击时,我们改变this.game.paused状态为 true ,显示相应的提示给玩家,并为玩家单击或点击屏幕的行为建立一个事件侦听器。当单击或点击被检测到,我们删除文本并设 this.game.paused 为 false 。
paused 变量在 game 对象中是特殊的,因为它将暂停游戏中所有动画以及计算,所以一切都被冻结,直到我们取消暂停,游戏暂停状态被设置为false。
更新循环
update() 函数Phaser保留的函数之一。当你用这个名字命名一个函数,它将在游戏的每一帧被执行。
[JavaScript] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 |
|
我们用spawncandytimer 变量来跟踪时间。if 语句每秒检查一次,在游戏世界中释放一个新糖果后时间是否被重置,(也就是说,对于spawncandytimer是 1000毫秒)。然后,我们用forEach遍历所有糖果对象内的糖果集团(我们可以有一个以上的屏幕上使用 ),增加固定的数值到糖果的angle变量(存储在糖果对象中的rotateMe),使他们以固定的速度下落。我们做的最后一件事是检查 health 是否 下降到0,如果这样的话,那么我们在屏幕上显示游戏结束并暂停游戏。
糖果事件管理
我们使用item定义糖果,包含的函数有: spawncandy() ,clickcandy() 和 removecandy()。为了方便使用在 Game保留一些糖果的变量。
[JavaScript] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 |
|
函数首先定义三个值:
· 糖果随机下落x坐标(数值在0和游戏画面宽度之间)
· 糖果随机下落y坐标,基于糖果自身的高度
· 随机的糖果类型(糖果一共有五个不同的图像)
然后我们添加一个糖果作为精灵,其起始位置和图像根据上面的定义。我们还需要为糖果旋转设定动画帧。
接下来我们用物理引擎使糖果自然地从屏幕顶部坠落。然后,我们使糖果对点击产生回应,加入事件监听器。
为确保糖果离开游戏屏幕时被销毁,我们将checkWorldBounds 设为true。糖果离开屏幕时,函数events.onOutOfBounds()将被调用;我们用它调用removecandy() 函数。设置锚点在糖果上,使其绕轴线旋转。在这里我们设置了 rotateMe 变量,在update()循环中转动糖果;我们选择 -2和 +2之间的一个值。最后一行代码将新创建的糖果加入到糖果群组,这样我们就可以不停的循环他们 。
我们来讲下一个函数, clickcandy():
[JavaScript] 纯文本查看 复制代码
1 2 3 4 5 |
|
这里需要将一个糖果作为参数,采用Phaser自带的方法 kill() 删除它。我们还增加了得分1,并更新得分的文本。
重置糖果也同样容易和简单:
[JavaScript] 纯文本查看 复制代码
1 2 3 4 |
|
当糖果被点击或掉出屏幕。removecandy() 函数会被调用。candy对象会被删除,玩家失去10点生命。(游戏一开始玩家有10点生命,所以有一个糖果掉出屏幕游戏就结束了)
原型和游戏状态
我们已经了解了游戏机制,核心理念,以及游戏玩法。现在是时候去看代码的其他部分了:加载屏幕,加载资源,管理按钮点击等等。
我们已经知道了游戏状态,让我们一个接一个看看他们:
Boot.js
boot.js 是JavaScript文件,我们将定义主要游戏对象Candy(你可以取个你喜欢的名字)。这里boot.js 文件的源代码:
[JavaScript] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 |
|
正如你所看到的,我们从 var Candy = {} 开始,为游戏创建了一个全局对象。
Candy.Boot = function(game){} 代码创建一个新函数称为 boot() (index.html 有调用)将game 对象作为参数(index.html也被创建 )。
Candy.Boot.prototype = {} 代码是使用原型定义Candy.Boot 内容的一种方式。
[JavaScript] 纯文本查看 复制代码
1 2 3 4 5 6 7 8 |
|
Phaser有一些保留的函数名称,正如我之前提到 ;preload() 和 create() 就是其中的两个。 preload() 用于加载所有资源; create() 只调用一次(在 preload()之后),所以你可以把代码当对象一样安排,就像定义变量或添加精灵。
我们的Boot对象包含这两个函数,这样他们就可以被Candy.Boot.preload()和Candy.Boot.create()调用。正如你所看到 boot.js 文件的完整的源代码,preload() 函数加载了一个图像到框架中:
[JavaScript] 纯文本查看 复制代码
1 2 3 |
|
this.load.image()中第一个参数是我们给图像取的名字,第二个是图像文件的路径。
为什么我们要在 boot.js 文件中加载图像,用preload.js 不行吗?好吧,因为我们需要一个加载条来显示所有资源(preload.js)的加载状态, 因此它需要第一个被加载。
缩放选项
create() 函数包含了一些Phaser特定的输入和缩放设置:
[JavaScript] 纯文本查看 复制代码
1 2 3 4 5 6 7 8 |
|
第一行,input.maxpointers 设为1,我们的游戏不需要多点触摸。
scale.scalemode 控制游戏的旋转。可用的设置有: exact_fit ,no_scale 和 show_all你可以枚举他们,使用数值0,1,或2。exact_fit,将最大化游戏;no_scale将禁用缩放;show_all将确保游戏符合给定的尺寸,一切都会显示在屏幕上(按比例缩放 )。
将 scale.pagealignhorizontally 和 scale.pagealignvertically 设为 true ,将使游戏在水平和垂直方向居中。
调用 scale.setScreenSize(true) “激活”缩放。
最后一行, state.start('Preloader'),执行下一个Preloader 状态
Preloader.js
与preload()函数相比,create() 函数非常简单,因为 create() 函数只需要负责切换状态。
preloader.js 源代码:
[JavaScript] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
与 boot.js 比较像;定义 Preloader 对象和两个原型函数(preload() 和 create())。
Prototype 对象,我们定义了两个变量: candy.game_width 和 candy.game_height;它们设置游戏屏幕默认的宽度和高度。
preload() 的前3行代码设置舞台的背景颜色( # b4d9e7,浅蓝色),显示游戏中的精灵,setPreloadSprite() 函数将负责资源的加载。 我们来看add.sprite() 函数:
[JavaScript] 纯文本查看 复制代码
1 |
|
正如你所看到的,我们需要三个值:图像x轴绝对坐标(舞台宽度减去图像宽度再除以2),图像y轴绝对坐标(类似计算)以及图像的名称(我们已经在 boot.js 文件中加载)。
加载spritesheets
接下来的几行是使用 load.image() (你已经看到了)加载所有的图形资源。
最后3行代码有点不同:
[JavaScript] 纯文本查看 复制代码
1 |
|
load.spritesheet()函数,不是加载单一的图片,而是spritesheet。两个额外的参数告诉函数单个图像的尺寸。
在这里candy.png我们有5种不同类型的糖果。图片尺寸410x98px,但是单一元素的大小是82x98px,在load.spritesheet()函数中定义。玩家spritesheet以同样的方式加载。
create()函数开始游戏的下一个状态MainMenu,当所有资源加载完毕就会显示游戏菜单。
MainMenu.js
在这里渲染图片,添加按钮以及游戏循环。
[JavaScript] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 |
|
MainMenu没有preload()函数,因为资源已在Preload.js加载。
这里有2个函数,create(),startGame()。 先看 startGame() 函数:
[JavaScript] 纯文本查看 复制代码
1 2 3 |
|
这个函数只负责开始游戏循环,但是它不会自动执行,我们需要通过按钮来触发它。
[JavaScript] 纯文本查看 复制代码
1 2 3 4 5 6 7 |
|
create()有3个add.sprite()函数,它们加载图片到舞台上。我们的主菜单在背景上,小怪物在角落里,还有游戏的标题。
按钮
还有一个我们早已在Game 状态使用的对象,就是按钮:
[JavaScript] 纯文本查看 复制代码
1 2 |
|
这个按钮看起来比我们之前的代码都要复杂。我们通过八种不同的参数创建按钮:x轴位置,y轴位置,图像的名称(或精灵),单击该按钮时执行的函数,该函数执行环境,指定按钮使用的图片。
这是按钮的spritesheet,包含状态标签:
与candy.png非常相似,垂直排列。
记住最后三位数字传递给函数的意义—1 0 2-它们分别代表按钮的不同状态:over(鼠标悬停),out(正常),和down(触摸或点击)。在 button.png我们有不同的图片代表他们。
现在你已经了解了Phaser游戏框架的基础。恭喜你!
游戏成品
本文中使用的demo游戏已经演变成一个完整的游戏 , 你可以在这里玩。你看,有生命,成就,得分,和其他有趣的功能,他们中的大多数都基于你已经学到的知识。
你还可以阅读 这一篇记录 ,了解游戏起源以及游戏背后的故事,还有一些有趣的事实。
资源
在过去的几个月里,为移动设备开发HTML5游戏已经非常流行了。该技术将越来越好,几乎每天都有新的工具和服务出现,现在是进入潜在市场的最佳时机。
类似Phaser这样的引擎给了你开发适配各种不同手机设备游戏的能力。多亏了HTML5,你的目标不再仅仅是移动平台和桌面浏览器,还将包括其他不同的操作系统和原生平台。
现在有很多资源可以帮助你进入HTML5游戏开发,例如 HTML5游戏开发新手教程 列表或者 开始用HTML5开发游戏 。如果你需要任何帮助,可以在html5gamedevs 论坛寻求帮助或直接在 freenode IRC的# BBG通道上提问。你还可以关注即将发布的新书Firefox OS and HTML5 games。甚至还有一个Gamedev.js Weekly 通邮件列表你可以订阅。
概要
这是一段关于怪物要糖果demo代码的漫长旅程,我希望能帮助你学习Phaser,在不久的将来你可以开发很酷的游戏。
本文中使用的源代码可以在Github免费获得。你可以编辑代码,开发属于你的游戏,在开发过程中遇到任何问题欢迎访问Phaser论坛寻求帮助。