HTML5游戏开发 之 循环的控制(3)

来源:互联网 发布:网络教育本科怎么报考 编辑:程序博客网 时间:2024/06/05 00:23

7) 改进性能

RequestAnimationFrame确实是实现动画不可或缺的利器,但是不可以过度的使用。尤其是和一些渐变性的事件相关联的动画,比如,Chrome Web Store首页利用垂直滚轴不断显示页面内容。下面给出一个不太好的例子:
function onScroll() {

update();
}

?[Copy to clipboard]View Code HTML
function update() { // assume domElements has been declared// by this point :)for(var i = 0; i < domElements.length; i++) { // read offset of DOM elements// to determine visibility - a reflow // then apply some CSS classes// to the visible items - a repaint }}

window.addEventListener(‘scroll’, onScroll, false);


上面这个例子,每次scroll事件的时候,都调用RequestAnimationFrame,虽然浏览器会量力而行的执行动画渲染,但是有两个冲突的地方,成为了动画渲染的性能瓶颈。首先,scroll事件调用的频率,远远大约每秒60帧。也就是说,浏览器会缓存大量的RequestAnimationFrame事件,导致很多的update函在做无用功。其次,在每次执行RequestAnimationFrame的时候,里面对dom元素的属性进行了修改,引起了很多的Reflow和Repaint事件,显然完成所有的Reflow和Repaint时间的远远超过16ms。

为了解决第一个问题,首先要将scroll和RequestAnimaitonFrame函数分离,修改后的代码如下:

?[Copy to clipboard]View Code HTML
var latestKnownScrollY = 0;function onScroll() {latestKnownScrollY = window.scrollY;} function update() {  requestAnimationFrame(update);  var currentScrollY = latestKnownScrollY;   // read offset of DOM elements  // and compare to the currentScrollY value  // then apply some CSS classes  // to the visible items} // kick offrequestAnimationFrame(update);

上面的代码,scroll事件仅负责将最新的窗口位置,赋值给一个变量。这样的话,可以避免大量的RequestAnimationFrame事件被缓存,从而真正让RequestAnimationFrame按照浏览器的能力,进行动画渲染。

对于第二个问题,唯一的办法,尽量的减少Repaint和Reflow的事件。道理虽然简单,但是优化难度很大,要求读者对于Dom的渲染的每个函数都非常熟悉。下面列出几篇文章,仅供参考:
ClassList for great good.
Breakdown of repaint.
Learning from Twitter

发表在HTML5训练营|留下评论

HTML5游戏开发 之 循环的控制(2)

3) 如何调试帧率

理想的状态是,将游戏控制在60帧,这样充分利用显示器的能力,为用户提供流畅的画面。但是,通常情况下还是比较困难的,随着游戏逻辑的复杂性,帧率一般情况下都会下降。如果可以有一种可视化的方法,帮助开发者去观察每次RequestAnimationFrame的执行时间,可以让开发者及时的发现一些耗时的操作,尽量保证每次RequestAnimationFrame的执行时间控制在16ms内完成。

虽然目前还没有发现同一的方法,但是Chrome提供了abut:tracing的标签页,可以辅助开发者,去一探RequestAnimationFrame的究竟。不过,困难的是,如果想完全读懂 about:tracing,需要对于chrome的架构以及渲染方式非常熟悉,在加上GPU的原因,很难一下子完全理解整个页面。

这里介绍一个简单的用法,如下图,是从我的同事Seth的Blog中复制的图片,(如果想了解更多内容,可以参考Box2D, Web workers, Better performance),可以注意到,图中有两个红线,红线之间的间隔是16毫秒,相信很容易想到,这是每帧执行的最佳时间。在about:tracing页面中,双击每个方块,可以放大并出现每个函数的名字,按G,就可以立即出现两边的红线。里面有个泛蓝色的模块,标志WebViewImp:animate,它就是负责执行RquestAnimationFrame,貌似它的执行时间没有超过16ms,但是不幸的,因为在执行完RequestAnimationFrame之后,浏览器还对于需要渲染的元素,执行组装和渲染操作,很明显下图中的渲染时间,超过了红线的范围,也就是超过了16ms,图形渲染的效率自然会降低到60帧一下。

通过这个例子,可以看出,并不是把RequestAnimationFrame的执行时间控制在16ms以内,就可以得到60帧率的渲染。还需要整体考虑,RequstAnimationFrame引起了多少元素的渲染,以及多大程度的重绘。

about:tracing是非常好用的页面,有更多兴趣的同学,还请耐心学习chrome的渲染方式:
Trace Event Profiling Tool (about:tracing)

4) 页面显示API

RequestAnimationFrame很重要的优点,就是在Tab没有被显示的时候,可以暂停执行,不消耗CPU资源。对于大部分的动画需求,是没有问题的,但是也不能适应于所有的场景,举个例子,现在很多攻防类的游戏,在进攻其他家城堡的时候,可以派兵进行打仗。两军对阵的场景,通常是一段设计好的动画,有时候动画的时间还比较长,很多玩家会切换到其他页面,用户的本意是在其他网页等待对战结束。而RequestAnimationFrame控制的动画循环,会因为切换到其他页面,暂停执行,从而违背了设计的初衷,也违背用户的一些习惯。可以从程序逻辑上避开这个问题,比如人为加入一些计时器,按照时间播放相关的动画,但是这样容易和主程序的循环出现逻辑上的混乱。幸运的是,HTML5提供了Page Visibility的API。这个API可以在Tab被切换的时候,产生一定的回调,从而可以让开发者明确知道,目前页面处于哪种显示的状态。

function handleVisibilityChange() {
if (document.webkitHidden) {
if (playAnimation is true)
//continue to do this animation
}
}

document.addEventListener(“webkitvisibilitychange”, handleVisibilityChange, false);

上面是一段简单的代码,在发现页面不被显示的时候,可以执行一些必须被执行的动画。

5) Webworker的作用

现在很多的游戏,都利用到了Box2d或者其他重力引擎,而这种重力引擎的计算,相当耗费时间,如果将这些计算放在RequestAnimationFrame里面,很容易就会将每次执行的时间,超过16ms。最好可以将这类高成本的计算,放在一个单独的线程,每次RequestAnimationFrame只需要取到结果就可以了。

HTML5的webworker,刚好可以承担这个角色。如上图,是一个简单的设计图,box2d重力引擎,作为独立计算的单元,存在于webworker内部,利用setTimeout作为循环控制。在外部,RequestAnimationFrame控制动画的显示。

具体的代码和例子,可以参考:Box2D and Web workers for JavaScript developers

6) 毫秒精度的用处

RequestAnimationFrame最近刚推出了新的功能,可以提供微妙级别的精度,这个有什么用处呢?按照前面的说法,每秒60帧的显示,每帧的具体时间为(1000毫秒/60),大约为16.67毫秒。如果只有毫米级别的精度,为了有效的控制RequestAnimationFrame的执行时间,只能以整毫秒的精度,对RequestAnimationFrame进行检查,也就说,假设RequestAnimationFrame计划在5.5毫秒后执行,如果执行到某一步,发现当前时间已经是5毫秒,虽然理论上也许还有0.5毫秒的时间可以使用,但是不能在进行下一步的。现在有了微秒级别的精度,可以做更精细的控制。下图的解释比较形象,上面是16.667每帧可以执行的时间,下面是整毫秒数标志的时间,显然下面的图,会浪费很多的时间片。

目前,在chrome中,是通过window.performance.webkitNow()获得高精度的时间的,相信不久的将来,很快就会变成标准performance.now()。

7) 改进性能

[待续。。。]

发表在HTML5训练营|评论关闭

HTML5游戏开发 之 循环的控制(1)

1) 动画绘制的利器:RequestAnimationFrame

RequestAnimationFrame,绝对的大名鼎鼎。相信做HTML5的同学们,都听到过这个函数。再重复一下它的好处:
游戏页面在不被显示的时候,RequestAnimationFrame暂停执行,不会占用CPU时间
RequestAnimaitonFrame会将JS产生的动画以及CSS产生的动画,放到同一个Repait和Reflow的循环中。

对于第二点,很少有人提及,但是非常重要,如果没有RequestAnimationFrame,而用setTimeout,很有可能在每次绘制的时候,JS产生的动画和CSS产生的动画,没有同时发生,相信这个肯定不是你想要的结果。对于第二点,有点晦涩,不太容易懂,让我们更进一步解释一下,这就要从浏览器的渲染流程说起。大体上,浏览器的渲染流程有以下四步:

更新Dom的结构:每次你执行JS改变Dom结构,或者修改CSS相关属性,都会对Dom结构进行改变。现在HTML5中,多了Canvas元素,对于Canvas对象,执行某个操作,比如画一条线,也属于对Dom结构的改变。但是Canvas有个比较特殊的操作,比如对Canvas对象执行getImageData操作,会强制浏览器立即跳到第四步,将渲染好的浏览器窗口,绘制到屏幕。
渲染每个元素:在所有JS和CSS执行完毕之后,浏览器按照要求,开始对每个元素进行渲染。
将所有元素渲染到窗口:按照窗口大小的要求,将所有的元素,绘制到一个平面上。
通过操作系统窗口管理器,将渲染好的窗口,输出到屏幕。

通常,执行完第四步,称为一帧。目前,大部分的显示器,都会将显示控制在每秒六十帧,浏览器处于优化的目的,通常的显示频率也不会超过六十帧。

讲到这里,大约可以清楚一点,如果不用RequestAnimationFrame,而是用传统的SetTimeout,很难要求浏览器将同一次SetTimeout里面执行的Dom或者CSS操作放到同一帧中,也就会随机的出现,某个JS操作的动画和CSS或者Canvas的动画,不能同步。因为随机的,每次行为不一致,相信这是所有开发者都不愿意碰到的情况。没有开发人员怕Bug,但是害怕的是Bug不可以重现。

2) 计算游戏的帧率(FPS)

衡量游戏性能的重要指标就是帧率(FPS),因为浏览器的实现不一,有些浏览器出于优化的目的,没有严格的按照第一部分介绍的四步,有些时候,在某一帧没有完成的时候,就开始执行下一帧,所以理论上,很难严格的记录浏览器显示的帧率。如果你可以有个高速的摄像机,对着屏幕拍摄,是可以严格的记录屏幕显示的帧率,但是相信这个方法,很难被大规模使用。

目前也有一些第三方类库,可以帮助你衡量游戏的帧率,比如比较著名的https://github.com/mrdoob/stats.js,但是这个是非常不严格的,以StatsJS为代表的类库,是利用很衡量setTimeout或者setInterval每秒钟被执行的次数或者时间,来衡量帧率的。但是seTimeout函数被执行的次数和时间,和RequestAnimationFrame没有特别严格的对应关系,只可以作为参考。或者,简单一点说,每一帧被渲染的过程中,setTimetout函数很有可能被执行一次或者两次,甚至更多次。

理论上,为了监控RequestAnimationFrame帧率,需要开发者hook RequestAnimaitonFrame这个函数,在函数每次执行完毕的时候,执行Canvas的getImageData操作,强制浏览器渲染本次RequestAnimationFrame的所有操作,计算两次渲染操作的时间差,从而得出帧率。

幸运的是,Firefox浏览器已经开发了得到帧率的接口,可以省去很多周折。比如window.mozPaintCount这个接口,可以直接告诉开发者,浏览器渲染的帧率。

发表在HTML5训练营|标签为HTML5 游戏开发, 游戏开发|留下评论

HTML5 游戏开发 之 资源加载篇(2)

四) 下载过程的管理

4.1) 如何管理成千上百的资源

在游戏开发的过程中,很有可能会有成千上百张图片。最直接的方式,是将这些图片编写在代码中,但是图片的名字很容易改变的,会造成大量的维护工作,甚至影响代码的打包和发布。我的同事Boris,在他的代码演示库中,给出了一个参考实现方式,如下。这种方式,可以保证,在需要修改或者调整资源名称或者路径的时候,不需要接触代码。

?[Copy to clipboard]View Code HTML
{  "assetRoot": "url/to/assets",  "bundles": [  {    "name": "unique bundle name",    "contents": [    "relative/path/to/asset.jpg",    "another/asset.mp3"      ]  },  "autoDownload": true}var gal = new GameAssetLoader("http://path.to/gal.manifest");// Load the GAL. If manifest indicates autoDownload, this call will// start loading assets one by one.gal.init(function() {// Called when the library is initialized});

更完整的代码,可以参考GitHub上的源代码

4.2) 如何实现批处理的下载

再获得了资源列表之后,就要开始资源的下载。显然,需要这样的方法。

?[Copy to clipboard]View Code HTML
AssetManager.prototype.downloadAll = function(downloadCallback) {  for (var i = 0; i &lt; this.downloadQueue.length; i++) {  var path = this.downloadQueue[i];  var img = new Image();  var that = this;  img.addEventListener("load", function() {    // coming soon  }, false);  img.addEventListener("error", function() {  // coming soon  }, false);  img.src = path;}}<pre>下载的过程中,一般情况下都需要一个进度条,来显示完成的情况,所以必须对AssetManager进行计数。<strong><strong></strong></strong> <pre lang="html">function AssetManager() {  this.successCount = 0;  this.errorCount = 0;  this.downloadQueue = [];} AssetManager.prototype.isDone = function() {  return (this.downloadQueue.length == this.successCount + this.errorCount);}AssetManager.prototype.getProcess = function() {  return (this.successCount + this.errorCount)/this.downloadQueue.length;}

显然,也需要对每个img的load和error事件,进行计数。还请注意downloadAll函数有个参数叫做downloadCallback,在资源下载完成以后,通知此函数,进入游戏过程中。

?[Copy to clipboard]View Code HTML
img.addEventListener("load", function() {  that.successCount += 1;  if (that.isDone()) {    downloadCallback();  }}, false);img.addEventListener("error", function() {  that.errorCount += 1;  if (that.isDone()) {    downloadCallback();  }}, false

4.3) 游戏中的不同关卡

游戏通常是分关卡的,完全没有必要在一开始就将游戏的所有资源下载到本地,毕竟不是每个玩家都会将游戏通关。为了按需下载,比较完备的资源加载器,应该可以对每个资源配上一个标签或者属性,可以标志它属于哪一关。每一关的开始,只下载和本关相关联的资源,在每一关结束的时候,在去下载下一关的资源。不仅减少用户的不必要的等待时间,还可以有效的减少服务器的压力。

5.资源加载器的具体实现

5.1 PreloadJS

官方网站:http://www.createjs.com/#!/PreloadJS/download
开源代码:https://github.com/CreateJS/PreloadJS/

专门用于资源下载的类库,非常好用,考虑的也非常全面,首先推荐的一款软件,尤其是读者不希望加载特别大的游戏引擎是,这款软件可以作为首选。

具体的例子可以参考:https://github.com/CreateJS/PreloadJS/tree/master/examples

5.2 Cocos2d-HTML5

[TBD]

发表在HTML5训练营|标签为HTML5 游戏开发, 游戏开发|留下评论

HTML5游戏开发 之 资源加载篇(1)

一) 背景介绍

利用HTML5进行游戏开发,相比于其他语言例如Java、C++等,有很多不同,其中一个就是资源加载。本文主要对HTML5游戏资源加载的问题、原因以及解决方案,进行一些分析,试图解释一下问题:

  • 如何加载不同类型的资源
  • 如何进行批量加载
  • 如何显示加载的进度条
  • 如何存放资源

在文章的最后,也会列举一些游戏引擎的实现方案,供大家参考。通过此篇文章,希望可以让读者对于资源加载的技术有一个全面的了解。

二) 需要考虑的资源类型

一般游戏需要的资源,主要包括图片、音频、视频以及二进制数据文件。如果是3D游戏,还会需要一些模型文件,例如3dmax导出的obj文件。通常的情况下,这些资源文件,少则几十兆,多则几个G。对于很多客户端游戏,这个并不是特别大的问题。通常,它们可以将这些资源打在安装包中,随着安装的过程,一次性的存放在本地。

但是,Web游戏面临的情况比较复杂,主要有两个原因:

  1. 因为所有的资源都放在云端的服务器上。
  2. 浏览器为了优化网页的渲染,对于图片、音频等资源的加载,通常都是异步的。

大家可以回想一下,在打开某些网页的时候,偶然也会看到,即使在网页显示完以后,总有一两个图片的位置是空白的,大约几秒钟以后,这些图片往往又在不经意中显示了出来。

除了图片、音频等二进制文件,还有一类比较特殊的文件,就是Javascript文件。尤其是游戏逻辑比较庞大时,Javascript文件也可能有几百K,甚至好几兆。如果仅是根据文件的大小,这类文件似乎可以忽略不计。但是由于浏览器对于Javascript文件的处理是同步的,往往这些文件会成为性能的瓶颈。

举个例子,当浏览器在解析网页的过程中,碰到了<script>标签,它会立即转入对<script>标签的解析,同时阻塞的等待解析的完成。如果<script>标签,带有src属性,浏览器同样是阻塞的等待下载完成。所以,有时我们抱怨网络太慢,其实是委屈了运营商,很多时候,是脚本执行占用了太长时间,阻塞了网页的显示。

对于Javascript脚本的加载,首先要解决下载的问题,通常是伪装Javascript文件成资源文件,比如将Javascript中的脚本,作为整个字符串,放入一个JSON文件,充分发挥浏览器异步下载的能力。其次要缩短每次脚本文件解析的时间,这个最重要的就是按需“执行”,也就说要将脚本模块化。模块化是比较容易理解的,就是模仿面向对象的编程方法,将不同功能的函数放在不同的文件中。

但是,这样做带来另外一个问题,因为Javascript没有提供类似于面向对象语言中的模块继承功能,例如,在Java中,Java虚拟机会自动的将该文件依赖的其他类,导入运行时环境。为了实现模块化,也需要为Javascript模拟一套类似的功能,幸运的是,目前已经有许多成熟的类库,例如RequireJS。因为Javascript文件的加载不属于游戏开发的专有问题,在本文中不做详细介绍。

三) 如何加载不同类型的资源

2.1 通过浏览器内置对象的回调接口,实现资源加载

对于图片文件的加载,浏览器提供了方便的回调接口,比较容易实现,如下:

?[Copy to clipboard]View Code HTML
var image = new Image();image.addEventListener(“success”, function(e) {// do stuff with the image);image.src = "/some/image.png";

但是比较麻烦的是,HTML并没有提供对等的Audio、Video对象。对于Audio,虽然Web Audio API可以提供类似的功能,但是明显学习门槛高了一些。对于Video,目前还没有可以有效的方式,可以模拟类似的功能。对于文本、二进制等文件,更是比较麻烦。

2.2 通过Ajax请求,实现资源加载

利用Ajax对HTTP地址进行请求的能力,相信大家没有任何质疑。但是,在Ajax请求到相关资源以后,如何将资源转化为相应的图片、音频等对象,好像又产生了一些困难。

但是幸运的是,目前Ajax推出了新的标准,可以支持对二进制数据的提取。再辅助目前新的数据存储方式,比如Blob、FileSystem等,可以轻松的解决这个问题。

利用Blob将资源转换相应的对象,代码片段如下,更多代码请参考“New Trics in XHR”

?[Copy to clipboard]View Code HTML
window.URL = window.URL || window.webkitURL;  // Take care of vendor prefixes.var xhr = new XMLHttpRequest();xhr.open('GET', '/path/to/image.png', true);xhr.responseType = 'blob';xhr.onload = function(e) {  if (this.status == 200) {  var blob = this.response;  var img = document.createElement('img');  img.onload = function(e) {    window.URL.revokeObjectURL(img.src); // Clean up after yourself.  };  img.src = window.URL.createObjectURL(blob);  document.body.appendChild(img);...  }};xhr.send();

利用FileSystem,将资源转换为相应的对象,代码片段如下,更多完成代码,请参考“LOADING LARGE ASSETS IN MODERN HTML5 GAMES”

?[Copy to clipboard]View Code HTML
var xhr = new XMLHttpRequest();xhr.open('GET', url, true);xhr.responseType = 'arraybuffer';xhr.addEventListener('load', function() {  createDir_(root, dirname_(key).split('/'), function(dir) {    dir.getFile(basename_(key), {create: true}, function(fileEntry) {    fileEntry.createWriter(function(writer) {    writer.onwrite = function(e) {    // Save this file in the path to URL lookup table.    lookupTable[key] = fileEntry.toURL();    callback();  };  writer.onerror = failCallback;  var bb = new BlobBuilder();  bb.append(xhr.response);  writer.write(bb.getBlob());  }, failCallback);}, failCallback);});});xhr.addEventListener('error', failCallback);xhr.send();

上面两种方式,都是在获得资源后,为资源生成一个URL地址对象,在将此地址赋给相关的对象。

2.3 通过创建元素的方式,获得资源

对于第二种方式,相信不少读者担心不同浏览器的兼容性,毕竟里面利用了大量的HTML5新属性,而这些属性,很多属于刚刚发布的特性,每个浏览器支持的情况不太一样。所以,还需要一种可以兼容所有浏览器的方式。通过创建元素的方式,相对比较保险。参考如下代码:

?[Copy to clipboard]View Code HTML
var res = document.createElement(“image/audio/xxx”)res.src = “http://www.yourdomain.com”

但是这样的代码,也碰到了和第一个方法同样的问题,不是所有的元素都提供了onload函数。对于Image,处理相对简单。但是对于Audio/Vido,只能利用canplaythrough等函数做一些大约估计。

总之,为了做好资源下载,可能不能完全依赖某一类方法,最有可能的方式是根据每种方法的优缺点,根据具体原因进行选择。在一些比较成熟的游戏引擎和类库的实现中,也确实是融合了这三种不同的方法。

(待续)

发表在HTML5训练营|标签为HTML5 游戏开发, 游戏开发|留下评论

HTML5游戏的“2011”

HTML5的优势在哪里?确实是一个“仁者见仁,智者见智”的问题。而用HTML5开发游戏,更是一个争论很大的问题。有人担心性能,有人担心硬件支持,还有人担心视频和音频…用HTML5做游戏开发,到底可以吗?在今天的讨论中,让我们一起看几款游戏开发的案例。希望这些案例,可以给我们一个答案。

地点:http://www.beijing-open-party.org/topic/100
演讲:Google开发技术推广部经理 胡坤
(编者:希望借此机会可以对2011的游戏推广工作做一个总结)

发表在HTML5训练营|3 条评论

使用HTML5制作物理游戏

简介
HTML5技术为现今Web应用程序在浏览器中提供了非常广阔的发挥空间,其强大的功能让我们在浏览器上开发游戏和玩游戏不再是难事。利用Canvas和强大的JavaScript引擎,我们可以轻松地完成休闲游戏的开发。玩家只需打开浏览器,不用安装插件便可以享受到游戏带来的乐趣。

本文由NTFusion团队所作,结合了他们在Google Chrome Web Store上发布的《拯救PAPA》*和大家分享一下使用HTML5开发物理游戏的心得。

在阅读本文之前,您可以从Chrome Web Store安装《拯救PAPA》来尝个鲜儿。

搭建物理世界
物理游戏当中的一切物体,都是在一个设定好的物理世界中运作的。创建物理世界,我们现在使用的是Box2D物理引擎的JavaScript版本。构建物理世界的时候,首先我们需要定义相关的物理物体,然后根据游戏的特性所需,设定相关的物理参数。

// 创建物理世界实例
var world = new b2World(new b2Vec2(0, 9.8), true);
var scale = 1 / 30; /*像素与Box2D里的长度单位的转换*/

// 创建地面和其物理性质的设定
var bodyDef = new b2BodyDef();
var body = world.CreateBody(bodyDef);
var fixtureDef = new b2FixtureDef();
fixtureDef.density = 10;
fixtureDef.friction = 0.3;
fixtureDef.restitution = 0.1;
var polygonShape = new b2PolygonShape();
polygonShape.SetAsBox(800 * scale, 20 * scale);
fixtureDef.shape = polygonShape; body.SetType(b2Body.b2_staticBody);
body.CreateFixture(fixtureDef);
body.SetPosition(new b2Vec2(400 * scale, 490 * scale));

// 创建PAPA方块和其物理性质的设定
var bodyDef = new b2BodyDef();
var body = world.CreateBody(bodyDef);
var fixtureDef = new b2FixtureDef();
fixtureDef.density = 10; /* density 为密度*/
fixtureDef.friction = 0.3; /* friction 为摩擦系数*/
fixtureDef.restitution = 0.8; /* restitution 为弹性系数*/
var polygonShape = new b2PolygonShape();
polygonShape.SetAsBox(30 * scale, 30 * scale);
fixtureDef.shape = polygonShape;
body.SetType(b2Body.b2_dynamicBody);
body.CreateFixture(fixtureDef);
body.SetPosition(new b2Vec2(400 * scale, 100 * scale));

Box2D引擎里面有一个很好的功能DebugDraw,可以模拟出我们预设的物理世界并展示给我们看,非常适合我们做快速游戏原型开发。物理世界构建完毕后,通过DebugDraw功能我们就可以看到游戏的原型,这样我们就可以在这个基础上调整物体的参数、制作关卡等等。

// DebugDraw需要一个canvas实例,所以我们先创建b2DebugDraw实例,并设置相关参数
var debugDraw = new b2DebugDraw();
debugDraw.SetSprite(document.getElementById("canvas").getContext("2d"));
debugDraw.SetDrawScale(30.0);
debugDraw.SetFillAlpha(0.5);
debugDraw.SetLineThickness(1.0);
debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);

// 捆绑到物理世界实例
world.SetDebugDraw(debugDraw);

下图就是使用DebugDraw绘制出来的《拯救PAPA》中的物理世界实例:

图1.《拯救PAPA》中的物理世界

图1

这些工作完成以后,下一步我们需要在物理物体上“贴”上图片,这样玩家才可以看到真实的游戏画面。

同步显示对象
首先,我们需要创建一个DisplayObject类,其类似于Flash显示列表机制中的显示类,该类有x、y和rotation等相应属性,并且有自身的绘图方法,再通过数组形式来实现显示列表。

下一步,我们将需要显示的DisplayObject加入显示列表,再将其与Box2D中对应的物体的位置,自身旋转角度进行同步:
var position =body.GetPosition();
var angle =body.GetAngle();
displayObject.x = position.x / scale;
displayObject.y = position.y / scale;
displayObject.rotation = angle * 180 / Math.PI;

根据图1中的物理世界,我们画上图片之后,就可以见到下图的画面:

图2. 真实的游戏画面

图2

动画
DisplayObject与物理物体的同步处理好之后,我们便可以往它里面填充需要显示的图片,然后通过一定的时间间隔更换图片,即可达到动画的效果。通常,动画是由很多张图片组成的,为了减少读取次数,我们将动画中的所有的图片合成为一张大图,称为SpriteSheet。图3为《拯救PAPA》中1号工具的动画SpriteSheet:

图3.《拯救PAPA》中1号工具的动画SpriteSheet

图3

然后,我们便可以在SpriteSheet中根据坐标获取当前帧所需要显示的图片:
var canvas = document.createElement("canvas");
canvas.width = width;/*width为所画图案的宽度*/
canvas.height = height;/* height为所画图案的高度*/
canvas.getContext('2d').drawImage(sheet, x, y, width, height, 0, 0, width, height);

总结
HTML5技术还在逐步自身完善当中,我们的程序员也正在不断的努力,创作更多的好作品。而本文所谈及的物理世界的构建,显示对象以及对动画播放等技术,对于我们制作HTML5的游戏中是非常有用的。我们期待看到HTML5技术的不断发展和更多创新的HTML5网页游戏。

原创粉丝点击