1
0
mirror of https://github.com/apachecn/eloquent-js-3e-zh.git synced 2025-05-29 08:12:22 +00:00
This commit is contained in:
wizardforcel 2018-05-31 22:22:13 +08:00
parent 506b7d92eb
commit 7237494b4b

76
16.md
View File

@ -10,7 +10,7 @@
本章将会介绍如何实现一个小型平台游戏。平台游戏(或者叫作“跳爬”游戏)要求玩家操纵一个角色在世界中移动,这种游戏往往是二维的,而且采用单一侧面作为观察视角,玩家可以来回跳跃。
### 15.1 游戏
## 游戏
我们游戏大致基于由Thomas Palef开发的Dark Blue[www.lessmilk.com/games/10](http://www.lessmilk.com/games/10))。我之所以选择了这个游戏,是因为这个游戏既有趣又简单,而且不需要编写大量代码。该游戏看起来如下页图所示。
@ -22,7 +22,7 @@
该游戏包含一个固定的背景,使用网格方式进行布局,可可移动元素则覆盖在背景之上。网格中的元素可能是空气、固体或岩浆。可可移动元素是玩家、硬币或者某一块岩浆。这些元素的位置不限于网格,它们的坐标可以是分数,允许平滑运动。
### 15.2 实现技术
## 实现技术
我们会使用浏览器的DOM来展示游戏界面我们会通过处理按键事件来读取用户输入。
@ -34,7 +34,7 @@
在下一章中,我们会研究另一种浏览器技术——<canvas>标签。该标签提供了一种更为传统的图像绘制方式直接处理形状和像素而非DOM元素。
### 15.3 关卡
## 关卡
我们需要一种人类可读的、可编辑的方法来指定关卡。因为一切最开始都可以在网格,所以我们可以使用大型字符串,其中每个字符代表一个元素,要么是背景网格的一部分,要么是可移动元素。
@ -59,7 +59,7 @@ var simpleLevelPlan = `
整个游戏包含了许多关卡,玩家必须完成所有关卡。每关的过关条件是玩家需要收集所有硬币。如果玩家碰到熔岩,当前关卡会恢复初始状态,而玩家可以再次尝试过关。
### 15.4 读取关卡
## 读取关卡
下面的类存储了关卡对象。它的参数应该是定义关卡的字符串。
@ -117,7 +117,7 @@ class State {
这又是一个持久性数据结构,更新游戏状态会创建新状态,并使旧状态保持完整。
### 15.5 角色
## 角色
角色对象表示,游戏中给定可移动元素的当前位置和状态。所有的角色对象都遵循相同的接口。它们的`pos`属性保存元素的左上角坐标,它们的`size`属性保存其大小。
@ -237,7 +237,7 @@ const levelChars = {
这给了我们创建`Level`实例所需的所有部件。
```
```js
let simpleLevel = new Level(simpleLevelPlan);
console.log(`${simpleLevel.width} by ${simpleLevel.height}`);
// → 22 by 9
@ -245,7 +245,7 @@ console.log(`${simpleLevel.width} by ${simpleLevel.height}`);
上面一段代码的任务是将特定关卡显示在屏幕上,并构建关卡中的时间与动作。
### 15.6 成为负担的封装
## 成为负担的封装
本章中大多数代码并没有过多考虑封装。首先,封装需要耗费额外精力。封装使得程序变得更加庞大,而且会引入额外的概念和接口。我尽量将程序的体积控制在较小的范围之内,避免读者因为代码过于庞大而走神。
@ -255,7 +255,7 @@ console.log(`${simpleLevel.width} by ${simpleLevel.height}`);
我们会封装的一部分代码是绘图子系统。其原因是我们会在下一章中使用另一种方式来展示相同的游戏。通过将绘图代码隐藏在接口之后,我们可以在下一章中使用相同的游戏程序,只需要插入新的显示模块即可。
### 15.7 绘图
## 绘图
我们通过定义一个“显示器”对象来封装绘图代码该对象显示指定关卡以及状态。本章定义的显示器类型名为DOMDisplay因为该类型使用简单的DOM元素来显示关卡。
@ -433,7 +433,7 @@ DOMDisplay.prototype.scrollPlayerIntoView = function(state) {
我们可以在link标签中使用rel="stylesheet"将一个CSS文件加载到页面中。文件game.css包含了我们的游戏所需的样式。
### 15.8 动作与冲突
## 动作与冲突
现在我们是时候来添加一些动作了。这是游戏中最令人着迷的一部分。实现动作的最基本的方案(也是大多数游戏采用的)是将时间划分为一个个时间段,根据角色的每一步速度和时间长度,将元素移动一段距离。我们将以秒为单位测量时间,所以速度以单元每秒来表示。
@ -595,7 +595,7 @@ Player.prototype.update = function(time, state, keys) {
重力、跳跃速度和几乎所有其他常数,在游戏中都是通过反复试验来设定的。我测试了值,直到我找到了我喜欢的组合。
### 15.10 跟踪按键
## 跟踪按键
对于这样的游戏,我们不希望按键在每次按下时生效。相反,我们希望只要按下了它们,他们的效果(移动球员的数字)就一直有效。
@ -623,7 +623,7 @@ const arrowKeys =
两种事件类型都使用相同的处理程序函数。该处理函数根据事件对象的type属性来确定是将按键状态修改为true“keydown”还是false“keyup”
### 15.11 运行游戏
## 运行游戏
我们在第十四章中看到的requestAnimationFrames函数是一种产生游戏动画的好方法。但该函数的接口有点过于原始。该函数要求我们跟踪上次调用函数的时间并在每一帧后再次调用requestAnimationFrame方法。
@ -703,9 +703,9 @@ async function runGame(plans, Display) {
</body>
```
### 15.12 习题
## 习题
#### 15.12.1 游戏结束
### 游戏结束
按照惯例,平台游戏中玩家一开始会有有限数量的生命,每死亡一次就扣去一条生命。当玩家生命耗尽时,游戏就从头开始了。
@ -730,7 +730,7 @@ async function runGame(plans, Display) {
</body>
```
#### 15.12.2 暂停游戏
### 暂停游戏
现在实现一个功能——当用户按下ESC键时可以暂停或继续游戏。
@ -774,3 +774,51 @@ async function runGame(plans, Display) {
### 怪物
它是传统的平台游戏,里面有敌人,你可以跳到它顶上来打败它。这个练习要求你把这种角色类型添加到游戏中。
我们称之为怪物。怪物只能水平移动。你可以让它们朝着玩家的方向移动,或者像水平熔岩一样来回跳动,或者拥有你想要的任何运动模式。这个类不必处理掉落,但是它应该确保怪物不会穿过墙壁。
当怪物接触玩家时,效果取决于玩家是否跳到它们顶上。你可以通过检查玩家的底部是否接近怪物的顶部来近似它。如果是这样的话,怪物就消失了。如果没有,游戏就输了。
```html
<link rel="stylesheet" href="css/game.css">
<style>.monster { background: purple }</style>
<body>
<script>
// Complete the constructor, update, and collide methods
class Monster {
constructor(pos, /* ... */) {}
get type() { return "monster"; }
static create(pos) {
return new Monster(pos.plus(new Vec(0, -1)));
}
update(time, state) {}
collide(state) {}
}
Monster.prototype.size = new Vec(1.2, 2);
levelChars["M"] = Monster;
runLevel(new Level(`
..................................
.################################.
.#..............................#.
.#..............................#.
.#..............................#.
.#...........................o..#.
.#..@...........................#.
.##########..............########.
..........#..o..o..o..o..#........
..........#...........M..#........
..........################........
..................................
`), DOMDisplay);
</script>
</body>
```