用于 Unity 和虚幻引擎的 Asm.js 与 WebGL

发表于2016-07-27
评论0 4.5k浏览
       本文来自微软,是一个 web 开发系列文章的一部分。感谢你对实现了 SitePoint 的成员的支持。


       Unity 以及 Epic 的虚幻引擎,是频繁被游戏开发者使用的中间件工具,并不被限于去创建被编译成作为可执行文件运行的应用程序。Unity 之前有一个 web 播放器,它是一个使用了 ActiveX 的可下载插件。Chrome 停止了对 NPAP (网景(Netscape) 插件 API)的支持,但却在一年前才公布这个消息。
       在 4 月份,随着 Chrome 42 stable 的发布,他们终于把斧头挥向了他。要说为什么,可以有很多原因,而“挂起、奔溃、安全问题还有代码复杂性”则是最常被他们挂在嘴边的。Google 建议转移到 web 标准支持的技术,比如        WebGL,这种技术我下面会提到。
      因为有了 web 技术标准,微软也正效仿上面的做法,吐槽自家的 ActiveX,VBScript,attachEvent,以及其它传统的技术。对于 ActiveX 空间的需求已经因为 HTML5时代的功能而减少了很多, 其同样支持跨浏览器间互操作的产生。
      随着 WebGL 和 asm.js 的出现,开发者现在在浏览器中就可以充分利用他们计算机设备的能力,并接触到之前无法企及的市场。在本教程中,我会告诉你他们如何做到这一点的知识:

为什么要编译成 JavaScript?
       JavaScript 是唯一一种能在所有的浏览器上运行的语言。尽管只有 JavaScript 将会在浏览器上面运行,但你还是可以用其他语言编写代码,然后仍然编译成 JavaScript,如此就可以让它们运行在浏览器上了。这是由一种以 emscripten 而闻名的技术变成可能。
       Emscripten 是一种基于 LLVM 的项目,能以 asm.js 形式将 C 和 C++ 代码编译成高性能的 JavaScript 代码。简言之: 近乎于使用 C 和 C++ 直接运行与浏览器里面的原生速度,甚至更好。emscripten 将 OpenGL, 一种桌面图形     API,转化成 WebGL,它是这类 API 的 web 变体。
          那么这些代码是如何转成 WebGL 的呢?
          Unity 最近也为他们的运行在 WebGL 上的软件做了一些基准测试。
          然而,让代码进行交叉编译 (经常也被叫做 transpiling ) 并非没有缺陷。静态类型语言中发掘出来的通用的性能增强技术,比如多线程 (JS 是单线程的) 以及 SIMD (单指令多数据) 都无法使用。
          Mozilla,近来也跟着其他领头的技术公司搞起 SIMD.js 来,因此性能的提升和处理器使用的降低应该会是不久的未来将发生的事情。点击这里可以了解更多。出来上述的那些性能上的调整, Unity 还有赖于他们新的脚本运行,IL2CPP (内联 2 C++)。那是另外一篇文章要讲的事情了,不过 Unity 已经在每隔几个星期就做一个的华丽的系列网站中向诸位呈现 IL2CPP 的功能了。
          IL2CPP 拥有两个独立的部分:
          一个预先 (AOT) 编译器
          一个用于支撑虚拟机 (VM) 的运行时库
          来自.NET的中间语言 (IL) 经由 AOT 编译器编译成 C++ 源代码。一些诸如独立于平台的对线程和文件的访问,还有垃圾收集器,这些服务和抽象都是运行时库带来的好处。
          这里就能看到:


图片来源于 Unity
           当你在一台 Windows 机器上运行一个 .exe 时,在那个点上它实际上还不是字节码 (0 和 1). 它仍然还处在 VM 在运行时读取的一个二进制文件中, 然后才被转成字节码。Windows 机器上的 DotNet CLI 就是能读取这个二进制文件的虚机示例。CPU 除了二进制谁都不认,所以这一额外的步骤是需要的。
          仍然感到迷惑? 这篇文章解释了机器码、字节码和VM间的不同之处。

asm.js 是如何适应需要的?
          Asm.js,Assembly JavaScript 的简称, 它是 JavaScript 的一个自己。一个 asm.js 程序,不管是运行在一个现有的 JavaScript 引擎中,还是一个能识别并优化 asm.js 的预先(AOT)编译引擎中,其行为都是一致了 — 当然,速度要除外!
          速度方面,很难就它与原生代码的比较进行精确的测量,而之前对被编译成 asm.js 的 C++程序所进行的基准测试显示,一般会比使用 clang 这个面向 C,C++ 以及 Obj-C 编程语言的编译器所进行的原生编译慢两倍,有一点很重要,那就是这一测试是在对单线程程序运行最有利的情况下进行的。下面有更多关于 JavaScript 语言局限的内容。
          在后天, Clang 使用了 LLVM,它是一个用于构建,优化并产品中间甚或二进制机器码文件 (还是那些0和1)的库。LLVM 可以被当做一个编译器框架来用,其中你可以提供 “前端”(解析器和词法分析器,比如 Clang) 和 “后端” (将 LLVM 展现转换成实际的机器码的代码)
          想阅读更多: Mozilla 的 Alon Zakai 有一个美轮美奂的幻灯片 详细描述了这是如何运作的。
          那么 asm.js 有多酷呢? 好吧它有自己的 Twitter 账户, @asmjs。不过 asm 的站点有点冷清,它包括 W3C 的文档,以及一个内容全面的 FAQ。更好的是,Mozilla 在 2014 年对 Humble Mozilla Bundle 进行了整合,让你可以买到一堆利用 asm.js 的游戏。

为什么不把 JavaScript 转成 asm.js呢 ?
          JavaScript 因为其动态特性,并不能被编译成 asm.js,并提供诸多好处。当尝试将其编译成 C 甚至是 本地代码 是也会遇到同样的问题 – 使用了它的 VM 有必要关注那些非静态的方面。不过,你也可以手写 asm.js.
          如果有人已经以一种完全静态的方式对标准的 JavaScript 进行了翻译,那就有可能会有要使用 asm.js 的需要了。Asm.js 的存在是为了保证不需要开发做出额外的努力就让 JavaScript 变得更快。让 JIT 去理解一门动态语言及其静态编译器是非常困难的。
          为了更好的理解这一点,重点在于理解为什么 asm.js 会提供一种性能上的优势;或者说为什么静态类型语言执行起来能比动态的类型语言更好。原因之一就是“运行时类型检查需要花费时间”而思想更加深刻的一种回答应该要把对于优化静态类型代码的增强特性包含进来。来自于一种诸如 C 这样的静态类型语言的终极天赋就是事实上在这类语言的代码将要被编译时,编译器就知道每个对象的类型。
          Asm.js 是 JS 的一个受限子集,能很容易的被翻译成字节码。为了让这个子集获得这种优势,第一步必须做的就会是需要去分离 JS 所有的高级特性,这会有些复杂。不过 JavaScript 引擎是被优化过的并被设计成能将所有的那些高级特性直接翻译成字节码 – 因此像 asm.js 这样的一个中间步骤并没有提供太多的好处。

WebGL 是做什么的?
          WebGL (Web 图形库) 是一套 JavaScript API,使用它无需使用插件就能在任何兼容的 web 浏览器上渲染交互式的 3D 计算机图形和 2D 图形。WebGL 有三个明显的优势:
          多任务 : 对反光材质或者复杂光照环境的绘制会产生大量的开销,而鉴于 JavaScript 是单线程的而且受限于 CPU 的性能,为什么将这些任务分一些给 GPU,让其承担一些重任呢 ?
          性能 : 利用(GPU内置于设备中的)硬件加速,WebGL 非常适合用来实现游戏和复杂的视觉效果。
          着色器 : * * 复杂的视觉效果可以用被称作”着色器(shader)“的小程序来产生。这可能跟产生一个怀旧的棕色效果,或者对诸如水或者火焰这样更加复杂的模拟一样简单。看看 Shadertoy 所展示的一些例子,它们着实突出了这一点.
          当你对一个 WebGL 工程执行 build 操作时,Unity 会穿件一个目录,里面包含下面这些文件:
          一个 index.html 文件,将你的内容嵌到一个 web 页面中。
          一个 JavaScript 文件,包含面向播放器的代码。
          一个 .mem 文件,包含一个二进制镜像,用来初始化播放器的堆内存。
          一个 .data 文件,包含了资源数据和场景。
          一些辅助性的 JavaScript 文件,用来初始化和加载播放器.
          你也可以定制页面的样式来更好的契合游戏主题,而全屏 API也被推荐用来获得一个更加身临其境的体验。


对学习 WebGL 感兴趣吗? 进入 WebGL 学院 去看看完整的课程吧。

WebGL 少了些什么?
          WebGL 是 OpenGL ES 规范的一个子集。这是常见于诸如 Android 和 iOS 等移动设备上面的图形 API。那个 ES (嵌入式系统) 规范实际上就是 OpenGL —— 用于桌面机器和控制台的图形 API,例如 PlayStation 和 Wii —— 的一个子集。 因为 WebGL 并不是一个直接一对一匹配 OpenGL 的东西,所以有些功能特性是没有的。
          这里有一个简短列表描述了目前 Unity 游戏的 WebGL 版本还不支持的缺失功能特性。我们期待这些都会随着时间得到改善。
          运行时生成物质纹理
          电影纹理
          除了 WWW 类之外的网络支持(一个 WebSocket 插件)
          对于摄像头和麦克风访问的支持
          硬件光标支持
          大部分非基础的音频功能
          脚本调试
          线程
          任何需要动态生成代码的 .NET 特性

浏览器的支持程度如何 ?


          这里是问题所在。你可以就在这儿试试在其中两类浏览器中 WebGL demo 的效果。你需要使用支持 asm.js 的浏览器。在撰写此文时,对于 asm.js 的支持情况如下如下:
          Firefox
          Chrome
          Edge
          Safari
          有一点很重要,那就是对于 asm.js 规范在所有的浏览器之间并不是 100% 完全都实现了的,因此效果上会有所不同。不幸的是, asm.js 并没有收录在时下流行的特性检查网站 CanIUse.com,因此很难清除的了解其在每个浏览器中的支持程度如何。其也不会再移动浏览器上面起作用,因为当前它们还不支持 asm.js,尽管其中一些已经部分支持 WebGL 了。在 2014 年 11 月份我写过一篇关于 《WebGL 在移动设备上的现状》 。

性能怎么样?
          你可以在自己的浏览器上面试试 Unity 的基准测试套件, 看看它们执行从 C# 到 JavaScript 的转换效果到底怎么样. 这涵盖了从 Mandelbrot GPU 测试到 2D 物理以及粒子这所有的东西。在微软的 Edge 浏览器发布之前的,2014 年 10 月的这篇来自于 Unity 的文章,也突出了一些有趣的东西:
          在几乎所有的基准测试中,Firefox 的 asm.js 比 Chrome 和 Safari 的都要快,并且是当前运行Unity WebGL内容最好的浏览器。
          当你总是和 GPI 绑定时,可以预期到 WebGL 的执行同原生代码非常的相近。
          在一些领域,WebGL 实际上能显著的跑赢原生代码。 对于许多依赖脚本性能 (Mandelbrot 和 CryptoHash, 都用 C# 实现了其算法)的测试情况就是这样,因为 IL2Cpp 可以产生更加优化的代码 ( 这篇文章中有关于此的更多信息)。
          对于要重点优化的多线程和 / 或 SIMD(Unity 5.0 中的 PhysX 3.3 现在完全是多线程的),比如3D 物理测试这些领域,原生代码仍然能够比 WebGL 快上几倍。比起那个,2D 物理方面拿 Firefox 和原生 (Box2D 不是多线程的)做比较则非常的相近了。我们希望未来将带来面向 JavaScript 的 SIMD 和多线程扩展, 使这里的现状得到改变。
          那么,对于作为的开发者的你而言这有意味着什么呢? 自然有一些领域 WebGL 明显比原生代码要慢,比如那些要用到多线程的领域,不过 WebGL 性能还是不错的,并且只会与日俱增(而不回下降)。事实上,W3C 这个致力于开发 web 标准的国际性的社区刚刚发布了 WebAssembly, 这是一个新的面向 web 上安全代码的中间表现层。简称作“Wasm”, 是一种新的面向底层安全代码的二进制语法,暂时将会和 asm.js 同时运作。它将会对运行于浏览器中的 JavaScript 应用程序进行更深入的性能提升。
          Alon Zakai 三月份写过一篇文章,描述了过去一年主要的浏览器中 asm 和 JavaScript 速度整体提升了多少。

为什么会有人想这样做?
          这是一个普遍会被问到的问题。 话虽这样说,不过我最长听到的还是 ——“谁会想要下载一个 60MB 大的网站呢? —— 而你是对的 —— 60MB 对于一个网站来说是很巨大的!不过我想同样是每天从 YouTube 和 Netflix 下载 GB 大小视屏内容的人们也正是那同一群人。如果你把它看作是一个网站,那么它确实很大。不过如果你把它看作是一个游戏程序,60MB 就算是小的了!下面的案例学习所也对此进行了描述。
          此外,作为一名开发者的你现在可以绕过应用商店的限制了。想要更新你的应用程序吗?没问题,向你的网站推送一个新的构建就可以了。不想等待 iOS App Store 为期一个星期的审批程序(如果审批通过了的话,那确实就只有一个星期)吗? 推送更新到你自己的网站就可以啦,完美。
          你当然也可以总是用收费墙的形式进行售卖;或者进行某种形式的授权。此外,你无需为应用商店支付许可年费,也无需为他们的商店和系统桌面准备图片和图标,等等。现在情况看起来开始吸引人了,而我们仅仅只是触及了表面。
          看看 Illyriad 游戏的团队在发展期就太空战斗这个主题所做的。一开始他们的数据流只有一点点,在向你发送额外的数据量之前,这点数据量只够吸引你过去看看。我相信在我们最后一次谈话期间,它开始达到 50MB。你可以在观看我与他们一起做播客期间更多了解到它。用户可以立即跳进去并且玩起来 。没有庞大的客户端要下载,而进入的门槛也在瞬间被降低了。十几岁的我也许会在无尽的岁月中一直喜欢着它。
          相比之下,传统的方式在你开始播放游戏内容之前就得一次性的把所有东西都发送给你。当然,游戏主机已经开始使用“块”了,它会将游戏内容截断成较小的快,并且几乎是立即就从你需要播放的那部分开始下载。
          Jonas Echterhoff 曾指出,在 Unity 中, 通过利用 AssetBundles,流式的资源已经是可能的了。或者,你也可以试试这个 Asset Store 包,它会将 WebGL 构建数据进行重新打包,以此来将你的构建中的场景分割成多个文件,而你的内容在第一个场景被加载好就能够开始播放了:
https://www.assetstore.unity3d.com/en/#!/content/38368
          为了专注于星际空间的主题,我曾看过《星际公民》, 其内容看起来将近有 100GB 那么多。说实话你真的相信自己会将 100GB 的内容全部都体验一遍吗?

离线体验
          只因为你是指向的一个 HTML5 站点并不意味着你不能拥有一种离线的体验。不相信我?看看这个来自于 Snowbuddy 的创始人之一 Syd Lawrence 的视频吧。 在 2015 年的 PhoneGap 日,Syd 运行了《我们做的 Sh 真棒》,并且在其演讲期间向我们展示了一些用 PhoneGap 做的很棒的 app,还有他的关于制作高性能 PhoneGap App 的七条建议,而且还特别指出了如何创造出离线的体验。
让你的 APP 能混合利用本地和远程的内容,这一做法是明智的。像 IndexedDB、localStorage 和 AppCache 这些受到 Microsoft Edge 支持的技术,都能实现这一点。本地的页面可以放在你的 App 包中保管,那样仍可以提供一种基础的离线体验。
          
案例分析: Owlchemy 实验室的 Aaaaa! 被从 Unity 转成了 asm.js


          Owlchemy 实验室的团队已经在 2014 年对他们的游戏 Aaaaa!  进行了转化,并且 就这一过程组织了一篇不错的分析评论文章。 使用去年 6 月释出的 WebGL 预览版本,他们已经能够利用已有的 C# 代码,将其转成一个 JavaScript 文件,这个文件有超过 100 多万行那么长的代码!
          Aaaaa!  有级数超 200 ,在运行时会有超过 300 个资源被产生出来,此外还有 38 首完整的歌曲。在 PC/mac 上它们看起来就是一个 300MB 大小未压缩的文件,所以你可以想象的到,要是每次想要玩这个游戏都得下载所有这些东西应该会比较麻烦的。
          对存储的节约最大的来源之一就是 Unity 的音频剪辑流媒体方案,它可以在运行时按需处理音乐流。完成时,它们最终被压缩的 WebGL 构建大小,包括了所有载入的资源还有 Unity 引擎本身,结果是 68.8 MB。压缩后的独立 PC 构建是这个大小的 3 倍,也就是 192 MB。
          当然有一些 UX 上的变更需要做,包括重新绑定 ESC 键,它在许多游戏中应该映射的是一个暂停菜单,但在浏览器中则会是退出全屏并解除对鼠标的锁定。此外,因为浏览器的安全模型会强制让这些游戏沙盒化,所以在将数据块保存到磁盘上或者从用户的硬盘上加载自定义音频时可能会有问题。
          最后,考虑支持某种形式的云同步特性是重要的,因为游戏玩家常常不会只在同一台机器上玩基于 web的游戏。对于一个消费者而言,可以从任何机器上载入他们的存档并且会显示设置/保存,这样会更加方便。

还有其它中间件工具利用了这个东西吗?
          Epic 的虚幻引擎4(Unreal Engine 4)也可以导出到 WebGL 并且利用上 asm.js。你可以在这里找到一步一步的指南。 其过程除开首先处理 C# 的那一步——因为使用虚幻引擎编写的代码已经是 C++的了——几乎就是一样的。
          Epic 现有的演示片段有 Tappy Chicken,一种类似于 Tappy Bird 类型的游戏,在 iOS 和 Android 上也可以玩。他们在 GDC 2014 上面首先展示了运行于 Firefox 的 UE 4:
在这之前一年的 GDC 中, Mozilla 和 Epic 在他们其中的一次演讲中让所有人都吃了一惊,因为当时他们发现仅仅只在其 Epic Citadel Demo上工作一周之后 UDK (虚幻引擎 3) 就可以运行在浏览器中了。
          来自于 NomNom 游戏的《疯狂的怪兽(Monster Madness)》是第一个使用 asm.js 在 Web 上发行的商业虚幻引擎3游戏。

未来前景怎么样?
          WebGL 的应用并不只限于游戏。应用程序其实也可以很容易的利用上这项技术。
看看这个《虚拟巴黎(Unreal Paris) 1.2》的 Demo,用户在里面就可以漫步于一个精心设计的公寓。(下载链接)
          想象一下作为一名建筑师或者设计师在尝试向客户进行推销。你可以在匆忙中让它运行起来,就在浏览器里面。甚至更好,用户并不需要下载整个应用程序。
          瞧瞧像《Age of Ascent》这类东西。下载 54 MB 你就可以开始把玩这种应用程序了,因为它只下载你当时需要的数据流,并且能使用一种诸如 IndexedDB  的技术将其缓存到本地,所以并不需要你再下载一次。用户在几秒钟之内就可以启动并运行起来。在十二月的时候我采访过 Ilyriad 游戏的团队 ,更好的了解到他们是如何把技术组合到一起的。
          在另一个极端, 我们有《星际公民(Star Citizen),它有 70GB。对于许多人而言要进去这就是一个巨大的障碍,特别是在一些地方可能并没有快速的宽带。
          对于那些可能不想使用 C 或者 C++ 来编写其应用程序的开发者而言,仍然可以使用一种 WebGL  框架并且全部用 JavaScript 编写它。BabylonJS 就是这样的一个例子,它包含了一个 2D 和 3D 应用程序都支持的物理引擎,还有 handJS,它能将所有的输入类型 (点击、触摸、触控笔) 都汇集成指针时间。

还有其它的选择吗?
          当然有啦! PlayCanvas 是一个很棒的基于 WebGL 的框架,其物理上运用了 asm.js。其甚至于表现更好,因为它拥有优秀的文档和教程,就在一个基于浏览器的编辑器的顶部。


          这样的工具,你应该不需要使用某种类型的封装 (比如 Cordova 或者 PhoneGap) 来将应用导入注入 Google Play、App Store 或者  Windows Marketplace 这样的应用商店。


          ManifoldJS 的目标是通过利用 Web App 清单(支持 web 站点声明 app 风格的属性)让移动开发者的生活比以往更轻松。ManifoldJS 会针对支持它的平台使用那个标准,而对于那些不支持的就回退到 Cordova。Cordova 是不错,不过 W3C 也考虑过在 Mozilla 上就能跑得起来(Firefox 打开 Web App), Google (以 Chrome 为容器的 App) 还有 Microsoft 已经支持了(Windows 8 有本地Web 应用, Windows 10 扩展到能容纳 web 应用)。基于此,我们现在就能够包装一下网站并传出出能够被部署到不同 APP 商店中的混合应用程序了,当然仍然可以利用到每种设备上许许多多的本地原生特性(联系人、日历、文件存储、陀螺仪、 GPS,等等。)
Hosted web apps demo
Jeff Burtoft 在 http://www.thishereweb.com/hosted-web-apps-explained/ 中对托管的 web 应用进行了很好的描述。
当我们将两者结合起来的时候,我们可以创建出拥有原生运行速度的应用程序,它可以被部署到许多的 APP 商店中,并且使用的基本上就是一套代码库。对于移动开发而言没有银弹,但是这样做确实让过程更加容易了。

总结
          Unity 在它们的文档中概括了将资源导出到他们的 WebGL 播放器的过程。WebGL 和 asm.js 都支持的浏览器还在持续的增加,而 Firefox 甚至在今年较早的时候在 San Francisco 举办的 游戏开发者大会上展示了 WebGL 2.0 的特性。WebGL 2.0 带来了许多提升,包括一次渲染 32 个纹理的能力,相比之下目前的标准支持的数量是 8,此外开发者还能使用到抗锯齿和多目标渲染功能。
          将 Unity 游戏移植到 WebGL 的优势有很多:
          多渠道发布而不限于应用商店
          更小的包大小
          容易展示和共享项目
          如 Aaaaa! 案例分析以及Mozilla Humble Bundle所阐明的,开发者已经证实了该模型是有效的,所以就是我们利用浏览器终于可以提供的为我们的工作带来更多优势的好时机。
更实际的使用 JavaScript
          本文是来自于 Microsoft 技术传道者实践 JavaScript 学习的Web开发系列的一部分,涵盖了包括 Microsoft Edge 浏览器和新的 EdgeHTML 渲染引擎 有关于互操作性的最佳实践。
          我们鼓励您利用 dev.modern.IE 上的免费工具来对包括 Microsoft Edge —— Windows 10 上的默认浏览器——在内的浏览器和设备进行测试:
          检查你的站点上过期的库,布局问题以及可访问性
          使用 Mac, Linux, 和 Windows 虚拟机
          R在你自己的设备上对 Microsoft Edge 进行远程测试
          GitHub上的 Coding Lab:跨浏览器测试和最佳实践
          从我们的工程师和布道者那里深入了解 Microsoft Edge 和 Web 平台上的技术:
          Microsoft Edge Web 峰会 2015 (旨在展望新的浏览器,新的 web 平台标准的支持,以及来自 JavaScript 社区的嘉宾演讲)
          哇哦,我可以在 Mac 和 Linux 上测试 Edge & IE 了! (来自 Rey Bango)
          推进 JavaScript 而不破话 Web (来自 Christian Heilmann)
          让 Web 正常运作的 Edge 渲染引擎 (来自 Jacob Rossi)
          使用 WebGL 发挥 3D 渲染 (来自 David Catuhe ,包括 vorlon.JS 和 babylonJS 项目)
          托管 web 应用和 web 平台的创新 (来自 Kevin Hill 和 Kiril Seksenov,包括 manifold.JS 项目)
          更多免费的用于 Web 平台的跨平台工具和资源:
          面向 Linux, MacOS, and Windows 的 Visual Studio Code
          用 node.JS 编写代码 以及 Azure 上的免费试用码
          本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们
   看了上面的文章 热爱游戏创作的你是不是已经开始热血沸腾了呢?是不是迫不及待的想加入游戏团队成为里面的一员呢?
        福利来啦~赶快加入腾讯GAD交流群,人满封群!每天分享游戏开发内部干货、教学视频、福利活动、和有相同梦想的人在一起,更有腾讯游戏专家手把手教你做游戏!
腾讯GAD游戏程序交流群:484290331

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引