突破2.0

现在我们到了某个地方。

追赶

本周,我通过在已经创建的Breakout版本的基础上实现一些功能,进一步探索了Phaser框架的功能。 到目前为止,我添加的功能包括:

  • 球与运动员的桨或障碍物碰撞时的音效
  • 生活或在比赛结束之前球员可能错过球的次数
  • 分数随着击中每个方块而增加
  • 多层次
  • 球的速度和角度根据击中桨的位置而变化

除了新功能之外,我还重新组织了代码,以便于使用。 如果您想玩,可以在这里玩游戏,代码在这里。

我如何重组

我花了大部分的编码生涯来用PHP编写面向对象的代码。 因此,我非常习惯于在多个文件中编写和分隔类。 在Javascript中,完全有可能在单独的文件中编写代码,异步加载它们,并且一切正常。 在为游戏创建状态的情况下,仅将它们分开并不会削减它,因为它们依赖于存在于其上的状态对象,然后才能将其添加到该对象中,并且我希望在游戏的任何部分之前将所有状态添加至该对象跑了

这么快就好了,问题是我需要创建一个Phaser.Game对象,然后创建一个states对象,然后再创建所需数量的状态并将其添加到states对象中,然后才能调用game.state .start()使球滚动(在这种情况下会反弹)。 代码是异步加载的,因此我不能保证在创建游戏对象后便会产生状态,那我该怎么办?

答案是:使用RequireJS。 使用这个小的库,通过设置在异步文件加载完成时要调用的回调,我可以实现所需的同步功能。 看一看:

看起来很简单吧? 我将我的每个状态都分离到了自己的文件中,RequireJS发挥了魔力,使我的生活变得轻松。

在合理地组织代码之后,我开始编写新功能。 第一? 音频。

哔哔声和斑点

让我们继续讲一下音效,因为它们可能最容易实现。

不过,在进入代码之前,我想指出一点,我完全忘记了音乐和声音效果不是免费的(大部分时间),我不得不去寻找一些免费的音乐。 不久,我偶然发现了Freesound和我的救星:NoiseCollector。 可以在Freesound上将类似的声音分组,然后NoiseCollector上载了Pong softsynth压缩包,正是我想要的。

好了,现在输入代码。 我们将通过在游戏状态下将它们加载到preload()函数中,来处理要使用的声音文件,几乎与我们使用的声音文件完全相同。

加载所需的音频文件后,我们将文件分配给对象,以便我们可以实际控制其播放时间和方式。

在这种情况下,我在游戏状态下创建了一个soundEffects对象,并将每个对象保存到该对象以供以后参考。 一旦将其放入对象中,您所需要做的就是播放它:

就是这样。 真。 当然,由于使用了Phaser,您可以弄乱音量,从一个时间开始播放文件并在另一个时间结束播放文件,并通过音频执行大量其他操作。 但是对于Breakout来说,一声不响,一团糟几乎就是您所需要的。

没有更多的permadeath!

我添加的下一个功能是游戏结束前玩家可以使用的生命数量有限。 这为需要游戏的人减轻了游戏的难度,并为那些更好的人提供了安全网,以防万一出了问题。

实现几乎太简单了,无法在此处进行介绍,但是我添加了生命作为游戏状态的成员,在我们的create()方法中对其进行了初始化,并在到达屏幕底部时检查了它:

为了使玩家更容易知道他们还剩下多少生命,我在create()期间在屏幕的右上方添加了一个文本计数器,并在此检查中对其进行了更新。

高分

实施得分系统就像跟踪玩家的生活一样容易。 我将比分添加到游戏状态中,并在create()中对其进行了初始化,如果球碰到一个障碍,则在update()中对其进行了更新 。 就像在生活中一样,我会在屏幕上跟踪玩家的得分。

升级

关卡系统的运行几乎完全符合我的预期。 以前,我有一个行循环和一个列循环,它们在create()方法中制作了一堆块。 我输出了每个块的xy坐标,并将每个坐标保存为点数组中的一个点,该数组称为level 。 结构如下所示:levels [level] [point] [x,y]。 然后,我意识到我希望将来能够支持多种块类型,因此我将结构更新为:levels [level] [point] [block type,[x,y]]。 从那里,我编写了一个小函数来读取数组以构建一个级别:

一旦能够从输入数组构建级别,就添加了几个级别并将以下内容添加到了update()方法中:

最初检查玩家是否赢得比赛的游戏( this.blocks.size === 0)已更改为首先检查是否存在另一个关卡。 如果是这样,请更新关卡文字,停止球移动,重置球的位置,建立关卡,然后让玩家再次发射球。 如果没有其他等级,则玩家获胜。

增加品种

我的实现中缺少的原始“突破”中的功能之一是球速度和角度的任何变化。 最初看到的变化通常是由球与桨接触的地方引起的。 对于我的实现,我决定如果球击中球拍左右三分之二,速度和角度都会受到影响,如果击中中间球,它将保持不变。 另外,我使用Math.random()使其变得有些不可预测。

这部分虽然很简单,却让我有些困惑,因为我完全忘记了在玩家的球拍上设置锚点 。 我以为,通过请求this.paddle.x我将获得球拍左侧的x坐标,一旦我认为是左侧,我想将球拍宽度的1/3加到获得我想检查与球之间是否碰撞的区域。 该部分是正确的,但是由于我将桨的锚点设置为居中,因此当我引用this.paddle.x时将返回该内容 我一意识到这一点,就调整了支票,从x位置减去了桨叶宽度的一半,这一切都神奇地起作用了。 故事的寓意:注意您的锚点

复活节彩蛋

我的游戏中还缺少一个拼图。 我只有一种类型的积木。 球击中球并朝相反方向反弹。 易于实现,易于使用。 我想添加一种额外的块类型,并且由于级别实现很容易实现,所以我决定这样做。

我首先在create()中加载图像,然后更新了createBlock()方法以接受其他Sprite的键。 一旦为该级别构建了额外的块,我只需要在update()的球/块碰撞部分中添加一个检查,然后检查block.key即可查看击中了哪种类型的块。

我为那一块砖头添加了一些特殊的功能,因此我特意避免显示此部分的代码,因为我希望您去查找它并看看会发生什么。

喊出来

过去我已经指出了这些问题,但是Mozilla开发人员网络的人们提出了一些有关Web开发的最有用的文档,您可以找到它们。 在解决一些问题时,我偶然发现了他们很棒的游戏开发信息。 事实证明,他们甚至还提供了使用Phaser进行Breakout的教程,因此,如果您对我一直在做的事情感到困惑,请随时进行检查。

接下来是什么

当然,有更多功能可以添加到此Breakout克隆中,但是我想探索Phaser引擎的其他有用功能,以及我想尝试的分发方法(例如Electron)。 最重要的是,我需要开始学习如何创建比单色块和文本更复杂的图形,而且当我找不到免费需要的内容时,我需要学习创建自定义声音效果和音乐。 下一篇文章下周可能不会出现,但是我想我将集中精力从头开始创建更全面的体验。