构建多人太空射击游戏:第一部分
介绍
太空——最后的边疆。也是令人惊奇的炸毁东西的好地方。
在本教程中,我们将构建一款多人太空射击游戏,但有一个不同之处:我们不是让每个人都在自己的电脑上玩,而是将传统客厅合作的精神带入现代。
游戏本身将在单个浏览器窗口中运行。每个玩家在智能手机上打开一个 URL,智能手机会变成游戏手柄,并允许他们的飞船加入游戏。
我们将使用什么
让我们简单一点:我们将使用Pixi.js进行渲染,并使用deepstream.io作为多人服务器。
Pixi.JS是一个适用于浏览器的 2D 渲染库。它使用 WebGL(Web 图形库),并尽可能将繁重的工作留给 GPU(图形处理单元)。否则,Pixi.JS 会求助于画布。Pixi 就是这样的:一个渲染库,为您提供您期望的所有 Stage、Sprite 和 Container 对象,但没有游戏逻辑构造 - 这些是我们的工作。
deepstream.io是一种用于实时连接的新型服务器。它处理各种持久连接,例如用于浏览器的 TCP 或 Websocket,并提供数据同步、发布-订阅和请求-响应等高级概念。最重要的是,deepstream.io 速度超快。
60 帧每秒
我们希望实现流畅的 60 FPS 帧率,并且希望我们的控件能够顺畅运行。这意味着游戏手柄上的每一次触摸都需要在 16.6 毫秒(或一帧)内转换为屏幕上的操作。幸运的是,pixi 和 deepstream 完全能够实现这一壮举。
但也有网络延迟!信息需要时间才能传输。事实上,光纤每 10,000 公里大约需要 67 毫秒,这还不包括交换机、路由器和其他进一步减慢速度的网络跳数。这意味着,如果您在美国运行服务器并在欧洲玩游戏,您的游戏不会感觉特别灵敏。
关于本教程
本教程将带您了解高级概念和所有实施的棘手部分 - 为简洁起见,它跳过了许多项目设置、css/样式和大多数更常见的方面。要了解所有内容如何组合在一起,只需前往 Github存储库。
需要最新的浏览器
本教程充分利用了 WebGL 和 ES6 语法等新浏览器功能。我们的游戏在所有最新浏览器上均能发挥最佳效果(在 Chrome 51、FF 47 和 Edge 25 中测试过),但在老旧的 IE 8 上玩起来就没那么有趣了。
结构
让我们首先创建以下三个文件:
- game.js将包含创建 PIXI 阶段的主要游戏对象,添加和删除太空飞船并管理游戏循环(稍后将详细介绍)
- spaceship.js将代表单个玩家/宇宙飞船
- index.js将启动一切
创造舞台
PIXI 基于显示对象(例如“精灵”或“影片剪辑”)的层次结构。这些对象可以分组到“容器”中。每个 PIXI 项目都以最外层的容器开始,我们将其称为“舞台”。
//in game.js
class Game {
constructor(element) {
this.stage = new PIXI.Container();
}
}
要将对象层次结构转换为图像,您需要一个“渲染器”。PIXI 将尝试使用 WebGL 进行渲染,但必要时可以回退到画布。
对于我们的太空射击游戏,我们将让 PIXI 决定使用哪种渲染器。唯一的要求是渲染器需要扩展到整个屏幕尺寸,并且不应具有背景颜色,以便我们可以在其后面放置基于太空的图像。
要创建渲染器,请将以下几行添加到游戏类的构造函数中:
this.renderer = PIXI.autoDetectRenderer(
window.innerWidth,
window.innerHeight,
{transparent: true},
false
);
element.appendChild( this.renderer.view );
}
添加宇宙飞船
是时候将一艘宇宙飞船添加到我们的舞台上了。我们的飞船将由称为“精灵”的小图像组成。要创建一个,我们将告诉 PIXI 创建一个PIXI.Sprite.fromImage( url )并将其移动到其初始坐标。默认情况下,这些坐标指定了精灵的左上角。相反,我们希望它们指定中心,因此我们还需要将精灵的 x 和 y 的锚点位置设置为 0.5。这也将在稍后旋转精灵时用作枢轴点。最后,我们将宇宙飞船添加到舞台上。
// in spaceship.js
class SpaceShip {
constructor(game, x, y) {
this._game = game;
this._body = PIXI.Sprite.fromImage("/img/spaceship-body.png");
this._body.position.x = x;
this._body.position.y = y;
this._body.anchor.x = 0.5;
this._body.anchor.y = 0.5;
this._game.stage.addChild(this._body);
}
}
渲染舞台
那么我们的宇宙飞船在哪里?到目前为止,我们已经创建了一个舞台和一个渲染器,但我们还没有告诉渲染器渲染舞台。我们将通过添加一个名为_tick()的方法来做到这一点。
_打钩()
为什么我们的渲染方法叫做tick()而不是render()?这个方法实际上会成为我们游戏的起搏器。每次要渲染一帧时,这个方法都会计算自上一帧以来经过的时间,通知游戏中的所有对象有关更新阻碍,渲染舞台,最后安排下一帧。
为此,我们将使用名为requestAnimationFrame(callback) 的浏览器方法。这将安排一个函数在下次可以绘制帧时执行。我们将在game.js中添加此方法两次- 一次在构造函数的末尾以绘制初始帧,一次在_tick()方法本身内。
// in game.js
constructor( element ) {
...
requestAnimationFrame( this._tick.bind( this ) )
}
_tick() {
this.renderer.render( this.stage );
requestAnimationFrame( this._tick.bind( this ) );
}
如果一切顺利,你的游戏现在应该是这样的:
有点颜色
到目前为止,一切都很好,但我们的宇宙飞船看起来仍然有点苍白。这也不足为奇,因为我们使用的精灵只是一张灰度图像。要为每个玩家添加不同的颜色,我们需要设置一个名为tint 的属性。
this._body.tint = 0x00ff00; // green in hex
此色调属性产生以下图像:
添加炮塔
接下来是炮塔。需要进行一些重构。飞船的机身和炮塔必须同步移动,并且需要相互定位。为了实现这一点,我们将创建一个PIXI.Container,并将飞船的机身和炮塔放入其中。
我们将宇宙飞船构造函数中的代码改为:
// container
this._container = new PIXI.Container();
this._container.position.x = x;
this._container.position.y = y;
// body
this._body = PIXI.Sprite.fromImage("/img/spaceship-body.png");
this._body.tint = this.tint;
this._body.anchor.x = 0.5;
this._body.anchor.y = 0.5;
this._container.addChild(this._body);
// turret
this._turret = PIXI.Sprite.fromImage("/img/spaceship-turret.png");
this._turret.tint = this.tint;
// the turret doesn't sit exactly at the center of the ship
this._turret.anchor.x = 0.45;
this._turret.anchor.y = 0.6;
// the turret's pivotin point is towards the bottom of the sprite
this._turret.pivot.x = 1;
this._turret.pivot.y = 7;
this._container.addChild(this._turret);
// add the whole container to the stage
this._game.stage.addChild(this._container);
我们的宇宙飞船现在看起来相当完整:
<fon
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~