在拥有 16GB 内存的四核处理器上进行开发时,很容易忘记考虑它在移动设备上的性能。本文将详细介绍将游戏移植到 Firefox OS 或类似硬件目标的一些最佳实践和注意事项。
充分利用 256 Mb RAM/800 Mhz CPU
在开发游戏时,有很多方面需要考虑。当您的目标是每秒绘制 60 次时,垃圾收集和低效的绘制调用会开始阻碍您。让我们从基础开始……
不要过度优化
这在关于游戏优化的文章中听起来可能违反直觉,但优化是最后一步;对完整、可工作的代码进行优化。虽然记住这些提示和技巧永远不会错,但您在完成游戏并在设备上玩过之后才知道是否需要它们。
优化绘制
在 HTML5 2D 画布 上绘制是大多数 JavaScript 游戏中最大的瓶颈,因为所有其他更新通常只是代数运算,不会触及 DOM。画布操作是硬件加速的,这可以为您提供一些额外的喘息空间。
使用全像素渲染
当您在画布上渲染对象时,使用非整数的值会导致子像素渲染。
ctx.drawImage(myImage, 0.3, 0.5)
这会导致浏览器进行额外的计算来创建抗锯齿效果。为了避免这种情况,请确保在调用 drawImage
时使用 Math.floor
对所有坐标进行四舍五入,或者如您将在本文中进一步阅读,使用 按位
运算符。
在离屏画布中缓存绘制
如果您发现自己每一帧都有复杂的绘制操作,请考虑创建一个离屏画布,在离屏画布上绘制一次(或在每次更改时绘制),然后在每一帧上绘制离屏画布。
myEntity.offscreenCanvas = document.createElement(“canvas”);
myEntity.offscreenCanvas.width = myEntity.width;
myEntity.offscreenCanvas.height = myEntity.height;
myEntity.offscreenContext = myEntity.offscreenCanvas.getContext(“2d”);
myEntity.render(myEntity.offscreenContext);
在画布标签上使用 moz-opaque(仅限 Firefox)
如果您的游戏使用画布并且不需要透明,请在画布标签上设置 moz-opaque
属性。此信息可以在内部使用来优化渲染。
在 Bug 430906 – 在画布上添加 moz-opaque 属性 中描述了更多信息。
使用 CSS3 变换缩放画布
CSS3 变换通过使用 GPU 更快。最佳情况是不缩放画布,或者使用更小的画布并向上缩放,而不是更大的画布并向下缩放。对于 Firefox OS,目标尺寸为 480 x 320 像素。
var scaleX = canvas.width / window.innerWidth;
var scaleY = canvas.height / window.innerHeight;
var scaleToFit = Math.min(scaleX, scaleY);
var scaleToCover = Math.max(scaleX, scaleY);
stage.style.transformOrigin = "0 0"; //scale from top left
stage.style.transform = "scale(" + scaleToFit + ")";
在 这个 jsFiddle 中查看其工作原理。
缩放像素艺术的最近邻渲染
从上一条开始,如果您的游戏主题是像素艺术,您应该在缩放画布时使用以下技术之一。默认的调整大小算法会产生模糊效果,破坏了美丽的像素。
canvas {
image-rendering: crisp-edges;
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-optimize-contrast;
-ms-interpolation-mode: nearest-neighbor;
}
或者
var context = canvas.getContext(‘2d’);
context.webkitImageSmoothingEnabled = false;
context.mozImageSmoothingEnabled = false;
context.imageSmoothingEnabled = false;
有关更多文档,请参阅 MDN 的 image-rendering。
用于大型背景图像的 CSS
与大多数游戏一样,如果您有一个静态背景图像,请使用带有 CSS 背景属性的普通 DIV 元素,并将它放置在画布下方。这将避免在每次滴答时将大型图像绘制到画布上。
用于图层的多个画布
与上一条类似,您可能会发现有些元素经常改变和移动,而其他元素(例如 UI)则从未改变。在这种情况下,一种优化方法是使用多个画布元素创建图层。
例如,您可以创建一个位于所有内容顶部的 UI 图层,并且只在用户输入时进行绘制。您可以创建一个游戏图层,其中包含经常更新的实体,以及一个用于很少更新的实体的背景图层。
不要在 drawImage 中缩放图像
在加载时将图像的各种尺寸缓存到离屏画布中,而不是在 drawImage 中不断缩放它们。
jsPerf – Canvas drawImage 缩放性能.
小心使用重量级的物理库
如果可能,自己编写物理引擎,因为像 Box2D 这样的库在低端 Firefox OS 设备上表现不佳。
当 asm.js 支持登陆 Firefox OS 时,Emscripten 编译的库可以利用接近本机的性能。在 Box2d Revisited 中阅读更多内容。
使用 WebGL 而不是 Context 2D
说起来容易做起来难,但将所有繁重的图形处理工作交给 GPU 将释放 CPU 的能力,使其发挥更大的作用。即使 WebGL 是 3D 的,您也可以使用它来绘制 2D 表面。有一些库旨在抽象绘制上下文。
最小化垃圾收集
JavaScript 在内存管理方面可以宠坏我们。我们通常不需要担心内存泄漏或保守地分配内存。但如果我们分配了太多内存,并且垃圾收集发生在帧中间,这会占用宝贵的时间,并导致 FPS 明显下降。
池化常见对象和类
为了最大程度地减少在垃圾收集期间被清理的对象数量,请使用一个预先初始化的对象池,并重用它们,而不是一直创建新对象。
通用对象池的代码示例
避免内部方法创建垃圾
有各种 JavaScript 方法创建新对象,而不是修改现有对象。这包括:Array.slice
、Array.splice
、Function.bind
。
阅读有关 JavaScript 垃圾收集的更多信息
避免频繁调用 localStorage
LocalStorage 使用文件 I/O 并阻塞主线程来检索和保存数据。使用内存中的对象来缓存 localStorage 的值,甚至可以将写入操作保存到玩家不处于游戏中间时。
抽象存储对象的代码示例
使用 IndexedDB 的异步 localStorage API
IndexedDB 是一种用于在客户端存储数据的非阻塞 API,但对于小而简单的数据来说可能过大了。Gaia 的库可以使 localStorage API 异步,使用 IndexedDB 可在 Github 上获得:async_storage.js。
其他微优化
有时,当您用尽了所有选项,它仍然无法更快时,您可以尝试以下一些微优化。但是请注意,这些优化只有在重度使用时才会产生效果,每毫秒都很重要。在您的热游戏循环中寻找它们。
- 使用
x | 0
而不是Math.floor
- 使用
.length = 0
清空数组,避免创建新数组 - 牺牲一些 CPU 时间来避免创建垃圾。
- 使用
if .. else
而不是switch
- jsPerf – switch vs if-else
- Date.now() 而不是 (+ new Date)
- jsPerf – Date.now vs new Date().getTime() vs +new Date
- 或 performance.now() 用于毫秒级以下的解决方案
- 对于浮点数或整数(例如向量和矩阵)使用 TypedArrays
- gl-matrix – 用于高性能 WebGL 应用程序的 Javascript 矩阵和向量库
结论
为移动设备和性能不高的硬件构建是一个很好的创造性练习,我们希望您能确保您的游戏在所有平台上都能良好运行!
关于 Louis Stowasser
我是 Mozilla 的合作伙伴工程师,Gamedev Weekly 的维护者,以及位于澳大利亚布里斯班的 CraftyJS 游戏引擎的创建者。
关于 Harald Kirschner (digitarald)
Harald "digitarald" Kirschner 是 Firefox 开发人员体验和工具的产品经理——致力于赋予创作者权力,让他们编写代码、设计和维护一个对所有人开放且可访问的网络。在他在 Mozilla 的 8 年时间里,他在性能、Web API、移动设备、可安装 Web 应用程序、数据可视化和开发人员推广项目中不断提升自己的技能。
更多 Harald Kirschner (digitarald) 的文章……
关于 Robert Nyman [荣誉编辑]
技术布道师和 Mozilla Hacks 编辑。进行 HTML5、JavaScript 和开放网络方面的演讲和博客。Robert 是 HTML5 和开放网络的坚定支持者,自 1999 年起,一直在瑞典和纽约市从事 Web 前端开发工作。他还定期在 http://robertnyman.com 上发表博客,喜欢旅行和结识新朋友。
12 条评论