SpriteSheet精靈動畫引擎

吳秦發表於2014-02-26

SpriteSheet精靈動畫引擎

 

本文介紹FlashSpriteSheet精靈序列圖與其它渲染方式的效能對比、SpriteSheet的原理及注意實現,最後實現了一個精靈序列圖的渲染引擎。本文的SpriteSheet引擎及demo可以在github上下載:https://github.com/saylorzhu/SpriteSheet

動畫渲染效能對比

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:不同動畫渲染效率對比(具體測試資料與所使用資源有關)

渲染方式

描述

CPU

記憶體(KB

檔案大小(KB

時間軸

向量

12

6m

24

向量+cacheAsBitmap

12

6.2m

24

點陣圖

2

6.8m

534

點陣圖+匯出類

2

13m

537

點陣圖渲染

SpriteSheet精靈序列圖

1

11m

220 png + 31 json/xml

從上表可以得出,精靈序列圖消耗CPU最少,並且檔案大小適中,但記憶體消耗較大。對於遊戲來說,CPU銷燬越小,幀頻可以越大,遊戲越流暢。

clip_image002

1:精靈序列圖動畫效果及幀頻、記憶體資訊

可以看出使用精靈序列圖幀頻和記憶體都非常穩定,記憶體沒有頻繁的gcgc非常消耗cpu會造成遊戲卡頓現象。反觀其它渲染方式,會發現記憶體不穩定,這對遊戲效能是一個風險。

clip_image004

2:向量動畫

clip_image006

3:向量+cacheAsBitmap動畫

clip_image008

4:點陣圖動畫

clip_image010

5點陣圖+匯出類

下面詳細介紹下精靈序列圖的原理及注意事項。

精靈序列圖

SpriteSheet精靈序列圖是一種大的網格式點陣圖,其中每一格都對應著一個動畫截圖,每一動畫截圖對應動畫的一幀。精靈序列圖通常採用PNG格式,這樣可以使用Alpha通道。

clip_image011

6:角色受傷動畫序列圖

除了大點陣圖之後,還必須有一個對應的資料描述檔案,常用的格式有jsonjson-arrayxml。資料描述檔案,用來指定每幀動畫在大圖中的位置(偏移位置、寬、高等等),如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-blittingblit = Bit-Block Image Transfer)技術,它涉及到使用點陣圖來渲染最終的顯示效果。 將需要顯示的效果,畫素會繪製到一個已新增到舞臺上的點陣圖中。為了表現動畫效果,會在一個迴圈中更新點陣圖的畫素。關鍵步驟如下:

1)     載入動畫中需要的Sprite Sheet點陣圖資料(.png檔案)

2)     displayList中新增一個BitmapData目標點陣圖資料(畫布)

3)     向畫布複製或者擦除遊戲(copyPixel vs draw

4)     根據遊戲顯示層次順序將Sprite Sheet複製到畫布

5)     在遊戲迴圈中重複第34

copyPixel的效率比draw高,所以一般情況下,使用copyPixel複製影像到畫布。

記憶體佔用

在所有動畫渲染方式中,精靈序列圖對幀頻的影響是最小的。因為精靈序列圖會預先被快取到BitmapData例項中,這就可以使渲染速度變得更快。一定要隨時注意記憶體的佔用,仔細把控,詳盡規劃。精靈序列圖之所以效率高,是由於點陣圖序列都快取在記憶體中從而可被快速調取。但這樣也可能會導致巨大的記憶體開銷。

注意:一張圖片佔用多少記憶體只取決於影像的尺寸,而與影像檔案的型別和影像壓縮無關。

點陣圖所佔記憶體(位元組)= 點陣圖寬度 x 點陣圖高度 x 4

假設一幀的圖片的大小為200x400畫素,佔用記憶體312.5KB。如果一個動畫18幀,則佔用記憶體約5.5M。如果一個角色包含4個方向或4個動作的動畫,則佔用記憶體約22M。同屏線上10個角色,則佔用記憶體約220M

SpriteSheet工具

現在Flash cs6已經整合了將動畫匯出為SpriteSheet,如下圖所示:

clip_image012

7Flash cs6匯出SpriteSheet設定

TexturePacker也可以打包圖片為SpriteSheet格式。

精靈序列圖引擎

前面介紹了精靈序列圖的原理及注意事項,下面實現一個精靈序列圖動畫的引擎,支援Flash Cs6/TexturePacker匯出的JSONJSON-ArrayStarlingXML3種資料格式。

clip_image014

8:精靈序列圖引擎類圖

SpriteSheet

clip_image015

9SpriteSheet

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類表示圖片在大圖中的偏移位置(左上角為原點)和大小(未旋轉前)等等資訊,根據資料描述檔案生成。

clip_image016

10SpriteFra示意

JsonFormatJsonArrayFormatXmlFormat

SpriteSheet序列圖資料解析類,分別解析對應格式的描述資料。

Demo例項

SpriteSheet使用非常簡單,與原生Movieclip差異不大。下面的例子分別載入JSONJSON-ArrayXML格式的資料及對應的PNG資源,然後建立SpriteSheet例項。

Demo:

package

{

           import com.as3game.asset.AssetManager;

           import com.as3game.spritesheet.SpriteSheet;

           import com.as3game.spritesheet.vos.DataFormat;

           import flash.display.BitmapData;

           import flash.display.Sprite;

           import flash.events.MouseEvent;

           import flash.filters.ColorMatrixFilter;

           import flash.text.TextField;

          

           /**

            * ...

            * @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);

                       }

           }

}

 

 

相關文章