使用Flash开发网页游戏少不了与各种美术资源打交道。对于静态资源的那就是各种图片,对于会动的资源可以考虑直接做成swf。制作成swf的美术资源又可以分为两种:一种是直接将关键帧罗列在主时间轴上,那么当程序使用Loader类加载完swf文件后,就可以直接addChild在显示列表上;另一种是将美术资源封装成影片剪辑(MovieClip),并为AS3导出类,这样在程序中可以用类似new XXX的方式将美术资源创建出来。
弊端
然而,这种使用swf的方式有一个非常大的弊端,就是会重复创建位图,导致内存浪费。比如,你使用Flash CS5制作了一个swf文件,在主时间轴上放置了一个图片。那么,当你每次使用Loader加载这个swf文件的时候,实际上会创建出很多个位图实例,非常占用内存。Flash自身有一个BitmapData类是用来处理这种情况的,具体用法就是多个DisplayObject共用一个BitmapData,做到位图资源共享,减少内存开销。但是,你需要手动指定哪几个DisplayObject需要共用BitmapData。像上述这种情况,两次加载同一个swf文件,Flash并不会自动为我们共享使用其中的位图资源。
多次装载一个含有桌面壁纸图片的swf文件后,内存的使用情况
Flash的这种做法可以理解,毕竟Adobe认为绝大多数Flash中使用的美术资源都是矢量图而不是位图。但是,对于页游来说,基本上所有的美术资源都是位图序列帧动画。比如一个场景中有10个模型相同的玩家角色,如果存在10份序列帧动画的位图副本,那是巨大的浪费。
一种解决方案
一种解决方案就是使用自己定制的类实现动画的播放,而不是使用MovieClip。这样,我们可以确保同一个位图在内存中仅存在一份副本,也就是一个BitmapData。因此,swf在这里仅用作资源打包。
使用Flash CS,在库中为相应的图片导出AS3链接,这样我们就能通过 new TestBmpClass() 的方式在程序中创建对应的美术资源
为什么使用swf而不是自定义的文件格式打包图片资源呢?原因有两点,其一是对于带alpha通道的图片来说swf的压缩体积更小,其二是Flash虚拟机解析swf文件的速度比我们自己解析自定义格式文件的速度来的快。
公司目前使用的是一种自有的图片格式,这种格式能够对bmp图进行无损压缩,不过其体积远大于jpeg+alpha通道,解压的速度也比较缓慢(因为没有avm2虚拟机的内部支持)。我的任务是:a)将公司的动画图片转化为swf文件,b)为swf库中的每个图片生成as3链接。
二进制拼装SWF文件
要批量地将一堆bmp图片转换为swf文件,有两种做法。其一是使用Flash CS自带的批处理脚本jsfl,其二是直接通过拼装二进制文件拼出swf。由于需要批量转换大量的美术资源,直接拼二进制文件的方式效率较高,所以我采用后者。
swf的文件格式Adobe已经对外开放,可以参见 SwfFileFormat.pdf。
简单地说,swf文件是以 标签-文件块(tag file) 的形式组织起来的。每个标签文件的文件头标识了当前文件块的长度,以及是什么类型的文件块。比如,Flash中的每一帧对应着一个ShowFrame文件块,而每个AS3类则对应着一个DoABC文件块。我所需要拼装的swf文件有着如下的结构:
每个标签文件的含义由它的名字就可以大致猜出。其中白色的标签是必须的,橙色的标签是为了导出AS3类而添加的,蓝色的标签是为了放置在场景中而添加的。由于我们只需要swf中的BitmapData,因此图片并不需要放置在场景中。蓝色标签的加入实际上会增加文件大小,降低解析速度。不过,这样的swf文件在双击点开后就是一片空白,并没有预览。因此,可以考虑分别导出preview版和release版的swf文件。其中preview版在双击打开时可以预览动画,而真正发行的则是没有蓝色标签的release版。
另一个需要注意的地方是,即时不需要将图片放置在场景中显示,也需要ShowFrame标签,否则Flash虚拟机会一直卡在那里。
为AS3导出类
在swf中的图片资源并不能直接使用,需要为AS3导出链接。也就是说,需要为每个图片生成他们对应的AS3类。导出的类需要继承自flash.display.BitmapData,同时我们还可以把自己需要的字段(例如centerX,centerY)添加到类中。
在转换的过程中,我们可能还需要记录一些其它的信息,比如frameCount,interval。对于这些我们可以另外新建一个类,用静态变量的方式储存这些信息。
每一个AS3类对应的其实是一个DoABC文件块。要注意所有导出的类必须有着独一无二的类名,因此最好用文件名+后缀的方式为类命名。另外,在DoABC文件块之后,必须跟着一个SymbolClass文件块,用来将某个AS3类和某个图片资源关联起来。
我这里讲的非常简略,具体操作起来还是有点复杂。所幸Flash的虚拟机AVM2是对外开源的,其DoABC文件格式以及字节码指令可以参见 Avm2Overview.pdf。
解决资源重名
一般来说,我们导出的AS3类名是以文件名命名的,而美术做的资源文件和可能会重名。因此,我们需要确保每个导出类具有唯一的类名。一般来说,路径+文件名具有唯一性,但我们并不能以路径+文件名作为类的类名,因为这意味着文件一旦生成后路径永远不会被改变。最终,我们选择以文件名+生成时间+一个自增的值作为文件的类名。
但这样做也有一个问题要解决。我们在加载相应的swf文件后,需要使用getDefinitionByName()方法获取相应的类定义,再将对应资源new出来。如果仅以文件名作为类名,那么我们很容易就能通过文件名获取类定义。如果以文件名+生成时间+自增值作为类名,那么实际上我们在加载文件后并不知道类名是什么。
解决这个问题的办法也挺简单。在主时间轴上放置一个TextField,在TextField中保存导出类的类名。这样,在加载swf后,我们就可以通过获取场景中的TextField得到类名,再通过类名获取相应的类定义。