本文同时也发布在我的博客:http://www.livoras.com/

转载请注明出处: http://www.livoras.com/post/23

Note:这篇文章是在做一个移动端HTML5小游戏《别碰钉子》时候写的一篇游戏方案分析文档。个人认为在移动端上做HTML5游戏最困难的点在于在性能上适配不同的手机浏览器。

现在比较主流做HTML5游戏的方式是用Canvas,用例如Phaser、Cocos-js之类的框架来做。在PC端上因为硬件比较好,性能问题不大。但是在移动端这种像素级别的Canvas操作上会遇到比较大的性能瓶颈,在性能上对不同浏览器(一般是不同Webkit新老版本)进行兼容和在PC端对不同版本的IE进行兼容难度相当。

本文分析了一个游戏不同的实现方案的性能上的区别,总结出一种实现高性能移动端HTML5游戏的方案:

  1. 使用DOM + JS控制CSS3属性开启3D加速实现动画
  2. 使用time-based动画算法替代一般的frame-based动画算法

用这种方式实现的HTML5游戏,可以不同手机浏览器的性能上达到一种体验基本一致的效果。微信扫一扫可玩:

下面是这种方案的出来的分析过程。


1. 前言

《别碰钉子》这个游戏其实相当于《Flappy Bird》游戏的一个改版,该游戏特点和如《神经猫》这类游戏不一样。这个游戏其实是对物理世界有模拟,无法单纯使用CSS3来实现。为了在不同移动平台浏览器上获取到比较良好的效果,对不同的技术方案进行可行性分析和性能测试,然后选择最佳方案以获得最佳体验效果。

该分析不针对任何游戏框架,因为游戏框架底层其实也是使用HTML5、CSS3等,分析直接针对不同方案最原始实现进行优化和测试,力求找出真正的性能瓶颈进行优化。


2. 可能技术方案以及初步分析

2. 1 方案一:Pure CSS3

描述:

使用纯原生CSS3的key-frames、translate、transition高性能CSS3属性来进行游戏动画的实现。

优点:

可以轻松开启3d GPU硬件加速,直接条跳过JavaScript解释器的性能瓶颈。移动平台上最流畅的动画实现方案。

缺点:

  1. CSS3的transition动画ease类型有限,这样使得模拟物理世界多变的动画变得困难,但是可以通过这个方案进行解决。
  2. 因为CSS3动画不通过JavaScript控制,无法对动画过程进行检测和逻辑操作,这使得物理世界的碰撞检测变得不可能。直接成为这个方案最大的硬伤,不排除有方案可以来实现这种不可能,有待探索。
  3. CSS3实现动画的开发效率会比JS实现同类动画开发效率要低。

2. 2 方案二:JavaScript + Canvas

描述:

使用JavaScript和Canvas进行动画的实现,由于移动平台的限制,基本不考虑WebGL。使用requestAnimationFrame + 逐帧渲染Canvas进行动画的实现。基本上游戏框架内部就采取这种实现,所以这里不讨论游戏框架的使用,直接讨论底层性能优化。

优点:

  1. 所有动画都是JS控制,可以对动画进行最精细的调控。
  2. 基本上可以做到任意能够想到的效果。

缺点:

  1. 因为Canvas是像素级的渲染,每次动画的实现其实是对画布的“擦除 + 重新渲染”。对于移动平台来说会有很大的计算压力,可能造成较大的性能瓶颈。
  2. 无法启动3d底层GPU加速,不能压榨渲染性能。

2. 3 方案三:JavaScript + CSS3

描述:

不使用Canvas进行游戏元素的渲染,而是使用DOM进行元素的实现。动画的实现采用这么一种方式:

  1. 用CSS3开启3D加速,使用translate进行元素位置的控制。
  2. 使用JS控制CSS3的translate属性进行动画的实现。

优点:

  1. 既能使用JavaScript对动画进行精细控制,又能使用3D硬件加速

缺点:

  1. 无法像Canvas那样做出非常绚丽的效果。
  2. 因为JS控制CSS3的方式实际操作的是DOM,一点元素多起来了,就会可能遇到一个非常大的性能瓶颈:DOM天生就非常慢。
  3. 游戏复杂程度高了,这种方案开发效率会比较低。

综上,第一种方案虽然效果最好,因为遇到较大的实现困难,直接排除;方案2和方案3值得探索,对它们进行一个性能上的测试,挑选最佳方案进行游戏的实现。


3. 技术方案性能分析

3. 1 性能相关环境因素

在分析之前先排除一个误区:移动端HTML5游戏和手机操作系统的关系。经过一系列在不同手机上同一浏览器的测试,同一手机上不同浏览器的测试。得出的现象是:在低级操作系统(2.3)下用好的浏览器也可以达到比较好的FPS,而在高级操作系统(4.4)中用低级浏览器可以获取到一个比较低FPS,如图:

 Android 2.3, FPS:58

 Android 4. 4, FPS:9

而同一个手机不通的浏览器也会有不一样的性能,小米2上用webkit build 为573.36的版本浏览器和534.30的版本浏览器居然是天壤之别:

 小米2 webkit 573.36, FPS: 10

 小米2 webkit 534.30, FPS: 62

而经过观察,在不同手机上的webkit的build版本相同的情况下,获取到的性能参数FPS是在同一个级别上的。在小米二和华webkit build的版本都为533.1的情况下,FPS基本都在10左右:

 小米2, FPS:10

 华为, FPS: 9

而在同一个build的Chrome上可以获取良好的50~60的FPS。

也就是说,在这个性能测例中,获取到结论是:一个游戏的执行效率和操作系统没有太大关系,而是和浏览器有关系。

注意:

  1. 该结论是不严谨的,仅代表在该测例情况下,并且由于没有足够多的垃圾测试手机,所以这只是一个推论。
  2. 这里简单地用webkit build版本来区分浏览器只是一种快捷方式,实际上,不能用webkit build版本来区分浏览器。同一个build的浏览器性能表现也可能有较大差异。

接下来的测试基于这个推论,在同一个测例性能表现较大的不同浏览器上,对表现较差的进行优化,减少性能表现差异。

3.2 方案性能测试和优化

一个方案的优秀与否参考两个标准:

  1. 页面的FPS
  2. 主观体验上动画流畅程度(10为满分,可参考PC端Chrome,主观判断)

针对方案2和3,做这么一个测试,做一个小人在页面上移动,遇到边界马上反弹,观察不同浏览器版本的FPS和体验的流畅程度。

3.2.1 方案二的测试和优化

用方案二(JavaScript + Canvas)方式编写了测试:http://livoras.github.io/blog/ca-move/ ,扫一扫可见效果:

在webkit build为537.36(下为浏览器一)的一个浏览器上获取到性能数据:

 FPS: 10 流畅程度:3

在webkit build为534.30(下为浏览器二)的一个浏览器上获取到性能数据:

 FPS:50~62 流畅程度:4

让人惊讶的是,两个浏览器在FPS表现较大,但是流畅度感受上差不多(比较卡)。而且,最大的差异在于,浏览器一(FPS较低)的小人的移动动作比浏览器二(FPS较大)的移动速度要慢得多。

经过研究发现,我们一般的动画算法是frame-based算法,也就是说我们每帧动画让小人移动的位移是一样的。假设我们一帧动画移动5px,在FPS为10的浏览器中一秒钟就移动 5 x 10 = 50px。而在FPS为60的浏览器中一秒钟就移动5 x 60 = 300px。

这就是为什么一些浏览器感觉特别卡(移动慢)的原因,因为我们浏览器的FPS不一样,我们的算法是frame-based的!而大多数框架的算法也是frame-based算法,例如Phaser就是这样。那怎么做到不同浏览器都可以做到统一的速度上的体验呢?把动画改成time-based算法即可。

time-based算法就是,小人的移动速度和FPS没有关系,而是直接和时间挂钩。以前我们是px/FPS,现在改为px/sec。也就是说在每一帧动画中基于过去的时间来计算移动的位置。详情可参考:

  • http://viget.com/extend/time-based-animation
  • http://blog.sklambert.com/using-time-based-animation-implement/
  • http://codetheory.in/time-based-animations-in-html5-games-why-and-how-to-implement-them/

对上面的测例进行了算法上的优化,包括:

  1. Canvas清除不是整块画布,而是清除小人局部的画布。
  2. 使用time-based算法来实现动画。

修改完以后的测例:http://livoras.github.io/blog/ca-time-r/ ,扫一扫可见效果: 

在浏览器一和浏览器二上效果卓然,两个浏览器达到统一的体验速度,也就是小人移动的速度是一样的。但是的动画体验上还是感觉到卡(体验5分左右),而我们在这个测例已经没有可以优化的地方(小人是直接使用drawImage,没有必要Pre-render优化)。

这就是我们在这个方案上能做到的优化。

3.2.2 方案三的测试

我们直接套用方案二的time-based Animation算法,用JavaScript控制CSS3的translate属性来进行动画的实现方案三,启动了3d加速(transform:tranlate3d),测例:http://livoras.github.io/blog/ca-css3-time-r/ ,扫一扫:

在webkit build为537.36(浏览器一)的一个浏览器上获取到性能数据: FPS:60+ 流畅程度:6

在webkit build为534.30(浏览器二)的一个浏览器上获取到性能数据: FPS:60+ 流畅程度:5

出现了让人惊讶的经过,原来只有FPS 10的浏览器一飙升到了60,而且流畅程度完爆了canvas的实现。而浏览器二保持原来的流畅程度和FPS。

3.2.3 测试结果

在条件相同的情况下,经过不同的手机上不同的浏览器测试后,方案三的体验流畅程度都比方案二的好,并且通过time-based animation算法,即使不同方案的实现都可以达到同一个速度(不是流畅程度)上的体验。

3.2.4 已知问题
  1. time-based算法在MX上会出现浏览器假死的状况。

4. 方案选择

根据测试,衡量各方面因素,最终采取的技术实现方案定为:

使用time-based animation算法,用JavaScript控制CSS3的translate属性开启3d硬件加速来实现游戏动画方案。


参考资料:

  • http://www.slideshare.net/iiwork/high-performance-mobile-web-game-development-in-html5
  • https://blog.safaribooksonline.com/2012/06/29/faster-mobile-gaming-with-css3/
  • http://www.html5rocks.com/en/tutorials/canvas/performance/#disqus_thread
  • http://jsperf.com/render-vs-prerender
  • http://simonsarris.com/blog/427-increasing-performance-by-caching-paths-on-canvas
  • http://kristerkari.github.io/adventures-in-webkit-land/blog/2013/03/08/dealing-with-svg-images-in-mobile-browsers/
  • http://buildnewgames.com/dom-sprites/
  • https://github.com/davidshimjs/collie-benchmark/wiki/Performance-Benchmark
  • http://blogs.adobe.com/webplatform/2014/03/18/css-animations-and-transitions-performance/
  • http://viget.com/extend/time-based-animation
  • http://blog.sklambert.com/using-time-based-animation-implement/
  • http://codetheory.in/time-based-animations-in-html5-games-why-and-how-to-implement-them/
  • http://www.joelambert.co.uk/morf/
  • http://jeremyckahn.github.io/blog/2013/07/28/60-fps-or-bust-dynamically-prerendering-css-animations/