在游戏中发挥HTML5 Canvas的潜能

标签: 技术与实践 Canvas HTML5 | 发表时间:2012-05-04 15:51 | 作者:feijun
出处:http://www.webapptrend.com

目前, 支持HTML 5的浏览器和Windows 8 Metro成为了游戏开发的候选方案。

利用canvas(画布)可以实现 硬件加速,你可以在上面绘制游戏的内容,通过一些技巧可以达到 每分钟 60 的渲染速度。

在游戏中,fluidity(流畅性)这一概念非常重要,因为好的流畅性能带给玩家更好的体验。

这篇文章的主旨在于教你一些技巧,让你可以最大程度地发挥HTML5 canvas的潜能。

我将通过一个例子来表达我要讲的内容。这个例子是2D tunnel effect(2D隧道效应),它是我为Coding4Fun 会议写的(我参加了在法国举办的TechDays 2012)。

( http://video.fr.msn.com/watch/video/techdays-2012-session-technique-coding4fun/zqy7cm8l).

早在80年代,当我还是一个年轻的demomaker的时候曾受到一些Commodore AMIGA代码的启发,从而写了这个2D tunnel effect。

经过不断地改进,现在它仅使用了 canvasJavascript(最初的代码是基于68000汇编的):

完整的代码可以从这里获得: http://www.catuhe.com/msdn/canvas/tunnel.zip

这篇文章的目的不是讲解该程序的开发过程,而是让你通过优化已有的代码来达到一个实时性的效果。

使用 off-screen canvas (离屏画布)来读取图片数据

我想讲的第一点是怎样使用canvas来读取图片数据。实际上,任何一个游戏都需要图形来显示游戏界面和背景。canvas有一个非常有用的画图方法: drawImage 。这个功能可以用来绘制游戏界面,通过它你可以定义起始和目的区域。

但是有时候光使用它是不够的,比如你想要在源图像上实现一些特效,或者源图像不是一个简单的位图而是一个复杂的资源(如地图)。

在这些情况下,你需要访问到图片的内部数据。但是 Image标签无法读到这些数据,这时候就该canvas上场了。

事实上,每当你需要从图片中读取内容的时候,你都可以使用off-screen canvas。也就是说,当你导入一张图片的时候,你只需要将它渲染到canvas中(而不是DOM里),然后你就可以通过读取canvas的像素点来获得源图片的内容了(这个过程非常简单)。

有关部分的代码如下(2D tunnel effect中用来读取隧道的纹理数据的):

var loadTexture = function (name, then) {

    var texture = new Image();

    var textureData;

    var textureWidth;

    var textureHeight;

    var result = {};

    // on load

    texture.addEventListener(‘load’, function () {

        var textureCanvas = document.createElement(‘canvas’); // off-screen canvas

        // Setting the canvas to right size

        textureCanvas.width = this.width; //<– “this” is the image

        textureCanvas.height = this.height;

        result.width = this.width;

        result.height = this.height;

        var textureContext = textureCanvas.getContext(’2d’);

        textureContext.drawImage(this, 0, 0);

        result.data = textureContext.getImageData(0, 0, this.width, this.height).data;

        then();

    }, false);

    // Loading

    texture.src = name;

    return result;

};

为了使用这些代码,你还要保证隧道纹理图片的导入是异步的,因此你需要传递 then参数,代码如下:

// Texture

var texture = loadTexture(“soft.png”, function () {

    // Launching the render

    QueueNewFrame();

});

使用硬件缩放功能

现代浏览器和Windows8都支持 硬件加速的canvas,这意味着,你可以使用 GPU来调整canvas里面内容的尺寸。

在2D tunnel effect里,该算法要求处理canvas的每一个像素点,因此一个1024×768的canvas就得处理786432个像素点,并且为了达到流畅性的要求,每分钟得处理60次,也就是说每分钟要处理 47185920个像素点!

很显然,任何可以 减少像素处理总数的方法都能带来极大的性能 提升

又一次轮到canvas上场了!下面的代码展示了怎样使用硬件加速来调整canvas的内部有效区域使之等于DOM对象的外部尺寸:

// Setting hardware scaling

canvas.width = 300;

canvas.style.width = window.innerWidth + ‘px’;

canvas.height = 200;

canvas.style.height = window.innerHeight + ‘px’;

请注意DOM对象的尺寸( canvas.style.widthcanvas.style.height)和canvas有效区域的尺寸( canvas.widthcanvas.height)之间的差别。

当这两个尺寸不同的时候, 硬件会自动调整有效区域的大小,这是一件很棒的事:我们可以绘制低分辨率的图形,然后通过 GPU的调整使之符合DOM对象的大小(实现一个漂亮 免费的模糊滤镜效果)。

在这种情况下,本来只有300×200 的图像会被GPU扩展到跟你的窗口一样大。

所有的现代浏览器都支持该功能,因此你可以放心的使用。

优化 rendering loop

制作游戏的时候,需要一个rendering loop用来绘制所有的组件(如背景,界面,分数等等)。这个loop是代码的核心,因此必须充分优化从而保证游戏的快速和流畅。

RequestAnimationFrame

HTML5一个有趣的功能是使用 window. requestAnimationFrame. 代替 window. setInterval 来创建定时器,从而实现每(1000/16) 毫秒渲染一次(以达到60fps),你可以通过 requestAnimationFrame将该任务交给浏览器。调用这个方法表明你想要尽快的更新有关的图形。

浏览器会将你的请求放入内部渲染计划,并使之与其本身的渲染及动画代码(CSS, transitions等等)同步。这个方法另一个有趣的地方在于如果窗口不显示(minimized, fully occluded等等),你的代码就不会被调用。

这能改善性能,因为浏览器可以优化并发渲染从而提高动画的流畅性(如你的渲染周期太长的话浏览器会将它与其本身的渲染及动画周期同步)。

代码很清晰(别忘了window前缀):

var intervalID = -1;

var QueueNewFrame = function () {

    if (window.requestAnimationFrame)

        window.requestAnimationFrame(renderingLoop);

    else if (window.msRequestAnimationFrame)

        window.msRequestAnimationFrame(renderingLoop);

    else if (window.webkitRequestAnimationFrame)

        window.webkitRequestAnimationFrame(renderingLoop);

    else if (window.mozRequestAnimationFrame)

        window.mozRequestAnimationFrame(renderingLoop);

    else if (window.oRequestAnimationFrame)

        window.oRequestAnimationFrame(renderingLoop);

    else {

        QueueNewFrame = function () {

        };

        intervalID = window.setInterval(renderingLoop, 16.7);

    }

};

你只需要在rendering loop的结尾调用这个函数并在接下来的代码段里进行注册即可:

var renderingLoop = function () {

    …

    QueueNewFrame();

};

访问 DOM(Document Object Model)

为了优化rendering loop,你必须遵循这条黄金准则:DO NOT ACCESS THE DOM(不要访问DOM)。即使现代浏览器在这方面做了优化,读取DOM对象属性还是太慢了。

例如,在我的代码里,我使用了Internet Explorer 10 profiler(IE10的提供的分析器,按F12快捷键打开),显示的结果如下:

 

如图所示,访问canvas的宽度和高度会花费大量的时间!

原始代码如下:

var renderingLoop = function () {

    for (var y = -canvas.height / 2; y < canvas.height / 2; y++) {

        for (var x = -canvas.width / 2; x < canvas.width / 2; x++) {

            …

        }

    }

};

你可以通过两个变量来预先获取canvas.width 和 canvas.height,然后在后面用变量来代替这些属性值:

var renderingLoop = function () {

    var index = 0;

    for (var y = - canvasHeight / 2; y < canvasHeight / 2; y++) {

        for (var x = - canvasWidth / 2; x < canvasWidth / 2; x++) {

            …

        }

    }

};

是不是非常简单?虽然有时候很难注意到这些细节,但是请相信我这绝对是件值得的事。

预先计算

通过分析器得知, Math. atan2函数比较慢。事实上,该操作并不需要在运行时计算,你可以在JavaScript里面加一些代码预先计算出结果。

 

一般来说,预先计算一些较为费时的代码是一种好方法。这里,在运行rendering loop之前,我已经计算好了 Math.atan2

// precompute arctangent

var atans = [];

var index = 0;

for (var y = -canvasHeight / 2; y < canvasHeight / 2; y++) {

    for (var x = -canvasWidth / 2; x < canvasWidth / 2; x++) {

        atans[index++] = Math.atan2(y, x) / Math.PI;

    }

}

atans数组的使用明显的提高了性能。

避免使用 Math.round , Math.floor 以及 parseInt

最后一点是 parseInt的使用:

 

当你使用canvas时,你需要使用一些整数坐标。实际上,所有的计算都采用的浮点数,你需要将它们转换成整形。

JavaScript 提供了 Math.round, Math.floor 甚至 parseInt 来转换数值。但是这个方法做了一些额外的工作(比如检测数据是不是有效的数值, parseInt 甚至先将参数转换成了字符串!)。在我的rendering loop里面,我需要一个更快的转换方法。

在我的旧的汇编代码里面,我使用了一个小技巧:将数据右移0位。这会将浮点数从浮点寄存器移到整数寄存器,并且是通过硬件转换的。右移0位不会改变数据的值,但是会以整数形式返回。

原始代码如下:

u = parseInt((u < 0) ? texture.width + (u % texture.width) : (u >= texture.width) ? u % texture.width : u);

以下是改进后的代码:

u = ((u < 0) ? texture.width + (u % texture.width) : (u >= texture.width) ? u % texture.width : u) >> 0;

当然该方法要求你的数据是合法的数值。

最终结果

实现了上述优化之后得到的结果如下:

 

你可以看到做了这些基本的功能优化之后的表现。

原始的隧道渲染(没做任何优化):

做完上述优化之后:

下表展示了每项优化对帧速率的影响(在我的机器上):

 

更进一步

记住这些关键技巧,你就可以为现代浏览器或Windows8制作实时、快速、流畅的游戏了。

 

原文链接: Unleash The Power of HTML5′s Canvas For Gaming

 

您可能也喜欢:

利用HTML5 Canvas创建交互式Bubble Chart

关于HTML5你不得不知道的五件事情

关于HTML5的11个让人难以接受的事实

HTML5视频发展状况
无觅

相关 [游戏 html5 canvas] 推荐:

25 超棒的 HTML5 Canvas 游戏

- 迎客松 - LinuxEden开源社区-Linux伊甸园
Canvas 元素作为HTML5标准的一部分,允许你通过脚本动态渲染点阵图像. 这是为了基于浏览器的矢量图形而设计. HTML Canvas 把一个绘图 API 展现给 JS 客户端,使得脚本能够把想绘制的东西都绘制到一块画布上. 阅读全文 | 邮件推荐 | 评论回复.

html5 canvas入门

- - Marshal's Blog
可以把canvas看做div,不过,它的长和宽不能通过css来定义,要使用标签属性:. 或者javascript对象属性设置:. 使用canvas,只有一种操作方式,使用javascript. 获得canvas对象的上下文对象,该对象是操作canvas的主要对象:. 使用canvas画最简单的线, 点击运行示例,结果看起来是这样:.

在游戏中发挥HTML5 Canvas的潜能

- - Web App Trend
目前, 支持HTML 5的浏览器和Windows 8 Metro成为了游戏开发的候选方案. 利用canvas(画布)可以实现 硬件加速,你可以在上面绘制游戏的内容,通过一些技巧可以达到 每分钟 60 帧的渲染速度. 在游戏中,fluidity(流畅性)这一概念非常重要,因为好的流畅性能带给玩家更好的体验.

html5 canvas 图像处理

- - HTML5研究小组
前两天无意中看了下《pro html5 programming》,发现html5竟然也能很好的支持图像处理,在此稍稍交代一下. 与matlab处理图像类似的是,这里也是采用图像矩阵的形式. 下面就介绍一个简单的例子:. context1.drawImage(image,0,0);//绘制原始图像,(0,0)表示图像的左上角位与canvas画布的位置.

备份:html5 canvas cheat sheet

- - 膘叔
纯备份资料,HTML5的canvas资料.

HTML5 Canvas双缓存实例

- - Web前端 - ITeye博客
转自:http://www.108js.com/article/article3/30046.html?id=255. 下面是用HTML5的标签写的一个视差滚动动画的示例. 采用了制作动画或者游戏编程中常用的双缓冲技术:获取到页面中的Canvas对象之后,创建了一个与页面Canvas同样大小的Canvas对象.

HTML5 Canvas: 测试浏览器是否支持Canvas

- - CSDN博客Web前端推荐文章
本文翻译自Steve Fulton & Jeff Fulton. 在获取HTML页面上Canvas元素的引用后,我们需要测试一下该元素是否包含“上下文”(context). Canvas的上下文指的是由浏览器定义的用于绘画的平面. 简单地说,如果上下文不存在的话,Canvas也就名存实亡了. 测试浏览器是否支持Canvas有好几种方法.

HTML5 Canvas开发框架:CasualJS Framework

- Jimmy - ITeye论坛最新讨论
CasualJS Framework是根据ActionScript3?.0的架构开发的一套适用于HTML5 Canvas的面向对象的开发框架. 虽然Canvas提供了强大的绘图功能,但满足不了高级开发的需要. 利用CasualJS的显示对象架构及渲染机制,你可以轻松的在Canvas中操控各种位图、图形、影片剪辑等显示对象.

HTML5 Canvas(画布)教程 – 图像处理

- - Web前端 - ITeye博客
Canvas标记很多年前就被当作一个新的HTML标记成员加入到了HTML5标准中. 在此之前,人们要想实现动态的网页应用,只能借助于第三方的 插件,比如Flash或Java,而引入了Canvas标记后,人们直接打通了通往神奇的动态应用网页的大门. 本教程内容只覆盖了一小部分、但却是非常重 要的canvas标记的应用功能——图像显示和处理.

html5 canvas 详细使用教程

- - CSDN博客Web前端推荐文章
图形变形(平移、旋转、缩放). 矩阵变换(图形变形的机制). 绘制图像(图片平铺、裁剪、像素处理[不只图像、包括其他绘制图形]). 保存和恢复状态(context). 结合setInterval制作动画.      是html5出现的新标签,像所有的dom对象一样它有自己本身的属性、方法和事件,其中就有绘图的方法,js能够调用它来进行绘图 ,最近在研读《html5与css3权威指南》下面对其中最好玩的canvas的学习做下读书笔记与实验.