前言
最近参加了一个卡牌游戏项目的前期准备工作,工作内容是做一个卡牌游戏的战斗场景demo。其中有不少动作特效动画,比如攻击的时候卡牌需要扭动、被攻击时需要斜后方抖动平移等等。cocos2d-x引擎实现这些动画不是问题,无非是Sprite执行MoveBy、RotateTo、EaseIn、EaseOut等动作极其组合,难点在于动作特效的编辑。作为一个程序猿,我是有自知之明的,动作特效编辑不是我擅长的,这种事情最好交给专业的美术去做。
问题在于,美术用什么工具去编辑这些动画?程序如何去读取?
首先想到的是CocoStudio,但最终放弃了,因为这不是CocoStudio擅长的领域,最直接的,CocoStudio无法实现颜色透明度的渐变。
最理想的工具非Flash莫属,说道做动画,首先想到Flash。但是Flash做出来的动画怎么应用到程序呢?总不能将动画分解,然后一段段用程序翻译吧?
偶然间遇到这篇文章 http://baike.baidu.com/link?url=uZwJfQlmQzF6R88DmJdK9gv2q-L6jMwiIgkIz9YdUNukYnkBt7mVuR64m2LDDiWmLbr_Iq3UYLB5TLG_fsCkp_
突然眼前一亮,救星啊,原来FlashCS5以后的fla文件其实是个ZIP包,所有的帧数据都保持在zip包里面的DOMDocument.xml文件里面。而且,保持Flash的时候,你可以选择*.xfl格式,这样其实就是一个文件夹,DOMDocument.xml就躺在文件夹里面等你。
好啦,基本思路就有了,想办法解析DOMDocument.xml文件,将其翻译为cocos2d-x能读懂的。
PS:实现代码我已经发到github了,地址https://github.com/ctbinzi/FlashCS6ForCocos2d-x
解读DOMDocument.xml文件
在解读DOMDocument.xml之前,我们先了解一下Flash存档的目录结构,如下:
左侧是Flash的lib库结资源目录构,右侧是该Flash项目的存档目录结构,可以发现,lib库下面的每一个资源都对应一个位于存档文件夹下的LIBRARY目录下面的文件,位图资源对应的是图片文件,目录对应的是同名文件夹,MovieClip对应的是xml文件,且该xml文件完整地描述了该MovieClip的详细信息。当然,在存档目录的根目录位置有一个DOMDocument.xml文件,该文件描述的是该Flash舞台及其相关属性。
好了,了解完Flash存档目录结构和Flash资源的关系后,我们可以开始逐一分析他们了。
首先,我们还是从DOMDocument.xml开始入手,我将该xml文档中一些暂时不太关心的内容去除,并在需要我们着重关心的部位加上注释,记录如下
<DOMDocument frameRate="60">
<!-- DOMDocument是该xml文档的根节点,其中我们比较关心的属性只有一个frameRate,该属性值记录的是该Flash的帧频率,FlashCS6的默认值是24-->
<folders><!-- 该节点下罗列了我们在LIBRARY里面创建的文件夹,其实我们可以不必关心它 -->
<DOMFolderItem name="folder_effect" itemID="537776ce-000001df" isExpanded="true"/>
</folders>
<media><!-- 该节点下罗列了该Flash所用到的图片及其它从外部到了的多媒体资源信息 -->
<DOMBitmapItem name="8.png" itemID="5313e068-000001f8" sourceExternalFilepath="../Actions/LIBRARY/8.png" sourceLastImported="1392726125" externalFileCRC32="1072798625" externalFileSize="34982" originalCompressionType="lossless" quality="50" href="8.png" bitmapDataHRef="M 1 1393811560.dat" frameRight="4200" frameBottom="3840"/>
<!-- 该节点描述了该多媒体资源的相关属性,其中我们比较关心的属性如下:-->
<!--name 该多媒体资源的名字,即我们在Flash编辑器的库视图里面看到的名字,如果是在文件夹里面的话,其名字会带上文件夹路径-->
<!--itemID 唯一ID,Flash里面的所有资源都会被分配一个类似这样的唯一ID-->
<!--href 该资源对应存储目录下的文件相对路径,相对LIBRARY文件夹的路径-->
<DOMBitmapItem name="effect1.png" itemID="531d709d-000001e6" sourceExternalFilepath="../Actions/LIBRARY/effect1.png" sourceLastImported="1392901873" externalFileCRC32="2070406904" externalFileSize="16942" originalCompressionType="lossless" quality="50" href="effect1.png" bitmapDataHRef="M 2 1394436802.dat" frameRight="3480" frameBottom="3420"/>
<DOMBitmapItem name="folder_effect/effect2.png" itemID="531e6ce8-000001ee" sourceExternalFilepath="../Actions/LIBRARY/effect2.png" sourceLastImported="1362735664" externalFileCRC32="1477998738" externalFileSize="36159" originalCompressionType="lossless" quality="50" href="folder_effect/effect2.png" bitmapDataHRef="M 3 1394500443.dat" frameRight="8000" frameBottom="5200"/>
</media>
<symbols><!-- 该节点下罗列了该Flash所拥有的MovieClip信息,每一个MovieClip对应一个xml文档,存放于LIBRARY目录下面 -->
<Include href="1.xml" loadImmediate="false" itemID="5332958a-000001da" lastModified="1395884060"/>
<!-- 该节点描述了一个MovieClip的相关属性,其中我们比较关系的属性如下:-->
<!--href 该资源对应存储目录下的文件相对路径,相对LIBRARY文件夹的路径-->
<!--itemID 唯一ID,Flash里面的所有资源都会被分配一个类似这样的唯一ID-->
<Include href="2.xml" loadImmediate="false" itemID="531e636b-000001db" lastModified="1395884063"/>
<Include href="Card1.xml" loadImmediate="false" itemID="5313e070-000001fa" lastModified="1395884065"/>
<Include href="effect1.xml" itemID="533295a5-000001de" lastModified="1395912879"/>
<Include href="effect2.xml" itemID="533295a2-000001dd" lastModified="1395914886"/>
<Include href="folder_effect/effect3.xml" itemID="5333f886-00000213" lastModified="1395914979"/>
</symbols>
... ...
</DOMDocument>
需要特别说明的是,<timelines>节点及其子节点我没有做任何说明,因为我们这个项目不打算把动画直接做在舞台上。主要原因是为了方便管理,我们把所有动画都做到了MovieClip里面。从上述描述信息中我们可以看到,在这个xml文档中,我们比较关心的内容主要是图片资源信息和MovieClip信息,下面我们需要做的事情就是逐一解析MovieClip。
从上述内容我们知道每一个MovieClip在LIBRARY目录下面都有一个对应的xml文档来存储其详细信息,下面,我们还是按照DOMDocument.xml分析方法,分析一个MovieClip文档。
<DOMSymbolItem xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://ns.adobe.com/xfl/2008/" name="test" itemID="5377877f-000001fc" lastModified="1400342648" lastUniqueIdentifier="1">
<timeline>
<DOMTimeline name="test"><!--时间轴,每个MovieClip有且只有一个,不必关心-->
<layers>
<DOMLayer name="layer1" color="#9933CC" autoNamed="false">
<!-- 层,对应Flash编辑器里面的的层,每个层可以添加多个关键帧,每个关键帧对应一个DOMFrame节点 -->
<frames>
<DOMFrame index="0" duration="9" tweenType="motion" motionTweenSnap="true" keyMode="22017" acceleration="-100" soundName="test.mp3">
<!-- 记录关键帧信息,关键帧即如上图所示,带有实心点的帧。我们比较关系的属性有如下:-->
<!-- index 帧索序号,从0开始-->
<!-- duration 该关键帧持续帧数,即从改帧开始到下一个关键帧之间间隔的帧数 -->
<!-- tweenType 渐变动画类型,在我们这个项目中只会用到传统补间动画,对应值为motion-->
<!-- acceleration 缓动效果,比较有意思的是,该值和我们在Flash编辑器里面设置的值符号刚好相反-->
<!-- soundName 声音,其值对应DOMDocument.xml文档里面记录的名字-->
<SoundEnvelope>
<SoundEnvelopePoint level0="32768" level1="32768"/>
<!--声音属性,level0和level1分别对应了左声道和右声道音量值[0~32768]-->
</SoundEnvelope>
<elements>
<!--记录该帧放置内容,我们可以在一个关键帧放入影片剪辑、图片、声音等信息,这些信息都将一一记录如下-->
<DOMSymbolInstance libraryItemName="1" centerPoint3DX="-159.95" centerPoint3DY="-52">
<!--每一个被放入该帧的MovieClip都将对应一个该节点,其中我们比较关系的属性是-->
<!-- libraryItemName 该属性值记录了该MovieClip的名字,对应DOMDocument.xml文档里面记录的名字-->
<matrix>
<!--该节点记录了该MovieClip在该帧时的位置信息,通过选择矩阵的方式记录,包括了坐标位置、旋转角度、缩放等信息-->
<Matrix a="0.30902099609375" b="-0.9510498046875" c="0.9510498046875" d="0.30902099609375" tx="-77.8" ty="-25.3"/>
</matrix>
<transformationPoint>
<Point y="-86.4"/>
</transformationPoint>
<color>
<!--该节点记录了该MovieClip在该帧时的颜色信息,主要包括argb是个颜色通道的值,Multiplier是百分比值[0~1],Offset是相对值[-255~255]]-->
<Color alphaMultiplier="0.4296875" redMultiplier="0.83984375" blueMultiplier="0.87890625" greenMultiplier="0.87109375" alphaOffset="14" redOffset="8" blueOffset="29" greenOffset="15"/>
</color>
</DOMSymbolInstance>
<DOMBitmapInstance libraryItemName="8.png">
<!--每一个被放入该帧的位图都将对应一个该节点,其中我们比较关系的属性是-->
<!-- libraryItemName 该属性值记录了该位图的名字,对应DOMDocument.xml文档里面记录的名字-->
<matrix>
<Matrix tx="-299.95" ty="57.15"/>
</matrix>
</DOMBitmapInstance>
</elements>
</DOMFrame>
</frames>
</DOMLayer>
</layers>
</DOMTimeline>
</timeline>
</DOMSymbolItem>
需要注意的是,为了列举尽可能多的情况,并控制文档不会过长,我对原始文档做了调整,可能无法被FlashPro正常解析,但不影响我们理解。
从上述解析文档可以看出,需要我们着重关心的是<DOMFrame>节点属性,以及<SoundEnvelope>、<DOMSymbolInstance>、<DOMBitmapInstance>节点内容。此外,我们需要理解在Flash里面,动画师由关键帧组成的,即关键帧在时间上的延续,以及关键帧到下一个关键帧的渐变过程。
另外,Flash的舞台其实也是一个MovieClip,也就是说,如果我们直接在Flash舞台上放置了元件,或是在舞台的时间轴上添加了关键帧,那么在DOMDocument.xml文档里面的内容也将跟上述解析内容类似,一样对待即可。