As3中实现卡马克卷轴算法

2019-04-13 22:18发布

大部分act游戏里都会用到地图卷轴,我目前正在做的也不例外。

   老实说,我对flash的底层封装还是很信任的:它将繁杂的底层操作简单化了。譬如一张位图的滚动,不论这张图有多大,都应该会自动缓存、优化渲染。
   不过当一个朋友和我说一个3000*600的位图以60fps滚动会占用掉10%以上的cpu时,我半信半疑。

   但实际试验确实如此,我尝试各种办法降低消耗,比位图缓存、scrollRect、像素滚动(这个消耗简直要命…),最后想到卡马克算法。 
   我先是做了一个简单的demo,只有单向缓冲区,但实验结果很明显是降低了至少30%的cpu消耗。
于是我开始尝试进行完善和封装。


   卡马克卷轴是世界顶级程序优化大师,以及游戏引擎开发领域的先行者 约翰·卡马克所创造的一种平滑卷轴算法。(此人和zun并列为我的偶像)

    早在20多年前,电脑游戏的发展才刚刚起步。相比专门为游戏而生的电视游戏,电脑游戏在当时还非常“弱智”。
    tv游戏里那些可以卷轴的游戏大大提高了用户的体验,当控制角 {MOD}行走时,镜头会跟随主角,而地图会卷动以提供更为广阔的游戏场景,让玩家体验到一个连续的世界。超级马里奥就是这类游戏中的成功典范。

    但在1990年,还没有人能在电脑上实现这样的效果,开发人员都用一些蹩脚的技巧来让玩家觉得游戏的世界比屏幕要大,譬如当玩家移动到屏幕最右边的时候,游戏会停顿一会,然后右边的场景出现在屏幕上。

    主要是当时PC的性能还很差,而卡马克下定决心要找出一种办法来在PC上实现像超级马里奥那样的平滑卷轴效果。 

    最终他用了几天时间实现了,这个创新为以后的2d电脑游戏带来了新的可能。

    
     

    回到正题:

    卡马克算法的主要思想就是: 只更新屏幕变化的部分,从而节省渲染的消耗。
    22.jpg
       其主要思想就建立一块略大于屏幕的缓冲区域,将显示数据写入缓冲区。当屏幕移动到新位置时,会产生如上图一样的分布,可以看到中心部分,两者是完全一样的,只有边缘部分被改变了(也就是我们的缓冲区)。那么将这部分不需要的区域砍掉以后,绘制上新的内容,就可以达到内存重用,并且减少渲染区域的目的了。

     而在AS3的实现里,略有不同。因为AS3并未提供操作内存的与直接渲染屏幕的API,所有的元件都已经封装了底层。而刷新屏幕的操作只有自动渲染(enterFrame)与事件渲染(updateAfterEvent)。所以,在AS3中实现的意义并不能带来质的提升,不过同时,其实现的步骤要更加简单。


     大概步骤为:

    1。创建一个略大于屏幕的bitmapdata,而大于多少,取决与你所设定的缓冲区域,对缓冲区进行遮罩。

    2。滚动时判断是否抵达了缓冲区边缘,如果达到则使用bitmapdata.scroll方法将整个像素反向回滚。

    3。滚动的同时将产生空白区域,也就是准备填入新数据的地方。

    4。在空白区域填入新数据。

    5。将bitmap再次反向回滚,以给用户产生画面连续移动的错觉。
    
    所以通过这一系列动作的目的就是让屏幕上滚动的图像只有显示区域(OSD)与缓冲区(BUFFER),大大缩小滚动面积。

    我认为一个合格的程序员应该总是千方百计的降低cpu的消耗,而不是用硬件发展很迅速这样的理由去逃避。
    但经过我的测试,采用卡马克算法后,巨大图像的滚动(如8000*8000),在不同的机器上有不同的表现,E5200的机器上,多次检验,平均消耗都有下降。而在我的旧笔记本X40上,却消耗了更多cpu。对于这样的结果,辛苦了一天多的我显然是有些失望的。

    为什么会有这样的波动我还继续研究,或许是封装后,每帧的判断应当再进行简化,还有不少优化的余地。另外与内存读取速度也有些关系。
    即便如此,这样细小的波动在整个项目中所占的比例是微乎其微的。
    
    目前百度和google都搜不到此算法在As3中的应用,之所以这样写这样一个类,也是起一个抛砖引玉的作用,让其拓展出更多的思路,比如以此原理实现RPG中的无限延展的地图。