2010年7月24日 星期六

改善向量動畫造成的效能問題 - AniClip類別


這並不是什麼新概念也不是新東西,而且大家也都知道點陣圖對cup造成的負擔比向量圖來得少。
簡單的說,就是將MovieClip裡每一個影格的畫面都事先用bitmapdata畫起來,再依序播放,這樣雖然吃記憶體,但是很多時候可以減輕cpu的loading,讓整個網站跑起來不會卡卡的。


最早之前是看到bytearray部落格上發表的Banana Slice元件(http://www.bytearray.org/?p=117),真的很久了,兩年前了。當時由於自己本身十分不喜歡用元件的關係,所以想說有空自己再來寫一個類別。結果,人嘛…惰性,時間久了,工作上遇到相關的問題時,也都想辦法先用別的方式解決掉,就一直沒有要把它寫一個真正的類別檔出來。



這兩天比較有空了,也想起了這個東西,就想說,以後遲早要處理這東西的,所以就花時間寫了一下,寫了兩種版本,一種是可以讓現有的MovieClip直接繼承,只要在library裡設一下就好了,適合不需要太多code偏設計師使用的版本;另一種則是寫code去指定繪製某個MovieClip,比較適合純寫code時使用。

跟bytearray那一篇的文章一樣,還是要有對照組才看得出來效果,因此我也模仿弄了幾個demo用的swf。
首先是都沒經過處理的,一共放九個動畫在場景上,最後三個加上一些filter增加cpu的負擔。
點這裡去看看實際上跑出來的fps會是多少。

然後就是經過處理的,兩種版本我分別做了兩個demo檔,針對這九的動畫都改變了它們的播放速度(從慢到快)。
以下這個是AniClipExt版本(使用library繼承的版本):

看不到的可以點過去看:AniClipExtDemo.swf
原始檔下載: AniClipExtDemo.rar
另一個AniClip版本(純寫code的版本)跑起來的結果差不多,所以就不放了,有興趣的可以下載原始檔回去: AniClipDemo.rar

對照一下應該可以看得出來效能上的差別,至少在我的電腦上跑起來是有差,沒處理過的fps大約跑到40,有處理過的fps會拉高到約60。
如果是更複雜的動畫,效果應該會更明顯。(請記得若要比較要就是都用standalone flash player開啟,或是分別放到瀏覽器裡看,基準點才是一樣的,因為standalone flash player跑起來效能會比較好)



接下來是一些心得:

1. 由於是將每個影格都畫起來,因此如果MovieClip裡面還有其它的Children是動畫或按鈕,都會失效,所以這類別只能用來專門處理非巢狀式的動畫。

2. 因為都畫成點陣圖了,因此原本的hitArea將會變成點陣圖的範圍(方形),因此如果要針對這個動畫加上滑鼠事件,要小心hitArea的問題。

3. 有些動畫由於是用flash內建的繪圖工具畫的,線條的粗細不會被判別到實際的寬高裡,所以有可能會早成畫出來的圖有被邊緣切到的感覺,因此我只好大略設了一個「邊框」的變數,會多往外繪製4pixel的範圍,如果覺得4pixel還不夠的朋友請自己去類別檔裡去修改_border這個變數值。

4. 由於有些設計師在繪製動畫時會畫到x跟y是負值的地方去,因此這部份我有做調整,動態偵測MovieClip的有效Rectangle,再去校正點陣圖該繪製及擺放的區域。AniClipExt由於是被display tree裡的東西直接繼承,因此就只好這樣,而AniClip類別則是可以在指定繪製時多帶入一個Rectangle變數去指定要繪製的區域,確保可以將想要繪製的區域都畫到。

5. Bitmapdata在繪圖時會暫時耗去很多cpu的資源,因此若是動畫的範圍過大,可能一開始會有短暫的cpu衝高現象,這很難避免,我也設定了若是動畫的影格數超過20的話,每畫20個frame就會休息100 ms再去繪製接下來的20個影格,希望能避免cpu一下子處理不來造成的不良結果。

大概就是這樣。

PS:動畫部份是出自於Titan大師手筆,我只是借用。

---------------------
版本更新:
1. 2010/07/29 : 修正了一些bug,增加了改變draw細致度的功能setDetail method。
有需要的朋友請重新下載,前一版的在某些情況下會發生Bug。



It's no new idea or concept, since you all know bitmap animations cost less cpu than vector ones in flash.
To improve the efficiency is to simply draw every frame of the movieclip first, and then put the right bitmapdata to stage. Although it would need more memory but somehow can reduce the loading of cpu, the flash animation will run smoothly.


It came to me two years ago when I read the blog of ByteArray.org, who introduced his Banana Slice Component which is doing the same thing. Yes, two years ago. I was busy then and basically I personally really don't like to use component, so I decided to make one my own later some day. But you know, busy, and the thought was just a thought util now.

Sooner or later I have to face the efficiency of the animation, so I took one day to finish my classes. Two classes, one is for the movieclips which already exist in the stage. Just set the movieclips to extend the AniClipExt class in the library window and the class will automatically replace the frames into bitmapdata.
Another class is for programers. You can new a AniClip instance and asign which movieclip to be drawn.

As ByteArray.org, we need demos to show the efficiency difference between vector animations and bitmap ones. So the demos are below.
First is the original one. Just animations. I placed nine same vector animations to stage, and added some filters to the last three ones to make cpu cry louder.
Please click here to see the fps.

And here is AniClipExt demo. In this demo I additionally change the fps for each clip.

If you can't see the demo, please click here.AniClipExtDemo.swf
Download the source files:AniClipExtDemo.rar
AniClip version gets almost the same fps.
Download the AniClipDemo source files:AniClipDemo.rar

I believe you can see the difference between them. At least I got improvement on my pc. The original swf is around fps 40, and the fps is aound 60 after bitmapdata drawing.
The difference would be larger if the animation uses more complicated objects.


Below are some notes:

1. Because we draw every frame, the children of the movieclip will lose their properties or methods, if they are also animations or buttons.
2. The hitarea will be changed. Same reason, bitmapdata.
3. The linestyle thickness won't be count as the width or height of the movieclip, so I have to add one property named "_border" to draw larger area than the width/height values. You can change the value if you need.
4. Sometimes designers would make the animations to x < 0 or y < 0, so I had to get the really rectangle of the movieclip to make the bitmapdata to draw the entire area at runtime. But you can change the rectangle while you use AniClip.
5. Cpu may go high when the bitmapdata is drawing. It can't be solved yet. AniClip/AniClipExt class will draw 20 frames and then rest 100 ms for another 20 frames to prevent the overloading of cpu at the same time.

That's it.
Hope it can help.

PS: The animation of demos is by Titan Lee. Thanks to him.

---------------------
Updates:
1. 07/29/2010 : Fix some bugs, and add a new method "setDetail" to redraw the frames.
Please download the new one if you had downloaded the previous version.

沒有留言:

張貼留言