我正在制作一个游戏,其中健康栏(动画)和其他一些信息在视觉上表示,比如一些图标显示玩家拥有的炸弹数量等。现在,这可以在画布中完成(通过制作另一个画布来获取信息它位于主画布上,或者可以使用许多 div 和具有绝对定位的跨度来完成。这是我第一次制作基于浏览器的游戏,所以如果有经验的人看到这个,告诉我你的推荐。我想知道哪种方法会更快。
该游戏还将在移动设备上运行。谢谢!
我正在制作一个游戏,其中健康栏(动画)和其他一些信息在视觉上表示,比如一些图标显示玩家拥有的炸弹数量等。现在,这可以在画布中完成(通过制作另一个画布来获取信息它位于主画布上,或者可以使用许多 div 和具有绝对定位的跨度来完成。这是我第一次制作基于浏览器的游戏,所以如果有经验的人看到这个,告诉我你的推荐。我想知道哪种方法会更快。
该游戏还将在移动设备上运行。谢谢!
没有直截了当的答案,我建议您使用不同的浏览器进行 FPS 测试,它如何在您的用例中发挥作用。如果您不希望如此深入,我建议您只需在画布内绘制元素,如果您需要隐藏它们,则从渲染循环中省略 drawHUD() 调用。
对于 HTML HUD 覆盖<canvas>
应考虑以下因素
<canvas>
如果画布上有 DOM 元素,Web 浏览器合成器能否正确进行硬件加速
<canvas>
由于处理 DOM 元素的继承复杂性,HTML / DOM 操作总是比操作慢
<canvas>
像素空间保持在内部,如果您尝试在画布本身外部<canvas>
绘制元素,可能很难实现像素完美对齐<canvas>
HTML 为文本提供了比画布 drawString() 更多的格式化选项 - 是否需要 HTML 格式化
使用画布。如果需要,可以使用两张画布,一张覆盖在另一张上,但使用画布。
完全触摸 DOM 很慢。使文档重做布局,因为移动的 DOM 元素的大小非常慢。处理取消(或不取消)更多事件,因为在画布顶部有物理上的 DOM 项目可能会很痛苦,为什么还要麻烦处理呢?
如果您的 HUD 不经常更新,那么最快的做法是在它发生变化时将其绘制到内存中的画布上,然后在更新框架时始终将该画布绘制到主画布上。这样,您的 drawHud 方法将如下所示:
function drawHUD() {
// This is what gets called every frame
// one call to drawImage = simple and fast
ctx.drawImage(inMemoryCanvas, 0, 0);
}
当然更新HUD信息就像:
function updateHUD() {
// This is only called if information in the HUD changes
inMemCtx.clearRect(0, 0, width, height);
inMemCtx.fillRect(blah);
inMemCtx.drawImage(SomeHudImage, x, y);
var textToDraw = "Actually text is really slow and if there's" +
"often repeated lines of text in your game you should be" +
"caching them to images instead";
inMemCtx.fillText(textToDraw, x, y);
}
由于 HUD 通常包含文本,因此如果您使用任何内容,我真的强烈建议您对其进行缓存。更多关于文本性能的信息。
正如其他人所说,没有普遍的最佳方法,因为它取决于您需要渲染的具体内容、频率以及图形组件之间可能需要发生的消息传递。
虽然 DOM 回流确实很昂贵,但这种全面警告并不总是适用。例如,使用 position:fixed; 元素避免触发页面重排(如果有非固定子元素,则不一定在元素内)。重绘(如果这是错误的,请纠正我)很昂贵,因为它是像素推送,因此本质上并不比将相同数量的像素推送到画布上要慢。有些事情可能会更快。更重要的是,每个都有某些操作,它们比另一个具有性能优势。
以下是需要考虑的几点:
在许多 A 级浏览器上使用 WebGL 加速的画布元素的可能性越来越大。这适用于 2D,其优点是将绘图操作发送到 GPU,这比 2D 上下文快得多。但是,这在某些目标平台上可能不可用(例如,在撰写本文时,它在 iOS Safari 中可用,但在针对混合移动应用程序使用的 iOS UIWebView 中不可用。)使用库包装画布可以抽象这一点如果可用,请使用 WebGL。看看 pixi.js。
相反,DOM 具有 CSS3 动画/过渡,通常由 GPU 自动硬件加速(不依赖于 WebGL)。根据动画的类型,您通常可以通过这种方式获得比使用画布更快的结果,并且通常使用更简单的代码。
最终,作为软件性能的一项规则,了解所使用的算法至关重要。也就是说,无论使用哪种方法,您如何调度动画帧?您是否查看了分析器以查看哪些事情花费的时间最多?这种做法非常适合了解影响性能的因素。
我一直在开发具有多个动画的应用程序,并将每个组件都实现为 DOM 和画布。最初我很惊讶 DOM 版本比画布(用 KineticJS 包装)版本性能更高,但我知道这是因为所有动画元素都是位置:固定并使用 CSS(通过 jQuery UI 在引擎盖下),因此获得 GPU 性能。然而,管理这些元素的代码感觉很笨拙(在我的例子中是 ymmv)。使用画布方法可以实现更完美的像素渲染,但是它失去了使用 CSS 设置样式的能力(这在技术上也允许像素完美的渲染,但实现起来可能或多或少复杂)。
我通过将最复杂的动画节流到较低的帧速率实现了很大的加速,在我的情况下,这与 60fps 版本没有区别,但在较旧的 iPad 2 上运行流畅如黄油。节流需要使用 requestAnimationFrame 和钳制调用不再频繁比想要的帧率。这对于 DOM 上的 CSS 动画来说很难做到(尽管同样,这些在很多事情上本质上更快)。我接下来要看的是将多个基于画布的组件同步到同一个 requestAnimationFrame 循环(可能是独立节流,或者是循环方法,其中每个组件都获得一定的帧速率,这可能适用于 2-3元素。(顺便说一句,
通过分析并看到我的代码中与 GUI无关的一个类有一个经常调用的特定方法来计算属性,我还获得了巨大的速度提升。有问题的类是不可变的,所以我改变了方法来记忆值,发现 CPU 使用率下降了一半。感谢 Chrome DevTools 和火焰图!始终配置文件。
Most of the time, the number of pixels being updated will tend to be the biggest bottleneck, though if you can do it on the GPU you have effectively regained all the CPU for your code. DOM reflows should be avoided, but this does not mean avoid the DOM. Some elements are far simpler to render using the DOM (e.g. text!) and may be optimized by the browser's (or OS's) native code more than canvas. Finally, if you can get acceptable performance for a given component using either approach (DOM or canvas), use the one that makes the code simplest for managing that type of component.
最好的建议是尝试不同方法中的隔离部分,使用分析器运行,使用技术过度绘制或以其他方式突破限制,以查看哪种方法可以运行得最快,并且不要在必须优化之前进行优化。这条规则的警告是您要问的问题:我如何事先知道哪种技术方法可以实现最佳性能?如果您根据假设的答案选择一个,那么您基本上是在过早地进行优化,并且会忍受由此引起的任意痛苦。相反,如果您选择专注于应用程序需求的快速原型设计或(甚至更好)受控实验,那么您就是在进行研发 :)
Browserquest使用 HTML 元素显示他们的 HUD,它的好处是您不必担心重绘等(并且性能会非常好,因为整个浏览器引擎都经过优化以非常快地渲染 DOM。
他们(browserquest)还为不同的游戏元素使用了几个分层的画布元素。我不知道确切的结构,但我想元素显示在哪个画布上取决于它需要重绘的频率。