当我们Trendy Entertainment & Nom Nom Games的工程团队决定将我们的一款新的虚幻引擎3游戏 - 怪物狂欢在线 - 开发为跨平台游戏时,我们知道一个无缝的多人网络浏览器版本将是这一体验的核心。然而,一个很大的问题是确定要使用哪些基本技术才能将我们的游戏带到网络上。作为一名C++面向的开发者,我们很快确定从头开始重写游戏引擎是不可能的。我们需要一个解决方案,能够让我们以高效的方式将现有的代码移植到浏览器可以使用的一种格式中…
TL;DR? 看视频!
实地考察
我们仔细研究了我们面前的各种选择:FlasCC(一个GCC Flash编译器)、谷歌的NaCl、一个定制的原生C++扩展,或者Mozilla的Emscripten & asm.js。
在我们的测试中,Flash运行缓慢,并且在Pepper(Chrome)和Adobe的插件版本之间存在不一致的行为。再加上日益繁重的插件需求,我们选择寻找其他更无缝、更具前瞻性的方法。
NaCl的问题是需要一个封闭式的分发网站,这会将我们与用户的直接联系隔离开来,而且它还与处理器相关。pNaCL消除了封闭式网站的要求,并增加了动态代码编译的支持,但仍然存在处理器相关代码的问题,在我们看来,这需要进行特定设备的测试,而且启动时间可能会很长,因为代码将在首次运行时链接。最后,只在Chrome中工作将是我们的一个障碍,因为我们希望我们的游戏能够在所有主要浏览器中运行。
一个定制的插件/扩展使用C++将需要我们进行大量的测试和维护工作,才能在不同的浏览器、处理器架构和操作系统上运行,而且这种安装需求可能会吓跑许多潜在的玩家。
事实证明,对于我们团队而言,Emscripten编译器和asm.js是解决这些挑战的最佳解决方案,并且当与一组其他新兴的Web API相结合时,它使浏览器成为一个能够进行即时高端3D游戏的全功能平台。这只需要一点点试错,才能弄清楚我们该如何将它拼凑在一起…而这正是我们将在本文中介绍的内容!
迈向勇敢新世界的第一步
我们Trendy游戏工程师主要是老派C++程序员,所以Emscripten能够将我们现有的应用程序(基于Epic Games的虚幻引擎3)编译成asm.js优化后的Javascript,而且几乎不需要修改,这让我们感到惊讶。
要让项目能够使用Emscripten进行编译和运行,主要需要对虚幻引擎3特定的代码进行一些调整,这些调整实际上就是…1、2、3。
1.
// Esmcripten needs 4 byte alignment
FNameEntry* Allocate( INT Size )
{
#if EMSCRIPTEN
Size = Align( Size, 4 );
#endif
......
}
2.
// Script execution: llvm needs aligned data
#if EMSCRIPTEN
#define XFER(T)
{
T Temp;
if (!Ar.IsLoading())
{
appMemcpy(&Temp, &Script(iCode), sizeof(T));
}
Ar << Temp;
if (!Ar.IsSaving())
{
appMemcpy(&Script(iCode), &Temp, sizeof(T));
}
iCode += sizeof(T);
}
#else
#define XFER(T) { Ar << *(T*)&Script(iCode); iCode += sizeof(T); }
#endif
3.
// This function needs to synchronously complete IO requests for single-threaded Emscripten IO to work, so added a ServiceRequestsSynchronously() to the end of it which flushes & blocks till the IO request finishes.
FAsyncIOSystemBase::QueueIORequest()
真的,就是这些!在我们花了大约一天时间进行调整之后,我们就将游戏的Javascript“可执行文件”编译好,并在浏览器中运行。由于没有实现图形API,所以程序会崩溃,但它会输出日志!幸运的是,我们已经准备好使用虚幻引擎3的OpenGL ES2版本的渲染子系统,所以将渲染器移植到WebGL只花了一天时间。
WebGL似乎与OpenGL ES2相比,具有超集的功能,所以只需更改一些API调用,就可以将使用的着色器和方法匹配起来。事实上,我们能够通过使用WebGL的浮点渲染目标来实现某些后期处理效果(例如边缘轮廓和动态阴影)来进行改进。
但它运行如何呢?
现在我们已经有了在浏览器中渲染的内容,并且通过快速更改来捕获输入,我们可以开始玩游戏并分析它的性能。我们发现的结果令人鼓舞:在Firefox中,游戏的asm.js版本直接“开箱即用”,其性能几乎达到了原生可执行文件的33%。而这只是将单线程网络应用程序与多线程原生可执行文件进行比较(所以实际上是不公平的比较!;)。这大约是我们使用快速Flash移植看到的性能的两倍(我们仍然使用它作为不支持asm.js的旧浏览器的备用选项,尽管我们最终希望完全弃用它)。
它在Chrome中的性能没有那么惊人,大约是原生性能的20%,但仍然在我们目标范围内:即它能否在2011年款的MacBook Air上以45-60 FPS(禁用Vsync)运行?答案是肯定的。我们希望谷歌能够随着时间的推移继续改进其浏览器上的asm.js的性能。但就目前而言,我们认为,除非您使用这项技术制作浏览器的“孤岛危机”(这也许并不遥远),否则即使在Chrome中,您似乎也有足够的性能来进行大多数类型的网络游戏。
将碎片拼凑在一起
因此,从开始到完成,我们在一周内就将我们的虚幻引擎3 PC游戏变成了一个运行良好、图形丰富的网络游戏。但是,我们接下来要做什么呢?好吧,它仍然需要:音频、网络、流媒体和存储。让我们来讨论为每个系统使用的各种技术。
音频
这很简单,因为除了Flash之外,真正可靠的标准化网络音频系统只有一个:WebAudio。同样,这个API与它的移动版本OpenSL非常匹配,而我们已经集成了OpenSL。所以,一旦我们替换了各种调用,我们就有了。
在Mac Chrome中,有一个明显的问题,即标记为“循环”的声音有时不会被销毁,所以我们实现了一个Chrome特定的黑客来手动循环声音,并向谷歌提交了错误报告。好吧,我们在使用浏览器API时发现的一件事是,并不能保证每个浏览器都能够完全按照规范实现功能,但它可以完成工作!
网络
这有点棘手。首先,我们研究了Bananabread演示中使用的WebRTC,但WebRTC当然用于浏览器之间的通信,而这并不是我们想要做的。我们的在线游戏服务使用服务器和客户端架构,具有集中式基础设施,因此在这种情况下,WebSockets是我们要使用的API。棘手的地方在于,我们必须在JavaScript缓冲区中处理所有WebSockets的传入和传出数据,然后将其传递给“C++”Emscripten编译的游戏。
通过一些回调,这行得通,但我们还必须使用我们的UDP游戏服务器代码,并将WebSockets TCP风格的层放置到它上面 - 有些迭代是必要的,才能使数据包以WebSockets期望的完全相同的方式格式化,但一旦我们做到了这一点,我们的浏览器游戏就可以与我们后端托管的Linux专用游戏服务器通信,没有任何问题!
流媒体和存储
在网络上的一个优势是能够轻松访问浏览器的异步下载功能来流式传输内容。我们当然利用了我们游戏的这一点,初始下载时间不到10 MB。其他所有内容都按需在您玩游戏时使用标准的浏览器http下载请求流式传输:纹理、音效、音乐,甚至骨骼网格和关卡包。但更大的问题是如何可靠地存储这些内容。我们不想仅仅依赖于浏览器缓存,因为它不能保证立即进行游戏加载,因为我们无法预先查询磁盘上的浏览器缓存中是否存在某个内容。
为此,我们使用了IndexedDB API,它允许我们从安全的抽象存储位置异步保存和检索数据对象。它在Chrome和Firefox中都可以工作,尽管它仍然很挑剔,因为数据库有时会损坏(也许是在异步写入期间终止),并且必须重新生成。在最坏的情况下,这只会导致用户已经收到的内容重新下载。
我们目前正在研究这个问题,但除此之外,IndexedDB确实运行良好,并且具有为我们的应用程序提供标准文件IO功能的优势,这对于存储我们下载的内容很有用。(更新:截至12月10日的Firefox Nightly版本似乎会在发生这种情况时自动重置IndexedDB存储,并且它可能不会再次发生。)
立即玩,拥抱未来!
虽然我们还有更多的性能分析和调整工作要做,因为我们才开始使用Firefox的VTune支持来符号地分析浏览器中的asm.js性能。尽管如此,我们对目前的情况还是非常满意的。但不要听我们说,请亲自试一试,这里不需要安装或注册
立即在浏览器中匿名试玩我们的演示测试!
(如果我们的游戏服务器在负载下限制了访问权限,请耐心等待,我们仍在测试后端的可扩展性!)
我们在Trendy 设想有一天,无论身在何处,无论使用何种设备,任何人都可以畅玩任何游戏,无需摩擦、门槛或中间商。通过这些尖端网络技术的正确组合,今天就可以实现。我们希望其他有进取心的游戏开发者加入我们,通过网络直接接触玩家,得益于 Emscripten & asm.js,网络可能会成为功能最强大、影响范围最广的“游戏主机”!
关于 Jeremy Stieglitz
Jeremy Stieglitz 自2001年起就开始创建游戏和游戏技术,当时他开始与他人共同开发“Reality Engine”,这是一款业余 3D 游戏引擎,他于2005年将其出售给了Epic Games。从那时起,他开发了许多虚幻引擎独立游戏和工作室游戏,包括《怪兽狂欢:郊区之战》和《细胞因子》。2009年,他与他人共同创立了Trendy Entertainment,并在那里创建了《地下城守护者》。对于《地下城守护者 2》,他一直专注于Trendy的跨平台 Playverse 在线基础设施,并最近创立了Trendy子公司“NomNom Games”,专门为这项服务开发独立规模的游戏。他坚信技术驱动的跨平台、设备无关的游戏开发,以及网络的力量,让内容创作者能够直接连接到消费者,没有任何障碍。Jeremy 是Trendy Entertainment (www.trendyent.com) 的联合创始人兼首席技术官,以及NomNom Games (www.nomnomgames.com) 的负责人。
关于 Robert Nyman [荣誉编辑]
Mozilla Hacks 的技术布道者和编辑。发表有关 HTML5、JavaScript 和开放网络的演讲和博客文章。Robert 是 HTML5 和开放网络的坚定支持者,自1999年以来一直在从事网络前端开发工作——在瑞典和纽约市。他还定期在 http://robertnyman.com 上发布博客文章,喜欢旅行和结识新朋友。
11 条评论