recursal/MDN huggingface dataset:
2D breakout game using pure JavaScript
======================================

* 下一页 » (en-US)

在这里我们将完全使用 JavaScript 语言基于 HTML5 的 canvas 标签，一步一步的绘制一个简单的 MDN 消除游戏。

过程中的每一步都会有例子可供体验，让你更清晰的了解他的制作过程。你将学习到如何使用 canvas 标签的基本语法去实现简单游戏的渲染、动画、碰撞、控制胜负。

为了更快速高效的学习本系列教程，你需要掌握 JavaScript 的一些基础知识。学习完本教程之后你就可以创建自己的网页小游戏了。

![Gameplay screen from the game MDN Breakout where you can use your paddle to bounce the ball and destroy the brick field, with keeping the score and lives.](/zh-CN/docs/Games/Tutorials/2D_Breakout_game_pure_JavaScript/mdn-breakout-gameplay.png)

教程详情
----

全部教程 — MDN 消除小游戏 的各个版本我们正一起管理并托管到 GitHub 上：

1. 创建、绘制画布 (en-US)
2. 让球动起来 (en-US)
3. 反弹的墙 (en-US)
4. 键盘操作 (en-US)
5. 游戏结束 (en-US)
6. 创建砖块 (en-US)
7. 撞击处理 (en-US)
8. 统计得分、获得胜利 (en-US)
9. 鼠标控制 (en-US)
10. 完成 (en-US)

对于实现一个网页游戏而言，最好的方式是从纯 JavaScirpt 着手，那样可以让我们建立更坚实的基础。之后你可以在你的项目中选择你喜欢的框架。框架也只是用 JavaScript 语言实现的工具；如果你想要在项目中使用框架，你必须先了解语言本身。框架可以帮你提高开发效率并生成一些基础的内容；但是如果没有达到你的预期，你只能慢慢调试或者使用原生 JavaScript 去实现解决方案。

**备注：** 如果你对使用第三方游戏资源库开发 2d 网页游戏感兴趣，可以参考本系列教程的 2D breakout game using Phaser (en-US).

**备注：** 本系列教程可以用作游戏开发工作室的素材资源。如果你想探讨普通的游戏开发，你可以利用Gamedev Canvas Content Kit以及本教程的内容。

下一步
---

好，让我们开始吧。第一步 — 创建、绘制画布 (en-US)

* 下一页 » (en-US)
使用 Phaser 开发 2D breakout game
=============================

* 下一页 » (en-US)

在这个手把手的教程中，我们将使用 Phaser 框架制作一个使用 JavaScript 构建简单的 MDN 消除游戏。

教程的每一步骤都会有可供修改的样品来玩，所以你可以看到开发的每一步中间步骤。你将学到如何使用 Phaser 框架来实现基础游戏机制的基本知识，诸如渲染和移动图像，碰撞检测，控制机制，框架特定的帮助器功能，动画和补间，以及获胜和失败状态等。

为了充分理解这一系列的文章，你应该确保已有基本的中级 JavaScript 知识。学完本教程，你将有能力用 Phaser 构建简单的 Web 游戏。

![Gameplay screen from the game MDN Breakout created with Phaser where you can use your paddle to bounce the ball and destroy the brick field, with keeping the points and lives.](/zh-CN/docs/Games/Tutorials/2D_breakout_game_Phaser/mdn-breakout-phaser.png)

教学清单
----

所有的课程 — 以及我们接下来将一起做的各个版本的 MDN Breakout game 都能在 GitHub上找到

1. 初始化框架 (en-US)
2. 缩放 (en-US)
3. 加载资源并在屏幕上打印 (en-US)
4. 移动小球 (en-US)
5. 物理 (en-US)
6. 从墙上弹开 (en-US)
7. 弹块和控制 (en-US)
8. 游戏结束 (en-US)
9. 建立砖块 (en-US)
10. 碰撞检测 (en-US)
11. 得分 (en-US)
12. 胜利 (en-US)
13. 额外生命 (en-US)
14. 动画与补间 (en-US)
15. 按钮 (en-US)
16. 随机游戏 (en-US)

学习路线的小提示 — 最好先熟悉使用原生 JavaScript 进行网页游戏开发，这样可以打下坚实的基础。如果你还不熟悉原生 javascript 开发，我们建议你先过一遍这个系列，使用原生 Javascript 开发 MDN 消除游戏 (en-US).

在那之后，你就能随意挑选框架并用在你的项目中;我们选择了 Phaser 这个稳定优越的框架，它有着好的支持和社区环境以及大量优秀的插件。框架加速了开发并能帮你管理无趣的部分，让你专注于有意思的事务。然而，框架也有不好的地方，所以当一些意想不到的事情发生了或者想实现一些框架没有提供的功能时，你就将需要原生的 JavaScript 知识了。

**备注：** 本系列文章可用作实际游戏开发的材料。如果你想要使用 Phaser 讨论游戏开发，你还可以使用基于本教程的 Gamedev Phaser 内容套件.

Next steps
----------

好了，那我们就开始吧！前往系列第一部分 — 初始化框架 (en-US).

* 下一页 » (en-US)
Initialize the framework
========================

* « 上一页
* 下一页 »

这是Gamedev Phaser 教程 (en-US)系列的第一课。在课程完成之后，你可以在Gamedev-Phaser-Content-Kit/demos/lesson01.html找到源码。

在我们开始写游戏的功能之前，我们需要创建一个用来内部渲染游戏的基础架构。使用 HTML 就能做到 — Parser 框架将生成所需的 `<canvas>` 元素。

游戏的 HTML
--------

HTML 文档结构非常的简单，这个游戏将整个被渲染在框架生成的`<canvas>` 元素上。拿起你最爱的编辑器，挑一个好目录，创建一个 HTML 文档，存成 index.html，然后写下下面的代码：


```html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Gamedev Phaser Workshop - lesson 01: Initialize the framework</title>
    <style>
 \* {
 padding: 0;
 margin: 0;
 }
 </style>
    <script src="js/phaser.min.js"></script>
  </head>
  <body>
    <script>
 var game = new Phaser.Game(480, 320, Phaser.AUTO, null, {
 preload: preload,
 create: create,
 update: update,
 });
 function preload() {}
 function create() {}
 function update() {}
 </script>
  </body>
</html>
```

下载 Phaser
---------

下面我们将下载 Phaser 的代码，并应用到我们的 HTML 文档中。

1. 进入 Phaser 下载页面.
2. 选择最适合你的下载项 — 我们建议选择 min.js，因为它最小，而且你不太可能想去看它的源码
3. 将 Phaser 的源码存到一个和 index.html 同级的 /js 的目录下
4. 在上面第一个 `<script>` 标签里写下 phaser 的路径。
捋一捋我们干了些啥
---------

这个时候我们在 `<header>` 里定义了 `charset`、`<title>` 和一些基础的 css 来重置默认的 margin 和 padding。我们也用 `<script>` 标签向页面引入了 Phaser 源码。body  里也有一个 `<script>` 标签，我们将在里面写 JavaScript 代码来渲染和控制游戏。

`<canvas>` 元素是由框架自动生成的。我们是通过 `Phaser.Game` 创建一个对象并赋给了 game 变量来完成初始化的。参数的含义是：

* width 和 height 设置了 `<canvas>` 宽高。
* 渲染方式。有三个选项分别是 `AUTO`，`CANVAS` 和 `WEBGL`。我们可以指定使用 Canvas 还是 WebGL 来渲染，如果使用了 Auto 则优先使用 WebGL，如果浏览器不支持则会选择 Canvas。
* `<canvas>` 的 id。如果该参数有值，则使用该值作为 canvas 标签的 id，我们传入 null，则 phaser 会决定 canvas 的 id 值。
* 第四个参数指定了 phaser 的三个生命周期所对应的函数。我们使用相同的名字来让程序更清晰
    + `preload` 进行资源的加载。
    + `create` 会在资源加载完成后执行一次。
    + `update` 会一直循环执行来处理每一帧动画。
完整示例
----

以下是第一章的完整代码，可以直接在 JSFiddle 中运行：

下一步
---

现在我们已经完成了一个简单的 HTML 页面，并且学习了如何安装 Phaser，让我们继续学习第二章： scaling (en-US).

* « 上一页
* 下一页 »
Animations and tweens
=====================

* « 上一页 (en-US)
* 下一页 » (en-US)

这是Gamedev Phaser 教程 (en-US) 16 的**第 14 步**。你可以在Gamedev-Phaser-Content-Kit / demos / lesson14.html完成本课程后找到源代码。

为了使游戏看起来更加多汁和活泼，我们可以使用动画和补间。这将导致更好，更有趣的体验。让我们来探讨如何在游戏中实现 Phaser 动画和补间。

动画
--

在 Phaser，动画中，涉及从外部来源获取 spritesheet 并依次显示 sprites。作为一个例子，当碰到一些东西时，我们会让球摇摆。

首先，从 Github 抓取 spritesheet并将其保存在你的`/img`目录中。

接下来，我们将加载 spritesheet - 将以下行放在`preload()`函数的底部：


```js
game.load.spritesheet("ball", "img/wobble.png", 20, 20);
```


而不是加载单个图像的球，我们可以加载整个 spritesheet - 不同图像的集合。我们将按顺序显示精灵，创造动画的幻觉。该`spritesheet()`方法的两个额外的表格确定给定 spritesheet 文件中每个单个框架的宽度和高度，指示程序如何切割以获取单个框架。

加载动画
----

接下来，进入你的 create() 函数，找到加载球精灵的行，下面的调用`animations.add()`如下所示：


```js
ball = game.add.sprite(50, 250, "ball");
ball.animations.add("wobble", [0, 1, 0, 2, 0, 1, 0, 2, 0], 24);
```


要向对象添加动画，我们使用该`animations.add()`方法，其中包含以下参数

* 我们为动画选择的名称
* 一个数组，定义在动画过程中显示帧的顺序。如果你再次查看`wobble.png`图像，你会看到有三个框架。Phaser 提取它们并将它们存储在数组中 - 位置 0，1 和 2.上面的数组表示我们显示帧 0，然后是 1，然后是 0 等。
* 帧速率，以 fps 为单位。由于我们以 24fps 运行动画，有 9 帧，动画每秒将显示三次以下。
当球击中桨时应用动画
----------

在`arcade.collide()`处理球和桨（第一行内部`update()`，见下文）之间的碰撞的方法调用中，我们可以添加一个额外的参数，该参数指定每次发生碰撞时执行的功能，与该`ballHitBrick()`功能相同。更新内部的第一行`update()`，如下所示：


```js
function update() {
  game.physics.arcade.collide(ball, paddle, ballHitPaddle);
  game.physics.arcade.collide(ball, bricks, ballHitBrick);
  paddle.x = game.input.x || game.world.width \* 0.5;
}
```


然后我们可以创建`ballHitPaddle()`函数（具有`ball`和`paddle`作为默认参数），在调用时播放摆动动画。在结束`</script>`标签之前添加以下方法：


```js
function ballHitPaddle(ball, paddle) {
  ball.animations.play("wobble");
}
```


每次球击中桨时都会播放动画。你也可以`animations.play()`在`ballHitBrick()`函数内添加调用，如果你觉得它会使游戏看起来更好。

补间
--

而动画依次播放外部精灵，补间游戏中物体的属性平滑，如宽度或不透明度。

让我们在游戏中增加一个补间，使砖块在被球击中时顺利消失。转到你的`ballhitBrick()`功能，找到你的`brick.kill();`行，并将其替换为以下内容：


```js
var killTween = game.add.tween(brick.scale);
killTween.to({ x: 0, y: 0 }, 200, Phaser.Easing.Linear.None);
killTween.onComplete.addOnce(function () {
  brick.kill();
}, this);
killTween.start();
```


让我们来看看这里，看看这里发生了什么：

1. 当定义一个新的补间时，你必须指定哪些属性将被补间 - 在我们的例子中，而不是在被球击中时立即隐藏砖块，我们将把它们的宽度和高度缩放到零，所以它们将很好地消失。最后，我们使用该`add.tween()`方法，指定`brick.scale`为参数，因为这是我们想要补间。
2. 该`to()`方法定义补间结束时对象的状态。它需要一个包含所选参数的期望结束值的对象（比例取尺度值，1 为大小的 100％，0 为大小的 0％等），补间的时间（以毫秒为单位）以及使用的宽松类型补间。
3. 我们还将添加可选的`onComplete`事件处理程序，该处理程序定义了在补间程序完成时要执行的函数。
4. 最后一件事是立即开始补间`start()`。

这是补间定义的扩展版本，但是我们也可以使用速记语法：


```js
game.add
  .tween(brick.scale)
  .to({ x: 2, y: 2 }, 500, Phaser.Easing.Elastic.Out, true, 100);
```


这个补间将使用弹性宽松在半秒内将砖的比例翻倍，将自动启动，延迟 100 毫秒。

比较你的代码
------

你可以在下面的现场演示中查看本课程的完成代码，并使用它来更好地了解它的工作原理：

下一步
---

动画和 tweens 看起来很不错，但我们可以添加更多的我们的游戏 - 在下一节我们将看看处理按钮 (en-US)输入。

* « 上一页 (en-US)
* 下一页 » (en-US)
Load the assets and print them on screen
========================================

* « 上一页 (en-US)
* 下一页 » (en-US)

这是Gamedev Phaser 教程 (en-US) 16 的**第三步**。你可以在Gamedev-Phaser-Content-Kit / demos / lesson03.html完成本课程后找到源代码

我们的游戏将围绕屏幕滚动，弹出一个桨，摧毁砖块赚取积分 - 熟悉吗？在本文中，我们将介绍如何将 sprite 添加到我们的 gameworld 中。

有一个球
----

我们开始创建一个 JavaScript 变量来表示我们的球 - 在游戏初始化代码（我们的`var game...`块）和`preload()`函数之间添加以下行：


```js
var ball;
```


**备注：** 为了本教程，我们将使用全局变量。**本教程的目的是教导 Phaser 特定的游戏开发方法，而不是主观的最佳方法。**

加载球精灵
-----

使用 Phaser 加载图像并将其打印在我们的画布上比使用纯 JavaScript 容易得多。要加载资产，我们将使用`game`由 Phaser 创建的对象，执行其`load.image()`方法。在`preload()`函数的底部添加以下新行：


```js
function preload() {
  // ...
  game.load.image("ball", "img/ball.png");
}
```


第一个参数是我们要提供资产的名称 - 这将在我们的游戏代码中使用，例如我们的`ball`变量名称，所以我们需要确保它是一样的。第二个参数是图形资源的相对路径。在我们的情况下，我们将加载我们的球的图像（请注意，文件名不一定是一致的，但我们建议，因为它使一切更容易遵循。）

当然，要加载图像，它需要在我们的代码目录中可用。从 Github 抓住球图像，并将其保存`/img`在与`index.html`文件相同位置的目录中。

现在，要在屏幕上显示，我们将使用另一种 Phaser 方法`add.sprite()`：在`create()`函数内添加以下新的代码行，如图所示：


```js
function create() {
  ball = game.add.sprite(50, 50, "ball");
}
```


这将添加球到游戏，并将其呈现在屏幕上。前两个参数是要添加的画布的 x 和 y 坐标，第三个是我们之前定义的资产的名称。就是这样 - 如果你加载你的`index.html`文件，你会看到已经加载并在画布上渲染的图像！

比较你的代码
------

你可以在下面的现场演示中查看本课程的完成代码，并使用它来更好地了解它的工作原理：

下一步
---

打出球很容易; 接下来我们将尝试在屏幕上移动球 (en-US)。

* « 上一页 (en-US)
* 下一页 » (en-US)
Extra lives
===========

* « 上一页 (en-US)
* 下一页 » (en-US)

这是Gamedev Phaser 教程 (en-US) 16 的**第 13 步**。你可以在Gamedev-Phaser-Content-Kit / demos / lesson13.html完成本课程后找到源代码。

我们可以通过增加生活使游戏更愉快。在这篇文章中，我们将实施一个生活系统，以便玩家可以继续玩，直到他们失去了三个生命，而不仅仅是一个人。

新变量
---

在代码中的现有添加下面添加以下新变量：


```js
var lives = 3;
var livesText;
var lifeLostText;
```


这些分别将存储生命数，显示剩余生命数的文本标签，以及当玩家失去生命之后将在屏幕上显示的文本标签。

定义新的文本标签
--------

定义文本看起来像我们已经在分数 (en-US)课上已经做的 (en-US)。`scoreText`在`create()`函数内的现有定义下方添加以下行：


```js
livesText = game.add.text(game.world.width - 5, 5, "Lives: " + lives, {
  font: "18px Arial",
  fill: "#0095DD",
});
livesText.anchor.set(1, 0);
lifeLostText = game.add.text(
  game.world.width \* 0.5,
  game.world.height \* 0.5,
  "Life lost, click to continue",
  { font: "18px Arial", fill: "#0095DD" },
);
lifeLostText.anchor.set(0.5);
lifeLostText.visible = false;
```


在`livesText`与`lifeLostText`物体看起来非常相似的`scoreText`一个 - 它们定义在屏幕上的位置，显示实际文本和字体样式。前者被锚定在其右上边缘上，与屏幕正确对齐，后者位于中心位置，两者均使用`anchor.set()`。

该`lifeLostText`会表示，只有当生命消失，因此其知名度初始设置为`false`。

### 使我们的文字造型干燥

正如你可能已经注意到，我们使用相同的造型为三种文本：`scoreText`，`livesText`和`lifeLostText`。如果我们想要更改字体大小或颜色，我们必须在多个地方进行。为了使我们更容易维护，将来我们可以创建一个单独的变量来保存我们的样式，让我们`textStyle`将其调用并放在文本定义之前：


```js
textStyle = { font: "18px Arial", fill: "#0095DD" };
```


现在我们可以在使用文本标签的时候使用这个变量 - 更新你的代码，使文本样式的多个实例被替换为变量：


```js
scoreText = game.add.text(5, 5, "Points: 0", textStyle);
livesText = game.add.text(
  game.world.width - 5,
  5,
  "Lives: " + lives,
  textStyle,
);
livesText.anchor.set(1, 0);
lifeLostText = game.add.text(
  game.world.width \* 0.5,
  game.world.height \* 0.5,
  "Life lost, click to continue",
  textStyle,
);
lifeLostText.anchor.set(0.5);
lifeLostText.visible = false;
```


这样一来，改变一个变量中的字体将会将更改应用于每个使用的地方。

生活处理代码
------

为了在我们的游戏中实现生活，让我们先改变球对`onOutOfBounds`事件的影响。而不是执行匿名函数并立即显示警报：


```js
ball.events.onOutOfBounds.add(function () {
  alert("Game over!");
  location.reload();
}, this);
```


我们将分配一个所谓的新功能`ballLeaveScreen`; 删除以前的事件处理程序（如上所示），并将其替换为以下行：


```js
ball.events.onOutOfBounds.add(ballLeaveScreen, this);
```


我们想减少每次球离开帆布的人数。`ballLeaveScreen()`在代码末尾添加函数定义：


```js
function ballLeaveScreen() {
  lives--;
  if (lives) {
    livesText.setText("Lives: " + lives);
    lifeLostText.visible = true;
    ball.reset(game.world.width \* 0.5, game.world.height - 25);
    paddle.reset(game.world.width \* 0.5, game.world.height - 5);
    game.input.onDown.addOnce(function () {
      lifeLostText.visible = false;
      ball.body.velocity.set(150, -150);
    }, this);
  } else {
    alert("You lost, game over!");
    location.reload();
  }
}
```


而不是立即打印警报，当你失去了一生，我们首先从当前的数字减去一个生命，并检查它是否是一个非零值。如果是，那么玩家还是有一些生命剩下，可以继续玩 - 他们会看到生命中的消息，球和桨的位置将被重置在屏幕上和下一个输入（点击或触摸），消息将被隐藏球将再次开始移动。

当可用生活数量达到零时，游戏结束，并显示游戏过期警报消息。

事件
--

你可能已经注意到了`add()`，并`addOnce()`在上面的两个代码块的方法调用，并想知道它们的区别。不同之处在于，该`add()`方法绑定给定的函数，并使其在每次事件发生时执行，同时`addOnce()`当你希望绑定函数只执行一次，然后解除绑定时有用，因此不会再次执行。在我们的例子中，每个`outOfBounds`事件`ballLeaveScreen`都将被执行，但当球离开屏幕时，我们只想从屏幕上删除一次消息。

比较你的代码
------

你可以在下面的现场演示中查看本课程的完成代码，并使用它来更好地了解它的工作原理：

下一步
---

生活让游戏更加宽容 - 如果你失去一个生命，你还剩下两个，可以继续玩。现在让我们通过添加动画和补间来 (en-US)扩展游戏的外观和感觉。

* « 上一页 (en-US)
* 下一页 » (en-US)
Win the game
============

* « 上一页 (en-US)
* 下一页 » (en-US)

这是Gamedev Phaser 教程 (en-US) 16 的**第 12 步**。你可以在Gamedev-Phaser-Content-Kit / demos / lesson12.html完成本课程后找到源代码。

在我们的游戏中实现获胜是相当容易的：如果你碰巧摧毁所有的砖块，那么你赢了。

如何取胜？
-----

将以下新代码添加到你的`ballHitBrick()`函数中：


```js
function ballHitBrick(ball, brick) {
  brick.kill();
  score += 10;
  scoreText.setText("Points: " + score);

  var count_alive = 0;
  for (i = 0; i < bricks.children.length; i++) {
    if (bricks.children[i].alive == true) {
      count_alive++;
    }
  }
  if (count_alive == 0) {
    alert("You won the game, congratulations!");
    location.reload();
  }
}
```


我们循环使用组中的砖块`bricks.children`，检查每个砖块的`.alive()` 方法的活力。如果没有更多的砖块活着，那么我们会显示一个获胜的消息，一旦警报被关闭，重新启动游戏。

比较你的代码
------

你可以在下面的现场演示中查看本课程的完成代码，并使用它来更好地了解它的工作原理：

下一步
---

失败和获胜都是实施的，所以我们的游戏的核心游戏就完成了。现在让我们添加一些额外的东西 - 我们会给玩家将 3 个生活 (en-US)的，而不是一个。

* « 上一页 (en-US)
* 下一页 » (en-US)
Randomizing gameplay
====================

* « 上一页 (en-US)

这是Gamedev Phaser 教程 (en-US) 16 中的第**16 步**。你可以在Gamedev-Phaser-Content-Kit / demos / lesson16.html完成本课程后找到源代码。

我们的游戏似乎已经完成了，但是如果你看起来足够近，你会发现球在整个游戏中都以相同的角度从桨上弹起。这意味着每个游戏都非常相似。为了解决这个问题，提高可玩性，我们应该使反弹角度更加随机，在本文中我们将介绍一下如何。

让篮板更随机
------

我们可以根据撞击桨的确切位置来改变球的速度，通过使用沿着下方的线路运行功能来修改`x`速度`ballHitPaddle()`。现在添加这一行到你的代码，并尝试。


```js
function ballHitPaddle(ball, paddle) {
  ball.animations.play("wobble");
  ball.body.velocity.x = -1 \* 5 \* (paddle.x - ball.x);
}
```


这有点魔法 - 新的速度越高，桨的中心和球撞到的地方之间的距离就越大。此外，方向（左或右）由该值确定 - 如果球击中桨的左侧，则其将向左反弹，而击球右侧将向右反弹。最终这样做是因为对某些值进行了一些实验，你可以进行自己的实验，看看会发生什么。这当然不是完全随机的，但它确实使游戏玩法变得更加不可预测，因此更有趣。

比较你的代码
------

你可以在下面的现场演示中查看本课程的完成代码，并使用它来更好地了解它的工作原理：

概要
--

你已经完成了所有的课程 - 恭喜你！在这一点上，你将了解到 Phaser 的基础知识和简单 2D 游戏背后的逻辑。

### 练习跟随

你可以在游戏中做更多的事情 - 添加任何你觉得最好的东西，使它更有趣和有趣。Phaser 提供的无数有用的方法的基本介绍。以下是关于如何扩展我们的小游戏的一些建议，让你开始：

* 添加第二个球或桨。
* 改变每次命中背景的颜色。
* 更改图像并使用自己的图像。
* 如果砖块被迅速摧毁，并排排列（或你选择的其他奖金），则可获得额外的奖励积分。
* 创建不同砖块布局的水平。

一定要检查越来越多的示例列表和官方文档，如果你需要任何帮助，请访问HTML5 Gamedevs 论坛。

你也可以返回本教程系列的索引页 (en-US)。

* « 上一页 (en-US)
Bounce off the walls
====================

* « 上一页 (en-US)
* 下一页 » (en-US)

这是Gamedev Phaser 教程 (en-US)的**第 6 步**。在Gamedev-Phaser-Content-Kit / demos / lesson06.html完成本课后，你可以找到源代码。

现在已经介绍了物理引擎，我们可以开始在游戏中实现碰撞检测 - 首先我们来看看墙壁。

反弹边界
----

让我们的球从墙壁上弹起的最简单的方法是告诉框架，我们想要将 `<canvas>` 元素的边界视为墙壁，而不是让球移过它们。在 Phaser 中，可以使用该 `collideWorldsBound` 属性轻松实现。在现有 `game.physics.enable()` 方法调用之后添加此行：


```js
ball.body.collideWorldBounds = true;
```


现在球将停在屏幕的边缘，而不是消失，但它不会弹起。为了使这种情况发生，我们必须设置它的 bounciness。在上一行下面添加以下行：


```js
ball.body.bounce.set(1);
```


再次尝试重新加载 index.html - 现在你应该看到球从墙壁上弹起并在画布区域内移动。

比较你的代码
------

你可以在下面的现场演示中查看本课程的完成代码，并使用它来更好地了解它的工作原理：

下一步
---

现在开始看起来更像是一个游戏，但是我们无法以任何方式控制它 - 现在是介绍玩家挡板和控制的时候了 (en-US)。

* « 上一页 (en-US)
* 下一页 » (en-US)
The score
=========

* « 上一页 (en-US)
* 下一页 » (en-US)

这是Gamedev Phaser 教程 (en-US) 16 的**第 11 步**。你可以在Gamedev-Phaser-Content-Kit / demos / lesson11.html完成本课程后找到源代码。

得分也可以使游戏更有趣 - 你可以尝试击败自己的高分，或者你的朋友。在这篇文章中，我们将为我们的游戏添加一个评分系统。

我们将使用一个单独的变量来存储分数和 Phaser 的`text()`方法将其打印到屏幕上。

新变量
---

在以前定义的之后添加两个新变量：


```js
// ...
var scoreText;
var score = 0;
```

将得分文字添加到游戏显示
------------

现在在`create()`函数末尾添加这一行：


```js
scoreText = game.add.text(5, 5, "Points: 0", {
  font: "18px Arial",
  fill: "#0095DD",
});
```


该`text()`方法可以采用四个参数：

* x 和 y 坐标来绘制文本。
* 将呈现的实际文本。
* 用于呈现文本的字体样式。

最后一个参数与 CSS 样式非常相似。在我们的例子中，乐谱文字将为蓝色，大小为 18 像素，并使用 Arial 字体。

当砖块被破坏时更新分数
-----------

每当球击中砖块时，我们将增加点数，更新`scoreText`显示当前得分。这可以使用`setText()`方法 - 添加以下两行新`ballHitBrick()`功能：


```js
function ballHitBrick(ball, brick) {
  brick.kill();
  score += 10;
  scoreText.setText("Points: " + score);
}
```


这是现在 - 重新加载你的，`index.html`并检查得分更新每个砖击。

比较你的代码
------

你可以在下面的现场演示中查看本课程的完成代码，并使用它来更好地了解它的工作原理：

下一步
---

我们现在有一个得分系统，但是如果你不能赢得，那么玩和保持分数是多少？让我们看看我们如何能够增加胜利的状态，让我们赢得比赛 (en-US)。

* « 上一页 (en-US)
* 下一页 » (en-US)
Physics
=======

* « 上一页 (en-US)
* 下一页 » (en-US)

这是Gamedev Phaser 教程 (en-US) 16 的**第 5 步**。你可以在Gamedev-Phaser-Content-Kit / demos / lesson05.html完成本课程后找到源代码

为了在我们的游戏中的对象之间进行正确的碰撞检测，我们将需要物理学; 本文将向你介绍 Phaser 中的可用内容，以及演示典型的简单设置。

添加物理效果
------

Phaser 与三个不同的物理引擎（Arcade Physics，P2 和 Ninja Physics）捆绑在一起，第四个选项 Box2D 可作为商业插件使用。对于像我们这样的简单游戏，我们可以使用 Arcade Physics 引擎。我们不需要任何重的几何计算 - 毕竟只是一个球从墙壁和砖块弹起来。

首先，让我们在游戏中初始化 Arcade Physics 引擎。`physics.startSystem()`在`create`函数开头添加方法（使其成为函数内的第一行），如下所示：


```js
game.physics.startSystem(Phaser.Physics.ARCADE);
```


接下来，我们需要为物理系统启用我们的球 - 默认情况下，Phaser 对象物理不启用。在`create()`函数底部添加以下行：


```js
game.physics.enable(ball, Phaser.Physics.ARCADE);
```


接下来，如果我们要在屏幕上移动我们的球，我们可以设置`velocity`它`body`。再次添加以下行`create()`：


```js
ball.body.velocity.set(150, 150);
```

删除我们以前的更新说明
-----------

记得删除添加值的我们的老方法`x`，并`y`从`update()`功能：


```js
function update() {
  ball.x += 1;
  ball.y += 1;
}
```


我们正在使用物理引擎正确处理。

最终代码检查
------

最新的代码应该如下所示：


```js
var ball;

function preload() {
  game.scale.scaleMode = Phaser.ScaleManager.SHOW\_ALL;
  game.scale.pageAlignHorizontally = true;
  game.scale.pageAlignVertically = true;
  game.stage.backgroundColor = "#eee";
  game.load.image("ball", "img/ball.png");
}

function create() {
  game.physics.startSystem(Phaser.Physics.ARCADE);
  ball = game.add.sprite(50, 50, "ball");
  game.physics.enable(ball, Phaser.Physics.ARCADE);
  ball.body.velocity.set(150, 150);
}

function update() {}
```


尝试重新加载`index.html`- 球应该在给定的方向上不断移动。目前，物理引擎的重力和摩擦力设定为零。增加重力将导致球落下，同时摩擦力最终会停止球。

物理效果趣味
------

你可以用物理学来做更多的事情，例如添加`ball.body.gravity.y = 100;`你将设置球的垂直重力。因此，它将向上发射，但是由于重力的作用而下降。

这种功能只是冰山一角 - 有各种功能和变量可以帮助你操纵物理对象。查看官方物理文档，并使用Arcade和P2物理系统查看大量示例。

比较你的代码
------

你可以在下面的现场演示中查看本课程的完成代码，并使用它来更好地了解它的工作原理：

下一步
---

现在我们可以转到下一课，看看如何让球从墙上弹起 (en-US)。

* « 上一页 (en-US)
* 下一页 » (en-US)
Collision detection
===================

* « 上一页 (en-US)
* 下一页 » (en-US)

这是Gamedev Phaser 教程 (en-US) 16 的**第 10 步**。你可以在Gamedev-Phaser-Content-Kit / demos / lesson10.html完成本课程后找到源代码。

现在接下来的挑战 - 球和砖块之间的碰撞检测。幸运的是，我们可以使用物理引擎来检查单个对象（如球和桨）之间的碰撞，也可以检测对象和组之间的碰撞。

砖/球碰撞检测
-------

物理引擎使一切都变得更容易 - 我们只需要添加两个简单的代码。首先，在你的`update()`函数中添加一行，检查球和砖之间的碰撞检测，如下所示：


```js
function update() {
  game.physics.arcade.collide(ball, paddle);
  game.physics.arcade.collide(ball, bricks, ballHitBrick);
  paddle.x = game.input.x || game.world.width \* 0.5;
}
```


球的位置是根据组中所有砖的位置计算的。第三个可选参数是发生冲突时执行的功能`ballHitBrick()`。创建这个新功能作为代码的底部，就在结束`</script>`标签之前，如下所示：


```js
function ballHitBrick(ball, brick) {
  brick.kill();
}
```


就是这样！重新加载你的代码，你应该看到新的碰撞检测工作正常。

感谢 Phaser，有两个参数传递给函数 - 第一个是球，我们在碰撞方法中明确定义，第二个是球碰撞的砖组中的单个砖。在功能内部，我们从屏幕上删除所讨论的砖块，只需运行其`kill()`上的方法即可。

你将期望在使用纯 JavaScript (en-US)时编写更多自己的计算机来实现碰撞检测。这是使用框架的好处 - 你可以为 Phaser 留下大量无聊的代码，并专注于制作游戏中最有趣和最有趣的部分。

比较你的代码
------

你可以在下面的现场演示中查看本课程的完成代码，并使用它来更好地了解它的工作原理：

下一步
---

我们可以打砖块并删除它们，这已经是游戏的一个很好的补充。结果，更好地计算被毁砖增加得分 (en-US)。

* « 上一页 (en-US)
* 下一页 » (en-US)
Buttons
=======

* « 上一页 (en-US)
* 下一页 » (en-US)

这是Gamedev Phaser 教程 (en-US) 16 的**第 15 步**。你可以在Gamedev-Phaser-Content-Kit / demos / lesson15.html完成本课程后找到源代码

而不是立即开始游戏，我们可以通过添加他们可以按的开始按钮将该决定留给玩家。我们来调查如何做到这一点。

新变量
---

我们需要一个变量来存储表示游戏当前是否正在播放的布尔值，另一个代表我们的按钮。将以下行添加到其他变量定义之下：


```js
var playing = false;
var startButton;
```

加载按钮 spritesheet
----------------

我们可以加载按钮 spritesheet 与我们加载球的摆动动画相同的方式。将以下内容添加到`preload()`函数底部：


```js
game.load.spritesheet("button", "img/button.png", 120, 40);
```


单个按钮框架宽 120 像素，高 40 像素。

你还需要从 Github 抓取按钮 spritesheet，并将其保存在你的`/img`目录中。

将按钮添加到游戏中
---------

使用该`add.button`方法可以将新的按钮添加到游戏中。将以下行添加到`create()`函数的底部：


```js
startButton = game.add.button(
  game.world.width \* 0.5,
  game.world.height \* 0.5,
  "button",
  startGame,
  this,
  1,
  0,
  2,
);
startButton.anchor.set(0.5);
```


该`button()`方法的参数如下：

* 按钮的 x 和 y 坐标
* 要显示按钮的图形资产的名称
* 按下按钮时将执行的回调函数
* `this`指定执行上下文的引用
* 将用于*过度*，*超出*和*向下*事件的框架。

**备注：** 超越事件与悬停相同，当指针从按钮中移出时，当按下按钮时，向下移动。

现在我们需要定义`startGame()`上面代码中引用的函数：


```js
function startGame() {
  startButton.destroy();
  ball.body.velocity.set(150, -150);
  playing = true;
}
```


当按下按钮时，我们删除按钮，设置球的初始速度并将`playing`变量设置为`true`。

最后对于这一部分，回到你的`create()`函数，找到`ball.body.velocity.set(150, -150);`一行，并删除它。你只需要按下按钮时移动球，而不是之前！

在游戏开始之前仍然保持桨
------------

它按预期工作，但是当游戏尚未开始时，我们仍然可以移动桨，这看起来有点愚蠢。为了阻止这一点，我们可以利用`playing`变量，使得桨只有在游戏开始时才能移动。要做到这一点，调整`update()`功能如下所示：


```js
function update() {
  game.physics.arcade.collide(ball, paddle, ballHitPaddle);
  game.physics.arcade.collide(ball, bricks, ballHitBrick);
  if (playing) {
    paddle.x = game.input.x || game.world.width \* 0.5;
  }
}
```


这样一来，在所有的装载和准备之后，但在实际游戏开始之前，桨是不可移动的。

比较你的代码
------

你可以在下面的现场演示中查看本课程的完成代码，并使用它来更好地了解它的工作原理：

下一步
---

在本系列文章中我们将做的最后一件事情是，通过添加一些随机化 (en-US)的方式，球从球上弹起来，使游戏更有趣。

* « 上一页 (en-US)
* 下一页 » (en-US)
Game over
=========

* « 上一页 (en-US)
* 下一页 » (en-US)

这是Gamedev Phaser 教程 (en-US) 16 的**第 8 步**。在Gamedev-Phaser-Content-Kit / demos / lesson08.html完成本课后，你可以找到源代码。

为了使游戏更有趣，我们可以引入失去的能力 - 如果在到达屏幕底部边缘之前没有击球，那么这个游戏将会结束。

如何输
---

为了提供丢失的能力，我们将禁用球与屏幕底部的碰撞。在`create()`函数内添加下面的代码; 刚刚定义球的属性就好了：


```js
game.physics.arcade.checkCollision.down = false;
```


这将使三个墙壁（顶部，左侧和右侧）弹回球，但是第四个（底部）将消失，如果桨错过，则球从屏幕上脱落。我们需要一种方法来检测并相应地采取行动。在以前的新的下方添加以下行：


```js
ball.checkWorldBounds = true;
ball.events.onOutOfBounds.add(function () {
  alert("Game over!");
  location.reload();
}, this);
```


添加这些行将使得球检查世界（在我们的例子中是画布）边界并执行绑定到`onOutOfBounds`事件的函数。当你点击生成的警报时，页面将被重新加载，以便你可以再次播放。

比较你的代码
------

你可以在下面的现场演示中查看本课程的完成代码，并使用它来更好地了解它的工作原理：

下一步
---

现在的基本游戏就是让我们通过引入砖块来更有趣的是 - 现在是建造砖块 (en-US)的时候了。

* « 上一页 (en-US)
* 下一页 » (en-US)
Player paddle and controls
==========================

* « 上一页 (en-US)
* 下一页 » (en-US)

这是Gamedev Phaser 教程 (en-US) 16 的**第 7 步**。你可以在Gamedev-Phaser-Content-Kit / demos / lesson07.html完成本课程后找到源代码。

我们有球从墙上移动并弹跳，但它很快变得无聊 - 没有互动！我们需要一种介绍游戏的方法，所以在这篇文章中，我们将创建一个桨来移动并击中球。

渲染桨
---

从框架的角度看，桨非常类似于球 - 我们需要添加一个变量来表示它，加载相关的图像资源，然后做出魔法。

### 装载桨

首先，添加`paddle`我们将在我们的游戏中使用的`ball`变量，就在变量之后：


```js
var paddle;
```


然后，在该`preload`功能中，`paddle`通过添加以下新`load.image()`调用来加载图像：


```js
function preload() {
  // ...
  game.load.image("ball", "img/ball.png");
  game.load.image("paddle", "img/paddle.png");
}
```

### 添加桨图形

所以我们不要忘记，在这一点上，你应该从 Github 抓住这个图形，并保存在你的`/img`文件夹中。

### 渲染桨用物理引擎

接下来，我们将通过`add.sprite()`在`create()`函数中添加以下调用来初始化我们的桨，将其添加到底部：


```js
paddle = game.add.sprite(
  game.world.width \* 0.5,
  game.world.height - 5,
  "paddle",
);
```


我们可以使用`world.width`和`world.height`值来将桨定位到我们想要的位置：`game.world.width*0.5`将在屏幕中间。在我们这个例子中，世界和画布是一样的，但是对于其他类型的游戏，例如侧滚滚，这个世界会变大，你可以修改它来创造有趣的效果。

你会注意到，如果你`index.html`在这一点上重新加载，那么桨是目前不完全在中间的。为什么？因为计算位置的锚总是从对象的左上角开始。我们可以改变它，使锚在桨的宽度的中间和它的高度的底部，所以更容易将其定位在底部边缘。添加以下新行以下的行：


```js
paddle.anchor.set(0.5, 1);
```


桨现在位于我们想要的地方。现在，为了使它与球碰撞，我们必须为桨提供物理效果。继续添加以下新行，再次在`create()`函数的底部：


```js
game.physics.enable(paddle, Phaser.Physics.ARCADE);
```


现在，魔法可以开始发生 - 该框架可以在每个框架上检查碰撞检测。要启用桨和球之间的碰撞检测，请将`collide()`方法添加到如下`update()`功能中：


```js
function update() {
  game.physics.arcade.collide(ball, paddle);
}
```


第一个参数是我们感兴趣的对象之一 - 球 - 第二个是另一个，桨。这有效，但不如我们预期的那样 - 当球击中桨时，桨从屏幕上掉下来！我们想要的就是球从跳板上跳起来，而桨子停在同一个地方。我们可以将`body`桨设置成`immovable`球，所以当球击中它时不会移动。为此，请在`create()`函数底部添加以下行：


```js
paddle.body.immovable = true;
```


现在它按预期工作。

控制桨
---

接下来的问题是我们不能移动桨。要做到这一点，我们可以使用系统的默认输入（鼠标或触摸，取决于平台），并将桨位置设置到位置的`input`位置。将以下新行添加到`update()`函数中，如下所示：


```js
function update() {
  game.physics.arcade.collide(ball, paddle);
  paddle.x = game.input.x;
}
```


现在在每个新的一帧上，桨的`x`位置将根据输入的`x`位置进行调整，但是当我们开始游戏时，桨的位置不在中间。这是因为输入位置尚未定义。要修复，我们可以将默认位置（如果输入位置尚未定义）设置为屏幕中间。更新上一行如下：


```js
paddle.x = game.input.x || game.world.width \* 0.5;
```


如果你还没有这样做，请重新加载`index.html`并尝试！

定位球
---

我们有桨按预期工作，所以我们把球放在上面。它非常类似于定位桨 - 我们需要将其放置在屏幕中间水平和垂直的底部，与底部有一点偏移。要按照我们想要的方式放置它，我们将把锚定位到球的正中间。找到现有的行，并将其替换为以下两行：`ball = game.add.sprite( ... )`


```js
ball = game.add.sprite(game.world.width \* 0.5, game.world.height - 25, "ball");
ball.anchor.set(0.5);
```


速度保持不变 - 我们只是将第二个参数的值从 150 改为 -150，所以球将通过向上移动而不是下降来开始游戏。查找现有`ball.body.velocity.set( ... )`行并将其更新为以下内容：


```js
ball.body.velocity.set(150, -150);
```


现在球将从桨的中间开始。

比较你的代码
------

你可以在下面的现场演示中查看本课程的完成代码，并使用它来更好地了解它的工作原理：

下一步
---

我们可以移动桨，并将球反弹，但是如果球从屏幕的底部边缘反弹，那又有什么意义？我们来介绍丢失的可能性 - 也称为游戏 (en-US)逻辑。

* « 上一页 (en-US)
* 下一页 » (en-US)