一、 引言
在本文中,我们将使用WebGL技术来开发一款移动游戏,本游戏能够以60FPS的帧频运行于移动设备上,此游戏的风格类似于Flappy Bird和神庙大逃亡(Temple Run),起名叫Winter Rush。本文着重讨论和游戏开发过程中所使用的WebGL相关的three.js技术。
下图展示了此游戏开始时的一个快照。
游戏过程中,玩家可以使用左向与右向箭头键(或者屏幕上左右点按)来左右移动。
二、 为什么60 FPS很重要
帧速率越高,游戏内容渲染效果将是越光滑。对于一款游戏产品来说,运动效果的光滑度和控制的及时响应是尤为重要的。电脑屏幕通常刷新的频率是60hz,所以这是我们的***的约束。请注意,60帧/秒只是最理想的目标,但实际上任何超过30 FPS的帧速率都有看上去还不错的效果。
Paul Lewis对这一方面展开了深入探讨。为此,他开设了一个专门的网站进行有关技术的探讨,网站地址是http://jankfree.org/。
我可以非常自豪地告诉你,本文提供的示例工程能够以60FPS的帧速率工作于/iPad 4th Gent和Nexus 4手机上。
为了实现我想要的FPS目标,我采用了如下方案。
三、 简化3D场景
几何体:通过减少网格数目和每个网格的顶点数来简化场景中的几何体。切记:“低聚合”永远是很酷的!在这个游戏中,实现树效果中实际上仅使用了两个圆柱体︰一个用于实现树叶,另一个用于实现树干。在游戏中,当在赛道上运动时,实际上仅在赛道上放置了10棵树。
材质:3D引擎计算成本的很大一部分是花费在计算场景中每个几何面的照明效果上。在场景中灯光越少,则计算成本越低。Three.js引擎中材质运算代价按照从低到高的顺序依次分为:
(1)Basic材质:这是***的材质,不需要计算照明,你可以使用Basic材质和图像纹理做很多的事情。
(2)Lambert材质:使用这种材质能够呈现并非那么富有光泽性的外观效果。
(3)Phong材质:使用这种材质能够实现极富光泽性的外观效果。在我的测试中,Phong材质被证明比Lambert材质昂贵得多。例如,在本文提供的实例中,如果从Lambert材质切换到Phong材质,在iOS平台上FPS值会从60降到15。
四、 对象重用
这可能是为实现高性能web体验应遵循的最重要的规则了。在初始化阶段对象创建后,在游戏运行期间尽量不要创建新对象。这将避免内存“颠簸”——其可能会导致浏览器“窒息”。网站http://www.html5rocks.com/en/tutorials/speed/static-mem-pools/
处提供了一篇使用JS对象池方面的极好的文章,你可以参考一下。在本文游戏中,我重用3D对象(例如树)的方法是:当它们位于摄影机后面时重置它们的位置。在每一帧中,都会检查对象是否位于摄像机之后。如果对象位于摄像机之后,则把其位置重置于远离赛道的地方。另外需要说明的是,在本游戏中,我使用了THREE.Fog技术实现遮挡树目的。当然另一方面,因为这也是一种相当流行的解决方案。
五、 移动赛道
游戏中赛道的雪地板是一个平面网格。我们使用Perlin噪声算法生成地形高度(例如顶点的Y-坐标)。这种办法能够实现随机但平滑变化的坑洼效果。为了实现自然运动的赛道外观效果,我们使用了以下技术︰
(1)在每帧中,我们向照相机移动整个地板一小段距离,至于移动速度则基于玩家的操作速度。
(2)程序中,我们会检查是否地板已经移动到摄影机后面并超过一个预定义的STRIP_WIDTH量。如果是这样,我们会沿赛道向后重置地板STRIP_WIDTH大小的位置。然后,我们重新计算地形高度——这是通过把Perlin噪声位置增加到STRIP_WIDTH值大小实现的。
在游戏中,大致效果如下面的截图所示。
六、 简化碰撞检测
在Three.js编程中,你完全可以通过使用Raycasters方法实现非常精确的针对于每一个面的碰撞检测。有关这一方法,Lee Stemkoski就提供了一个不错的例子(http://stemkoski.github.io/Three.js/Collision-Detection.html)。然而,这种方法可能代价昂贵,需要针对每一对有可能发生碰撞的对象进行测算。在许多情况下,你完全可以简化碰撞检测;办法是,假设每个对象都是一个球体,那么,只需要简单地测量这些对象之间的距离即可。
请注意,您可能需要手动调整碰撞距离和碰撞盒位置,从而实现更可玩性的感觉。但是,也有一个问题,就是当扫射的时候玩家可能击中了关闭相机的对象。针对这一问题的解决办法是,把玩家射击盒从镜头前移出一点距离。
七、 实现阴影效果
在Three.js编程中,EffectComposer类允许你依次执行多个后处理着色器(post-processing shader)。这种方法需要使用多个脱屏缓冲(off-screen buffer),从而把上一个着色器结果传递到下一个。在手机设备上,这种方法给出并不理想的性能表现。解决方法是:把我们编写的多个着色器合并成一个超级着色器(SuperShader)。当然,实现这一点仅需要把每一个着色器代码复制并粘贴即可,但还要考虑适当的顺序。对于我们的Winter Rust游戏来说,我们把Vignette,Brightness/Contrast和Hue/Saturation着色器结合为一个大的着色器。此外,需注意,有些效果对于移动设备来说GPU消耗太大,最引人注目的是模糊相关技术。
八、 时间控制
在动画循环方面,我们应使用requestAnimationFrame方法并为动画应用时间偏移量。这样可以使动画速度独立于游戏帧速率。旅行距离应取决于已经过去实际的时间而不是帧数量。这种技术不会提高你的游戏的FPS值,但如果FPS值实在太低时会提高你的游戏玩家的速度知觉。请参考如下有关代码:
- //kick off animation
- var clock = new THREE.Clock();
- clock.start();
- gameLoop();
- function gameLoop(){
- requestAnimationFrame(gameLoop );
- var delta = clock.getDelta();
- //use delta to determine all distances travelled
- movePlayer(MOVE_SPEED * delta);
- }
九、 运行于目标设备
一旦你确定了目标设备,那么你会不断地在这些设备上进行测试并不断关注FPS值。OS X中的IOS模拟器是一个在桌面开发时调试iOS问题的优秀工具;但要注意,模拟器并不反映实际设备的性能。Adobe的Edge Inspect(https://creative.adobe.com/products/inspect)是另一个优秀的工具,它使你能够轻松地将多个移动设备连接到本地网页。当页面更改时它将自动重新加载该页面,并且还允许您访问Android控制台错误。
十、 移动开发的JS库
本小节给出开发我的游戏时使用的几个著名的js库,它们是:
- Three.js:这个不用多说;
- Zepto.js:这是一个jQuery的***替代品,共25K大小,而且在移动设备上速度很快;
- Howler.js:这是一个优秀的声效库,它能够应付多种移动设备的声音问题;
- TweenLite:使用该库可以很容易地实现缓动效果,在移动设备上效率不错。
十一、 哪些设备可以运行WebGL
支持WebGL的设备数量正在快速增长。除了在所有主流的桌面浏览器上运行外,现在WebGL内容运行于iOS和Android设备上没有大的问题。
然而,不是所有的支持WebGL功能的设备都是生而平等的。WebGL是一种要求苛刻的技术,因此在一些相对较旧的设备上运行时建议主要运行一些最基本的内容。例如,iPad 2在2011年推出,它能够运行WebGL,但能力非常有限。WebGL在近两年制造的移动设备上通常运行很好。我主要的移动测试设备是一台第4代iPad(2013年生产)和一台Nexus 4(2012年生产),这就是一个相对很好的参考基准。
十二、 待改进的地方
建议有兴趣的各位朋友,在学习本文提供实例项目过程中可以尝试向此项目添加以下项︰
- 移动设备上的倾斜控制。我选择使用点按方式来实现在手机上的倾斜移动,因为这样可以更匹配桌面体验。请注意:使用倾斜加速度计可是一种完全不同的控制系统,读者可另外尝试一下这种方案。
- 开发发烧友类型的桌面版。因为我想尽量使这个游戏在较慢的设备上也能运行良好,所以我不得不放弃使用一些漂亮的效果和几何体。因此,在构建桌面版本时使用更丰富的图形,效果应当更好。
- 使用Android全屏API
- 把程序中使用的HTML菜单技术浮动到WebGL场景上面,也许还可以添加一些漂亮的着色器摆动过渡效果,等等。
十三、 小结
本文简述了使用Three.js开发一款移动设备3D游戏的要点。***,真诚希望读者能够开发出在移动设备上具备良好性能的WegGL游戏产品。
附:本文中实例游戏Winter Rush的下载地址是https://github.com/felixturner/winter-rush。
【51CTO.com独家译稿,合作站点转载请注明来源】