来源:蜘蛛抓取(WebSpider)
时间:2016-07-05 12:41
标签:
按键精灵后台鼠标点击
SpriteSheet精灵动画引擎   本文介绍Flash中SpriteSheet精灵序列图与其它渲染方式的性能对比、SpriteSheet的原理及注意实现,最后实现了一个精灵序列图的渲染引擎。本文的SpriteSheet引擎及demo可以在github上下载:
动画渲染性能对比 Flash中动画制作方式有多种,如矢量动画、位图帧动画、精灵序列图等等。针对不同的制作方式,对同一个角色动画进行如下测试:一个角色在屏幕上显示5个实例,对应呼吸、施法、行走、受伤、普攻状态。 测试运行的环境: l  Release version of Flash Player 12.0.159.1 l  AMD Phenom(tm) II X4 830 Processor(2800 Mhz) l  Microsoft Windows 7 专业版 (32位) 得出测试结果如下表所示,为了节约大小资源中所用位图均为png8。 表1:不同动画渲染效率对比(具体测试数据与所使用资源有关)
内存(KB)
文件大小(KB)
<font style="font-size: 12pt" color="#
<font style="font-size: 12pt" color="#m
<font style="font-size: 12pt" color="#
矢量+cacheAsBitmap
<font style="font-size: 12pt" color="#
<font style="font-size: 12pt" color="#.2m
<font style="font-size: 12pt" color="#
<font style="font-size: 12pt" color="#
<font style="font-size: 12pt" color="#.8m
<font style="font-size: 12pt" color="#4
位图+导出类
<font style="font-size: 12pt" color="#
<font style="font-size: 12pt" color="#m
<font style="font-size: 12pt" color="#7
SpriteSheet精灵序列图
<font style="font-size: 12pt" color="#ff png + 31 json/xml
从上表可以得出,精灵序列图消耗CPU最少,并且文件大小适中,但内存消耗较大。对于游戏来说,CPU销毁越小,帧频可以越大,游戏越流畅。
图1:精灵序列图动画效果及帧频、内存信息 可以看出使用精灵序列图帧频和内存都非常稳定,内存没有频繁的gc,gc非常消耗cpu会造成游戏卡顿现象。反观其它渲染方式,会发现内存不稳定,这对游戏性能是一个风险。
图2:矢量动画
图3:矢量+cacheAsBitmap动画
图4:位图动画
图5:位图+导出类 下面详细介绍下精灵序列图的原理及注意事项。 精灵序列图 SpriteSheet精灵序列图是一种大的网格式位图,其中每一格都对应着一个动画截屏,每一动画截屏对应动画的一帧。精灵序列图通常采用PNG格式,这样可以使用Alpha通道。
图6:角色受伤动画序列图 除了大位图之后,还必须有一个对应的数据描述文件,常用的格式有json、json-array、xml。数据描述文件,用来指定每帧动画在大图中的位置(偏移位置、宽、高等等),如json格式如下:
JSON格式描述数据:
{&frames&: {   &呼吸0000&: {               &frame&: {&x&:0,&y&:0,&w&:110,&h&:110},               &rotated&: false,               &trimmed&: false,               &spriteSourceSize&: {&x&:0,&y&:0,&w&:110,&h&:110},               &sourceSize&: {&w&:110,&h&:110} }, &呼吸0001&: {               &frame&: {&x&:110,&y&:0,&w&:110,&h&:110},               &rotated&: false,               &trimmed&: false,               &spriteSourceSize&: {&x&:0,&y&:0,&w&:110,&h&:110},               &sourceSize&: {&w&:110,&h&:110} }, … }, &meta&: {               &app&: &Adobe Flash CS6&,               &version&: &12.0.0.481&,               &image&: &jsonformat.png&,               &format&: &RGB8&,               &size&: {&w&:1024,&h&:1024},               &scale&: &1& } }
其中: Key-&呼吸0000&:表示帧的名字/对应图片文件名(json-array格式中,使用filename字段表示); frame: 图片在大图中的偏移位置(左上角为原点)和大小(未旋转前)  需要注意这里的图片大小是图片未旋转前的大小; rotated:  是否旋转(顺时针方向); trimmed: 是否有去掉周围多余的透明部分; spriteSourceSize: x,y表示图片未去掉周围透明部分的偏移量,这是如果需要还原图片原先的大小要用的; sourceSize: 图片的原始大小,包含透明部分; 渲染机制 精灵序列图使用位块图像传输(bit-blitting,blit = Bit-Block Image Transfer)技术,它涉及到使用位图来渲染最终的显示效果。 将需要显示的效果,像素会绘制到一个已添加到舞台上的位图中。为了表现动画效果,会在一个循环中更新位图的像素。关键步骤如下: 1)     加载动画中需要的Sprite Sheet位图数据(.png文件) 2)     在displayList中添加一个BitmapData目标位图数据(画布) 3)     向画布复制或者擦除游戏(copyPixel vs draw) 4)     根据游戏显示层次顺序将Sprite Sheet复制到画布 5)     在游戏循环中重复第3、4步 copyPixel的效率比draw高,所以一般情况下,使用copyPixel复制图像到画布。 内存占用 在所有动画渲染方式中,精灵序列图对帧频的影响是最小的。因为精灵序列图会预先被缓存到BitmapData实例中,这就可以使渲染速度变得更快。一定要随时注意内存的占用,仔细把控,详尽规划。精灵序列图之所以效率高,是由于位图序列都缓存在内存中从而可被快速调取。但这样也可能会导致巨大的内存开销。
注意:一张图片占用多少内存只取决于图像的尺寸,而与图像文件的类型和图像压缩无关。 位图所占内存(字节)= 位图宽度 x 位图高度 x 4
假设一帧的图片的大小为200x400像素,占用内存312.5KB。如果一个动画18帧,则占用内存约5.5M。如果一个角色包含4个方向或4个动作的动画,则占用内存约22M。同屏在线10个角色,则占用内存约220M。 SpriteSheet工具 现在Flash cs6已经集成了将动画导出为SpriteSheet,如下图所示:
图7:Flash cs6导出SpriteSheet设置 TexturePacker也可以打包图片为SpriteSheet格式。 精灵序列图引擎 前面介绍了精灵序列图的原理及注意事项,下面实现一个精灵序列图动画的引擎,支持Flash Cs6/TexturePacker导出的JSON、JSON-Array、Starling(XML)3种数据格式。
图8:精灵序列图引擎类图 SpriteSheet类
图9:SpriteSheet类 SpriteSheet继承自flash.display.Sprite,包含一个bitmap成员用作画布。使用定时器Timer来驱动动画循环。 SpriteSheet使用类似Movieclip,提供play()、stop()、gotoAndPlay()、gotoAndStop()接口,并支持鼠标事件。 mAnimation成员(Animation实例)用于描述SpriteSheet当前表示的动作,如游戏中一个角色包含呼吸、行走、施法、受伤动作。 mTextureAltas成员(TexureAtlas实例)用于维护整个精灵序列图数据,并负责将特定帧位图复制到画布显示。 TexureAtlas类 TexureAtlas类保存了整个精灵序列图数据,并根据SpriteSheet的当前动作,生成构成动画的所有帧在精灵序列图中的偏移和大小。 Animation类 Animation动画信息类。 l  seqName表示动画序列名(e.g. &walk&) l  delay表示帧间隔 l  loop表示动画是否循环播放 l  arFrames:Vector.&SpriteFrame&;// 帧信息数据 SpriteFrame类 SpriteFrame类表示图片在大图中的偏移位置(左上角为原点)和大小(未旋转前)等等信息,根据数据描述文件生成。
图10:SpriteFra示意 JsonFormat、JsonArrayFormat、XmlFormat类 SpriteSheet序列图数据解析类,分别解析对应格式的描述数据。 Demo实例 SpriteSheet使用非常简单,与原生Movieclip差异不大。下面的例子分别加载JSON、JSON-Array、XML格式的数据及对应的PNG资源,然后创建SpriteSheet实例。
package {            import com.as3game.asset.AssetM            import com.as3game.spritesheet.SpriteS            import com.as3game.spritesheet.vos.DataF            import flash.display.BitmapD            import flash.display.S            import flash.events.MouseE            import flash.filters.ColorMatrixF            import flash.text.TextF           
           /**             * ...             * @author Tylerzhu             */            public class TestSpriteSheet extends Sprite            {                       
                       public function TestSpriteSheet()                        {                                   SWFProfiler.init(stage, this);                                   AssetManager.getInstance().getGroupAssets(&spritesheets-json&, [&data/json/jsonformat.json&, &data/json/jsonformat.png&], onAnimLoaded);                                   AssetManager.getInstance().getGroupAssets(&spritesheets-xml&, [&data/xml/xmlformat.xml&, &data/xml/xmlformat.png&], onAnimLoadedXML);                                   AssetManager.getInstance().getGroupAssets(&spritesheets-jsonarray&, [&data/json-array/jsonarrayformat.json&, &data/json-array/jsonarrayformat.png&], onAnimLoadedJsonArray);                        }                       
                      
                       private function onAnimLoaded():void                        {                                   var bitmapData:BitmapData = AssetManager.getInstance().bulkLoader.getBitmapData(&data/json/jsonformat.png&);                                   var sheets:* = AssetManager.getInstance().getContent(&data/json/jsonformat.json&);                                   var sp:SpriteSheet = new SpriteSheet(bitmapData, sheets, DataFormat.FORMAT_JSON);                                   sp.setAction(&呼吸&, 14);                                   sp.play();                                   addChild(sp);                        }                       
                       private function onAnimLoadedXML():void                        {                                   var bitmapData:BitmapData = AssetManager.getInstance().bulkLoader.getBitmapData(&data/xml/xmlformat.png&);                                   var sheets:* = AssetManager.getInstance().getContent(&data/xml/xmlformat.xml&);                                   var sp:SpriteSheet = new SpriteSheet(bitmapData, sheets, DataFormat.FORMAT_XML);                                   sp.setAction(&呼吸&, 15);                                   sp.play();                                   sp.y = 150;                                   addChild(sp);                        }                       
                       private function onAnimLoadedJsonArray():void                        {                                   var bitmapData:BitmapData = AssetManager.getInstance().bulkLoader.getBitmapData(&data/json-array/jsonarrayformat.png&);                                   var sheets:* = AssetManager.getInstance().getContent(&data/json-array/jsonarrayformat.json&);                                   var sp:SpriteSheet = new SpriteSheet(bitmapData, sheets, DataFormat.FORMAT_JSON_ARRAY);                                   sp.setAction(&呼吸&, 15);                                   sp.play();                                   sp.y = 300;                                   addChild(sp);                        }            } }
   
与本文相关的文章cocos2dx动画Animation介绍
一、帧动画
你可以通过一系列图片文件,像如下这样,创建一个动画:
CCAnimation *animation = CCAnimation::create(); &
//从本地文件中加载图片文件到CCSpriteFrame中区,然后添加到CCAnimation中 &
for (int i = 1; i & 15; i++) &
& & char szImageFileName[128] = {0}; &
& & sprintf(szImageFileName, &Images/grossini_dance_%02d.png&, i); &
& & animation-&addSpriteFrameWithFileName(szImageFileName); & &
animation-&setDelayPerUnit(2.8f / 14.0f); // 这个动画包含14帧,将会持续2.8秒. &
animation-&setRestoreOriginalFrame(true); // 14帧播放完之后返回到第一帧 &
CCAnimate *action = CCAnimate::create(animation); &
sprite-&runAction(action); &// 运行精灵对象 &
注意CCAnimation是由许多精灵帧组成,可以设置间隔时间,持续时间等,它实际上是包含着一组数据。CCAnimate是一个动作,它是通过CCAnimation对象创建。
二、精灵表动画
尽管手工动画很容易理解,但它很少用在游戏开发中。相反的,精灵表动画的方式在2D动画中经常使用。
这是一个精灵表。它实际上就是一系列动画帧图片,或者是一个能用于一个场景的图片集。
在OpenGLES1.1阶段,精灵表因为以下几点被广泛应用:
1、减少文件读写时间。读取一张图片比读取一堆小文件肯定要快。
2、减少内存消耗。OpenGL ES 1.1仅仅能够使用2的几次方大小的图片(也就是宽度或者高度是2,4,864,128,256,512,1024,...)。也就是说,OpenGL ES1.1将会分配给每个图片2的几次方大小的内存空间,即使你这张图片达不到这样的宽度和高度也会分配大于此图片的2的n次方大小的空间。那么运用这种图片集的方式将会减少内存碎片。
3、减少OpenGL ES绘制调用并且加速渲染。
Cocos2d-x v2.0升级到了OpenGL ES2.0.OpenGL ES2.0不会再分配2的几次方的内存块了,但是减少读取时间和绘制调用的优势依然存在。
那么生成的动画效果如何呢?正如我们所见,精灵表不是动画的一个必须条件。但是考虑到以上的一些优势,精灵表还是很有效率的。在Cocos2dx中,有许多不同的方式来创建精灵表。
三、通过.png 和 .plist文件创建精灵表
在cocos2dx 0.x和1.x版本中,CCSpriteSheet就是为以上的目的设计。在V2.0中CCSpriteBatchNode替代了CCSpriteSheet。
CCSpriteBatchNode对象包含了所有精灵帧的图片纹理。即使它不会绘制,你也必须要把它添加到场景中,例如:
CCSpriteBatchNode* spritebatch = CCSpriteBatchNode::create(&animations/grossini.png&); &
下一步,你需要运用CCSpriteFrameCache实例来确保帧名字对应帧边界。也就是说,图片在哪一块矩形区域中。例如:
CCSpriteFrameCache* cache = CCSpriteFrameCache::sharedSpriteFrameCache(); &
cache-&addSpriteFramesWithFile(&animations/grossini.plist&); &
一旦你的精灵表和帧加载完成,并且精灵表已经被添加到了场景中,你可以通过createWithSpriteFrameName方法来创建精灵。并且通过addChild要添加到精灵表中:
m_pSprite1 = CCSprite::createWithSpriteFrameName(&grossini_dance_01.png&); &
spritebatch-&addChild(m_pSprite1); &
addChild(spritebatch); &
createWithSpriteFrameName 方法将会从grossini.plist中找到对应的坐标以及矩形区域,之后再裁剪grossini.png的纹理成一个精灵帧。
现在我们创建一个CCArray对象并且天剑所有的帧动画进去。在这个动画的例子中,我们发现所有的14帧都有相同的大小,所以我们可以用一个嵌套的循环遍历它们,并且当完成添加14帧之后结束掉循环。
CCArray* animFrames = CCArray::createWithCapacity(15); &
char str[100] = {0}; &
for(int i = 1; i & 15; i++) &&
& & sprintf(str, &grossini_dance_%02d.png&, i); &
& & CCSpriteFrame* frame = cache-&spriteFrameByName( str ); &
& & animFrames-&addObject(frame); &
最后,我们需要创建一个CCAnimate动作实例来运行CCSprite。下面我们可以在CCRepeatForever动作中包裹CCAnimate动作来让它一直执行下去,像这样:
CCAnimation* animation = CCAnimation::createWithSpriteFrames(animFrames, 0.3f); &
m_pSprite1-&runAction( CCRepeatForever::create( CCAnimate::create(animation) ) ); &
四、文件动画
CCAnimateCache能够加载一个描述一批节点的xml/plist文件,包括帧名和他们的矩形区域。这个借口非常容易使用。
CCAnimationCache *cache = CCAnimationCache::sharedAnimationCache(); // 缓存在cocos2dx中一直是单例模式 &
cache-&addAnimationsWithFile(&animations/animations-2.plist&); &
CCAnimation animation = cache-&animationByName(&dance_1&); &
CCAnimate animate = CCAnimate::create(animation); &
sprite-&runAction(animate); &
(window.slotbydup=window.slotbydup || []).push({
id: '2467140',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467141',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467143',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467148',
container: s,
size: '1000,90',
display: 'inlay-fix'