基于Jscex.Async的JavaScript动画/游戏
2010-12-16 01:03 by 老赵, 4538 visits首先和大家宣布一个消息,Jscex的代码已经提交至Github上了,感兴趣的朋友下载来Dog Fooding一把,并欢迎提出反馈意见。Jscex受到F#计算表达式的启发,是一个面向JavaScript语言的monadic扩展,最常见的用途便是编写异步程序,尤其是逻辑复杂的异步程序。不过除此之外,使用这套异步库来编写动画或是游戏也是十分容易的事情。例如,一个人物的走动或是爆炸效果,其实可以视为一个贴图随时间不断变化的过程。这个变化的过程是异步的,但是有了Jscex.Async,我们只需使用最直接的同步形式编写代码就行了。
例如这里有个我用一个多小时编写的示例(需要支持canvas的浏览器,例如IE 9,FireFox,Chrome,Safari等等),类似于“是男人就撑过20秒”,其中有这么一段代码:
BulletGame.prototype._bulletFlyAsync = eval(Jscex.compile("$async", function (f) { var options = this._options; var bullet = {pos: {x: 0, y: 0}}; this._addBullet(bullet); for (var t = 0; this._playing && this._inArea(bullet.pos); t += 20) { $await(this._timer.setTimeoutAsync(20)); this._tryHit(bullet.pos); // 判断是否击中 bullet.pos = f(t); // 改变子弹位置 } this._removeBullet(bullet); }));
这段代码是一个异步程序,表示一颗子弹的飞行过程。子弹的飞行轨迹(某一时刻的位置)由函数f来决定,因此我们只需要写一个for循环,并且在循环内部“暂停”一段时间就行了,就这么简单。在循环之前,我们将子弹添加到一个容器里,这样在一个不断循环的paint方法里就会把这颗子弹绘制在canvas上面。循环结束后,我们将子弹从容器中移出。这一切都十分顺其自然,虽然这段代码的执行过程完全是异步的。
Jscex可以使用各种方式与现有代码组合至一块儿,例如这里我为BulletGame的prototype对象扩展了一个方法,为此我晚上还改进了一下Jscex的编译逻辑,以及Jscex.Async的实现,其目的就是在_bulletFlyAsync这样的函数内部保留this对象在JavaScript语义上的正确性。JavaScript语言中this引用的动态特征是种很强大的特性,但是对于“语言改造者”来说,在一个充满回调的过程中保持语义正确并不是件十分容易的事情(当然,也不算十分困难,只要“想明白”即可)。上面的代码经过编译之后,就类似于我们直接编写了这样的代码(暂时如此,未来会有修改和优化):
BulletGame.prototype._bulletFlyAsync = function (f) { return $async.Start(this, function () { var options = this._options; var bullet = { pos: { x: 0, y: 0} }; this._addBullet(bullet); return $async.Combine( $async.Delay(function () { var t = 0; return $async.Loop( function () { return this._playing && this._inArea(bullet.pos); }, function () { t += 20; }, $async.Delay(function () { return $async.Bind(this._timer.setTimeoutAsync(20), function () { this._tryHit(bullet.pos); bullet.pos = f(t); return $async.Return(); }); }) ); }), $async.Delay(function () { this._removeBullet(bullet); return $async.Return(); }) ); }); };
此外,JavaScript是一个很容易编写此类动画或是游戏的平台,因为它所有的代码都在UI线程上执行,因此不会有任何多线程方面的问题。例如上面的代码,我们可以放心地向容器里添加或删除“子弹”对象,试想在一个并发环境里构造一个线程安全的双向链表是件多么麻烦的事情。
最大的问题其实是浏览器环境里的性能问题,不过我对此并不怎么担心,因为V8已经给我们开了个好头,并且还在不断前进。此外对于动画和游戏来说,最大的性能问题更可能是canvas的绘图性能。现在这个简单的例子性能自然不会有太大问题,不过一旦加上贴图和素材,性能问题就会凸现出来了。对于目前的示例,我在安装Windows 7的台式机里试验了多种浏览器,帧数(fps)各有高低:
- Firefox:110左右
- Chrome:210左右
- IE 9:300至800不等,比较常见的是500左右
而在我的MBP上:
- Firefox:70左右
- Safari:60左右
- Chrome:190左右
此外还有同事测试了Safari最新的Nightly Build,帧数与Chrome不相上下。从中可以看出,IE 9的表现最为出色,可见它的GPU加速的确不是在吹牛的。值得一提的是,如果图像上没有子弹,那么帧数大约只有60出头,一旦同时出现了几十颗子弹,帧数变会飙升至500以上,峰值甚至可以到800。由此推测,IE 9也是在“按需”使用GPU,十分周全。
Jscex的源码及示例都在Github上,欢迎尝试。
赞一个…………