正在创建 Roll It

Justin Gitlin
Justin Gitlin

Roll It 是一项 Chrome 实验性项目,通过在手机和计算机上使用浏览器,您可以重新构想一款经典的木板路游戏。借助手机上的浏览器,你只需轻击手腕即可瞄准并掷球,计算机上的浏览器则使用 WebGL 和画布呈现 Roll It Alley 的实时图片。两台设备通过 Websocket 通信。无应用。无下载内容。无令牌。您只需要一款新型浏览器。

在 Google 创意实验室的指导下,Legwork 完成了用户体验、界面和游戏环境的开发工作,然后与开发合作伙伴 Mode Set 联手打造了 Roll It。在项目开展期间,我们遇到了许多独特的挑战。本文将介绍我们在 Roll It 实践的过程中用到的一些技术、发现的技巧和经验教训。

3D 工作流程

最初的困扰之一是,找出最佳方式,将我们的软件中的 3D 模型转换成网络文件格式。在 Cinema 4D 内制作素材资源后,我们简化这些模型并将其转换为低多边形网格。每个网格都被赋予了特定的多边形选择标记,用于区分对象的不同部分,以便着色和进行纹理化处理。然后,我们能够导出为 Collada 1.5 (.dae) 文件,并导入到开源 3D 程序 Blender 中,制作与 3.js 兼容的文件。确保模型正确导入后,我们将网格导出为 JSON 文件,然后使用代码应用光照。下面更详细地介绍了我们采取的措施:

在 C4D 内为对象建模。确保网格法线朝外。
在 C4D 内为对象建模。确保网格法线朝外。
使用多边形选择工具,为您要进行纹理处理的特定区域创建选择标记。将材质应用到每个选择标记。
使用多边形选择工具,针对您要添加纹理的特定区域创建选择标记。将材质应用到每个选择标记。
将网格导出为 COLLADA 1.5 .dae 文件。
将网格导出为 COLLADA 1.5 .dae 文件。
务必选中“导出 2D 几何图形”。在代码端,通常可以在 3D 环境中更广泛地支持导出三角形,但其缺点是会使多边形数量翻倍。多边形数量越多,对计算机处理器上的模型负担越大。因此,如果您发现运行缓慢,请将此复选框保持为选中状态。
确保选中“导出 2D 几何图形”。在代码端,通常可以在 3D 环境中更广泛地支持导出三角形,但其缺点是会使多边形数量翻倍。多边形数量越多,对计算机处理器上的模型负担越大。因此,如果您发现性能不佳,请勾选此复选框。
将 Collada 文件导入 Blender。
将 Collada 文件导入 Blender。
导入到搅拌机后,您会看到材质和选择标签也沿用了下来。
导入到搅拌机后,您会看到您的材质和选择标签也沿用了下来。
选择所需物体,然后根据需要调整其材质。
选择您的物体,然后根据需要调整其材质。
将文件导出为 Three.js 文件
将文件导出为 three.js 文件,以实现 webGL 兼容性。

编写代码

Roll It 使用开源库开发,可在现代浏览器中以原生方式运行。随着 WebGL 和 WebSockets 等技术的推出,网络正在逐渐接近主机游戏和多媒体的体验。随着 HTML 开发的更多新工具不断出现,开发者构建这些体验的便捷性和舒适度也实现了飞跃式发展。

开发环境

Roll It 的原始代码大部分是使用 CoffeeScript 编写的,这种语言简明扼要,可转编译为格式正确且执行 lint 检查的 JavaScript 代码。CoffeeScript 凭借出色的继承模型和更简洁的作用域处理机制,在 OOP 开发方面大放异彩。CSS 是使用 SASS 框架编写的,可为开发者提供大量强大的工具,用于增强和管理项目的样式表。在构建流程中添加这些系统需要花费一些时间进行设置,但回报绝对是值得的,对于像 Roll It 这样的大型项目来说更是如此。我们设置了 Ruby on Rails 服务器,以在开发过程中自动编译资源,因此所有这些编译步骤都变得透明。

除了打造精简舒适的编码环境外,我们还手动优化了资源,尽可能减少请求数量,加快网站的加载速度。我们使用了一些压缩程序(ImageOptimImageAlpha)运行每张图片。每个程序均以自己的方式优化图像:分别是无损和有损。通过合理组合设置,它们可以显著缩减图片的文件大小。这不仅能节省加载外部图片时的带宽,而且经过优化后,图片会转换成更小的 base64 编码字符串,用于在 HTML、CSS 和 JavaScript 中内联嵌入。虽然在进行 base64 编码时,我们还使用此技术将 Open Sans WOFF 和 SVG 字体文件直接嵌入到 CSS 中,从而减少了总请求次数。

基于物理特性的 3D 场景

THREE.js 是一个应用广泛的 3D JavaScript 库,适用于 Web。它封装了底层 3D 数学运算和基于硬件的 WebGL 优化,让普通用户可以轻松创建光线充足、精美的交互式 3D 场景,而无需编写自定义着色器或执行手动矩阵转换。Physijs 是热门 C++ 物理库的 THREE.js 专用封装容器,已转换为 JavaScript。我们利用以下库来以 3D 形式模拟球向目的地滚动、跳跃和弹跳。

从一开始,我们便致力于营造真实的滚球体验,同时确保游戏中的对象给人以真实的感觉。这需要通过多次迭代来调整 Physijs 场景的整体重力、球从球员投掷时的速度、球道跳跃的斜率以及球和球道材料的摩擦和恢复(弹力)属性。更高的重力和更大速度的结合创造了更真实的游戏体验。

进行顺滑处理

大多数新型浏览器与显卡组合都应利用 WebGL 环境中基于硬件的原生抗锯齿功能,但有些组合效果欠佳。如果抗锯齿功能本身不起作用,那么 THREE.js 场景中任何硬质和对比边的边缘都将呈锯齿状且难以处理(至少对我们敏锐的眼睛来说)。

幸运的是,我们通过一小段代码检测出该平台是否会原生支持抗锯齿功能。如果已正确运行,我们就可以开始了。如果没有,那么 THREE.js 中附带的一系列后处理着色器可以帮助我们。即 FXAA 抗混叠滤波器。通过使用该着色器每一帧重新绘制渲染的场景,我们通常会拥有更平滑的线条和边缘外观。请参见下面的演示:

// Check for native platform antialias support via the THREE renderer
// from: http://codeflow.org/entries/2013/feb/22/how-to-write-portable-webgl/#antialiasing
var nativeAntialiasSupport = (renderer.context.getParameter(renderer.context.SAMPLES) == 0) ? false : true;

基于加速度计的游戏控件

Roll It 的魔法大多来自玩家使用手机执行的滚球手势。移动设备已经能够使用浏览器内的加速计已有一段时间了,但作为一个行业,我们才刚刚开始探索网络上的基于动作的手势识别。我们受到手机加速度计提供的数据的限制,但只要稍微发挥想象力,就能打造出一些出色的新体验。

检测 Roll It 的主要“滚动”手势首先会跟踪来自窗口的 deviceorientation 事件的 10 个最新加速度计更新。用当前倾斜值减去之前的倾斜度值,即可存储事件之间的角度增量。然后,通过不断地对最后十个角度增量求和,我们可以在手机在太空中移动时检测连续旋转。当手机超过扫视角度变化的阈值时,就会触发翻滚。然后,通过找到该扫描中最大的单个倾斜度增量,我们可以估算球的速度。在 Roll It 中,此速度使用我们附加到每次加速度计更新的时间戳进行标准化。这有助于消除加速度计更新在不同设备上的变化速度。

WebSocket 通信

在玩家用手机掷球后,手机会向笔记本电脑发送一条消息,提示玩家将球发射。这条“roll”消息通过两台机器之间的 WebSocket 连接,通过 JSON 数据对象发送。JSON 数据很小,主要由消息类型、投掷速度和目标方向组成。

{
  "type": "device:ball-thrown",
  "speed": 0.5,
  "aim": 0.1
}

笔记本电脑和手机之间的所有通信都是通过如下所示的小型 JSON 消息进行的。每当游戏在桌面设备上更新状态,或者用户倾斜或点按手机上的按钮时,都会在机器之间传输 WebSocket 消息。为了使这种通信简单、易于管理,从任一浏览器使用单个退出点来广播 WebSocket 消息。相反,接收浏览器上则只有一个入口点,由一个 WebSocket 对象处理两端的所有传入和传出消息。收到 WebSocket 消息后,系统会使用 jQuery 的 trigger() 方法在 JavaScript 应用中重新广播 JSON 数据。此时,传入数据的行为与任何其他自定义 DOM 事件类似,并且可由应用中的任何其他对象获取和处理。

var websocket = new WebSocket(serverIPAddress);

// rebroadcast incoming WebSocket messages with a global event via jQuery
websocket.onmessage = function(e) {
  if (e.data) {
    var obj = JSON.parse(e.data);
    $(document).trigger(data.type, obj);
  }
};

// broadcast outgoing WebSocket messages by passing in a native .js object
var broadcast = function(obj) {
  websocket.send(JSON.stringify(obj));
};

Roll It 的 WebSocket 服务器是在两台设备通过游戏代码同步时即时创建的。Roll It 的后端是使用 GoGoogle Compute EngineApp Engine 平台上构建的。

倾斜菜单屏幕

除了游戏过程中使用的由事件驱动的 WebSocket 消息外,Roll It 中的菜单也可以通过倾斜手机和点按按钮来确认选择来控制。这就需要从手机传输到笔记本电脑的倾斜数据传输流更加一致。为了减少带宽用量并避免发送不必要的更新,这些消息仅在设备的倾斜度超过几个度时发送。如果手机平放在桌子上,那么发送倾斜数据流没有意义!传输速率也会受到限制,即使设备正在倾斜,在 Roll It 中每秒发送的 WebSocket 消息不超过 15 条。

在计算机上获取倾斜值后,系统会使用 requestAnimationFrame 对这些值进行内插计算,以保持流畅的感觉。最终会生成一个旋转菜单和一个滚动球,以帮助指示用户的选择。当手机发送倾斜数据时,系统会通过重新计算 requestAnimationFrame 循环内的 CSS transform 属性来实时更新这些 DOM 元素。菜单容器只是旋转了,但球好像在地板上滚动。为了实现这种效果,我们实现了一些基本的三角学,以将球的 x 坐标与其旋转相关联。简单等式为:旋转 = x / (直径 * π)

小结

掷骰子算是时代的标志。有为其开发提供支持的开源项目、我们办公桌上和我们口袋里设备的处理能力,以及网络作为平台的情况,这是一个真正激动人心的变革性时刻,能够在开放网络上互联互通。就在几年前,这项技术大部分只存在于专有系统中,无法随意使用和分发。如今,我们每天都在制作和分享新的拼图,而我们可以用更少的工作和更多的想象力来实现复杂的体验。还在等什么?打造卓越应用,与全世界分享!

Roll it 徽标