怪物狂欢 - 使用Emscripten在网络上制作游戏

当我们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的浮点渲染目标来实现某些后期处理效果(例如边缘轮廓和动态阴影)来进行改进。

EmscriptenGamePost
后期处理让一切都更漂亮了!

但它运行如何呢?

现在我们已经有了在浏览器中渲染的内容,并且通过快速更改来捕获输入,我们可以开始玩游戏并分析它的性能。我们发现的结果令人鼓舞:在Firefox中,游戏的asm.js版本直接“开箱即用”,其性能几乎达到了原生可执行文件的33%。而这只是将单线程网络应用程序与多线程原生可执行文件进行比较(所以实际上是不公平的比较!;)。这大约是我们使用快速Flash移植看到的性能的两倍(我们仍然使用它作为不支持asm.js的旧浏览器的备用选项,尽管我们最终希望完全弃用它)。

它在Chrome中的性能没有那么惊人,大约是原生性能的20%,但仍然在我们目标范围内:即它能否在2011年款的MacBook Air上以45-60 FPS(禁用Vsync)运行?答案是肯定的。我们希望谷歌能够随着时间的推移继续改进其浏览器上的asm.js的性能。但就目前而言,我们认为,除非您使用这项技术制作浏览器的“孤岛危机”(这也许并不遥远),否则即使在Chrome中,您似乎也有足够的性能来进行大多数类型的网络游戏。

BrowserFramerate
旧款MacBook Air上的60 FPS

将碎片拼凑在一起

因此,从开始到完成,我们在一周内就将我们的虚幻引擎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) 的负责人。

更多 Jeremy Stieglitz 的文章…

关于 Robert Nyman [荣誉编辑]

Mozilla Hacks 的技术布道者和编辑。发表有关 HTML5、JavaScript 和开放网络的演讲和博客文章。Robert 是 HTML5 和开放网络的坚定支持者,自1999年以来一直在从事网络前端开发工作——在瑞典和纽约市。他还定期在 http://robertnyman.com 上发布博客文章,喜欢旅行和结识新朋友。

更多 Robert Nyman [荣誉编辑] 的文章…


11 条评论

  1. Tom

    确实很有趣!不过,在学习方面,我想知道,使用 Unity3D 如何?Unity3D 有一个跨平台的快速原生插件,并且支持 Google Chrome 原生客户端。

    2013 年 12 月 12 日 下午 11:28

    1. Jeremy Stieglitz

      @ Tom,

      在我看来,Emscripten 相比 Unity 插件 + NaCl 的主要优势有两个方面:

      – 在需要使用 Unity 插件的浏览器上,插件安装要求可能会让您失去许多潜在的玩家。据 Trendy Entertainment 的一些曾经创建 Unity 网页游戏的同事告诉我,他们在插件关卡的用户流失率高达 75%。尽管 Unity 是一个相当“知名”的插件,但对于普通用户来说,安装他们可能从未听说过的插件仍然是一件令人担忧的事情。

      – 通过使用 Emscripten,您无需局限于使用 Unity。Unity 很棒,但还有其他功能强大的游戏引擎可以提供完整的源代码以便在 Emscripten 中编译,或者您可能想自己创建一个引擎来制作一个很酷的应用程序:)

      此外,Emscripten 的使用无需仅仅用于完整的网页应用程序,它还可以理论上用于创建网页托管的功能库,这些库可以在内部执行繁重的性能敏感工作,然后通过 JavaScript 将输出数据反馈到任何网页应用程序或 Unity 插件游戏。例如,我们很快就会使用这种方法来标准化我们的网络库。

      我个人认为,Unity 应该提供一个 Emscripten 网页版本作为部署选项。希望 Unity 总部有人能看到这一点!

      -Jeremy

      2013 年 12 月 14 日 下午 9:45

  2. Yuri

    很棒的技术栈!但是,由于我们的公司代理阻止了非标准端口,我无法登录游戏。为什么不简单地使用 80/443 端口进行网络连接呢?

    2013 年 12 月 13 日 上午 0:09

    1. Jeremy Stieglitz

      @ Yuri,

      实际上,我们没有理由不将 WebSockets 运行在像 80 端口这样的标准端口上!这是我们的疏忽,我们将在周一修复它!当它迁移到 80 端口时,我会在这里发布/更新博客,然后您应该能够在任何公司防火墙后面尝试游戏,哈哈!

      -Jeremy

      2013 年 12 月 14 日 下午 9:38

  3. Christian

    @Tom
    为每个架构编译代码并进行大量的原生代码实现,或者使用 Unity3D,这对于轻松入门来说很棒,但与将整个游戏带到平台无关的 JavaScript 中并获得这种性能相比,远没有那么令人印象深刻。
    请记住,此解决方案使用的是开放标准,而不是像 Unity3D 这样的专有基础,没有许可费用,所有人都可以访问。

    2013 年 12 月 13 日 上午 5:00

    1. Tom

      我理解您的观点,我同意您的看法。
      我被这个很棒的黑客技术所震撼,如果它速度快的话!好!
      但这仍然不是虚幻技术吗?

      2014 年 1 月 2 日 下午 5:17

  4. Andre Jaenisch

    你好,

    我已经将来自 firefoxosgaming.blogspot.com 的 Bob Thulfram 指向了这篇文章。
    但是目前(格林威治标准时间下午 3:57)似乎需要用户名和密码,所以我无法测试它。

    Debian GNU/Linux,使用 Firefox 26.0

    2013 年 12 月 13 日 上午 7:58

  5. Paul Irish

    精彩的写作!

    关于 pNaCL 的说明。上面说“只有在 Chrome 中工作是我们希望我们的游戏在所有主流浏览器中运行的愿望的绊脚石”。

    幸运的是,情况不再是这样。Pepper.js 是一个相当新的库,它将任何 pNaCL 应用程序通过 Emscripten 转化为 JS,以便在所有主流浏览器中运行:http://trypepperjs.appspot.com/

    我很好奇,是什么阻止了您将《怪兽狂欢》放到移动网络上?我认为,Firefox 和 Chrome(它们支持 WebGL 和 Web Audio)都希望您能够做到这一点。:)

    2013 年 12 月 13 日 下午 12:18

    1. Jeremy Stieglitz

      @ Paul,

      感谢您提醒我有关 pepper.js 的信息!这是一种非常有趣的方法,我们将在这里尝试一下。我仍然有一些潜在的测试方面的担忧,即需要在各种 CPU 架构上尝试 pNaCl 编译的应用程序以确保没有链接时/运行时问题,但我可能过高估计了这种情况会导致问题的可能性。毫无疑问,pNaCL 应用程序通过 Emscripten 在其他浏览器中运行的可能性使其更具吸引力……又是 Emscripten 拯救了局面?;)

      -Jeremy

      2013 年 12 月 14 日 下午 9:37

  6. v792933579

    浏览器内存不足,请重启

    2013 年 12 月 19 日 上午 4:31

  7. Karsten Bruch

    这是
    http://www.playverse.com/Anonplayer/0-a2aadd1b76e14d0e848ea1de18dca4e8
    暂时的吗?

    现在显示
    “匿名帐户未对此游戏启用!”

    干杯,
    Karsten

    2013 年 12 月 29 日 上午 7:13

本文的评论已关闭。