提升Flash效能的幾個注意事項

本文為看這篇文章之後的重點整理: http://www.adobe.com/devnet/flash/articles/efficiency-tips.html

這篇文章同時會對Flash開發程式師、動畫及美術有幫助。

提升效能

  1. 關掉沒有用到的物件的互動功能:
    減少畫面中active的物件能有效的增加效能,下面這段CODE可以關閉掉MovieClip和其他物件的所有互動關係。

    如果仍要與其他的元件做互動則使用mouseChildren = false;就好。
    fig01
  2. 判斷何時該用點陣圖(Bitmap)何時該用形狀(Shape):
    使用Bitmap:當今天圖片有許多漸層、複雜圖案、顏色很多時,用Bitmap較好。
    使用Shape:當今天的圖片是大塊色塊組成,感覺不利於縮放、但卻不需要縮放時,則使用Shape。(點陣圖按CTRL+B)fig02
  3. 將線段轉成色塊:線段所需要耗用的效能較多,因此可將不必要的線段轉換為填色。(在修改>形狀>將線段轉換成填色)
    fig03
  4. 避免使用虛線或點線,因為此種線段很耗費效能。
  5. 避免使用圓角,圓角比起直角的處理上耗效能。
  6. 避免圖像的坐標坐落位置帶有小數點,因為小數點的運算比起整數會耗費更多效能。

向量圖形最佳化

  1. 減少圖形中的節點(按修改>圖像>最佳化),或使用平滑工具來減少節點。
    fig06
  2. 避免使用漸層:漸層是非常耗效能的,若是必需要使用漸層,可在完成後,將其轉為Bitmap格式,或者以純色去取代。
  3. 不要使用不必要大小的bitmap圖片,或多餘的aplha值=0的透明色塊。因為這樣會增加所耗費的效能。
    fig08
  4. 避免使用遮罩:使用遮罩時,每次進入下一個影格都會重新繪製並產生裡面的元件,因此會耗費大量的效能。所以我們要盡量避免使用遮罩功能,若真的有需要,可以改用程式裡的scrollRect功能來取代遮罩,這樣在實務上會省下許多的資源,缺點是只能做方形遮罩。
    fig09
  5. 靜態物件避免使用濾鏡特效:因為濾鏡特效是每次重繪時都要重新計算一次特效顯示的值,若是今天是靜態物件要做濾鏡效果,可先在PHOTOSHOP內做好,便可節省濾鏡特效的運算時間。
    fig10
  6. 將陰影效果的品質調低
    fig12
  7. 假如圖片要resize,不要直接在flash裡改變圖片大小,而應該在外部先做好resize再loading進來。可以使用mipmapping 來計算圖片的最佳大小。
  8. 降低影格速度
    fig13
  9. 避免MovieClip呈現槽狀結構
    fig14
  10. 動畫目標物件可設置快取為點陣圖以節省效能,但此時要注意不要對該元件設置任何濾鏡,不然他每一個影格都會重新快取一次目標物件,這會耗費非常高的效能。
    2013-08-23_165042
    fig15
  11. 濾鏡特效的設定值設為2的指數(ex: 2 4 8 16….)
    fig16
  12. 如果只是非常簡單的動畫(如單純的位移事件),可以用程式碼去替代(如TweenLite),會更高彈性且節省效能。
    fig18
  13. 2.5D的CPU圖像模式的運算會很耗效能,此時可以用”快取為點陣圖”來製作2.5D的圖像。
  14. 觀察重繪區塊,去知道是那邊耗費較多的效能。(在FLASH DEBUG按右鍵=>顯示重繪區域),盡量讓重繪的區塊降到最低。
  15. 假如要設一個元件為不可視,記得停掉該元件,避免他在背後繼續執行(下stop)。
    fig22
  16. 避免元件出現在可視範圍外,若是需要將元件移除或移入視線,可以使用addChild()及removeChild()
  17. 除非有需要做特殊的文字效果,否則不要使用TLF text Field。

最佳化檔案素材大小

  1. 選擇適當的圖像壓縮比、非必要時,避免勾選”平滑化”的選項。
    2013-08-23_180249
  2. 考慮使用一整張的圖片或是用BITMAP填色:因為若顯示的區塊很小,但我們用一張超大張的圖片來做填色,這一大塊的圖片都會被包在SWF裡,因此若遇到這個狀況,可以用CTRL+B將點陣圖打散成形狀,將其縮小後,再將其轉回點陣圖,直接使用該圖片做為底色。
    但假如現況是某一張小圖片,會重複的出現在眾多區塊做為底色,那這時後就推薦用以BITMAP來填滿區塊的方式。
  3. 盡量避免在畫面中RESIZE圖片的大小
    fig27
  4. 假使是很單純、色塊及線條極少的狀況,可以將點陣圖轉為向量圖,這樣可以縮小SWF的體積。(修改>點陣圖>轉換為向量圖)
    2013-08-23_190251
  5. 一格格的動畫改為移動漸變動畫(這樣是省空間,但會耗效能,所以要取決於現在你是需要效能或者空間,去決定該用那種作法)。
    fig29
  6. 當個向量圖會在很多地方重覆出現時,將其轉換為SYMBOLS(圖像),這樣會可以在很多地方共用一個資源。
  7. 要嵌入字型時,只嵌入自己所需要的字碼範圍
    fig30
  8. 可在發佈設定的地方建立專案的描述檔去觀察所有元件的大小(右顛齒輪可新增描述檔)
    2013-08-23_190943
  9. 在發佈設定=>SWF=>進階的地方,把”包含XMP中繼資料”選項取消,可以減少一些檔案大小。
    2013-08-23_191205

優雅的開發FLASH(重點整理)

  1. 避免元件的所在位置帶有小數點
    fig32
  2. 考慮現在該用BITMAP或是SHAPE
  3. 文字字型選擇”消除鋸齒增加易讀性”
  4. 除非必要,不要讓文字變成可選擇的
    fig35
  5. 沒有需要移動漸變的影格不要設為移動漸變,可按右鍵的”移除補間動畫”來移除。
    fig36
    2013-08-23_191910
  6. 將元件的APLHA設為0時,他依然會接受滑鼠事件,會耗費處理滑鼠事件的效能。將元件的VISIBLE設為FALSE,元件仍然在畫面上。最好的方式是將其整個圖像完全刪除。
  7. 不要把元件壓在另一個元件之下,因為他還是會耗廢效能,把他的visible設為false或直接移除都可節省效能。

AS3的事件傳遞機制(Event、dispatchEvent及addEventListener)

當事件被發送出來之後。Event一般來説都會有一個Flow。 Flow分三個部分:

  1. Capture(捕獲階段)
  2. Targeting(目標階段)
  3. Bubbling(冒泡階段)

其關係圖如下:(來源為http://www.adobe.com/devnet/actionscript/articles/event_handling_as3.html)

efAzj

值得一提的是,在FLASH裡,只有和UI相關的物件,會有上圖所示的目標及補獲階段的事件流

一般像是Timer、Loader事件,是直接進入目標階段,並不會有上圖所示的事件流的流程。
在宣告Event時,也可以先指定事件是否要完整的跑完全部的流程。

宣告事件的語法及及參數意義如下:

參數說明(官方文件)

  1. type:String是要發送的事件的識別名稱EX:Event.COMPLETE
  2. bubbles:Boolean預設值是false,這個值是用來設定是否要有冒泡階段,如果傳false,代表他不會跑完全部的流程,只會從Capture到target階段就停止
  3. cancelable:Boolean預設值是false,這個值是用來設定是否這個事件可以被event.preventDefault();取消,如果設定為true的話,代表此事件是可以被取消的(稍候在dispatchEvent會更詳述這部份)。
    常用的可以取消的事件有(cancelable為true):   FocusEvent.MOUSE_FOCUS_CHANGEFocusEvent.KEY_FOCUS_CHANGETextEvent.TEXT_INPUT

而發送事件則是用這一段程式碼:

在發送事件時,要注意,假使今天我們是在STAGE裡面有一個box物件,那當我們用box.dispatchEvent(event);
即使事件是用box發送的,事件還是會從stage > root > box這樣跑。(請見上圖)
假如在建立Event時,bubbles設為true,那在上圖跑的流程為stage > root > box > root > stage
而若bubbles設為false,跑的流程則是stage > root > box

當我們用root.dispatchEvent(event);
bubbles設為true,上圖跑的流程為stage > root > stage
bubbles設為false,流程為stage > root

由此可知,事件流只會到目標(Target),就會停止往下傳
假如今天box裡面有一個物件button,若是用box.dispatchEvent(event)
button物件是不會接到事件流的。(因為到目標階段便會開始bubbles或停止)
並且假如今天root裡同時有box和box2兩個物件,
假使我們用box.dispatchEvent(event),則box2也不會接到事件。

至於第三個在建立Event的參數cancelable,不論值是true或false,都不會影響事件流的流程。
那他的作用是做什麼的呢?

我們可以發現,dispatchEvent有回傳一個布林值,cancelable就是在影響這個布林值的傳回參數。
今天我們假如要創建一個物件,但是要讓使用者有權力去阻止這個事件發生後的後續發展時,可以這樣寫

假使使用者在監聽的途中呼叫 event.preventDefault();,並且EVENT的cancelable為true時,dispatchEvent回傳的值就會是false,便不會執行trace("success!");那塊區塊。但假使EVENT的cancelable為false時,不論你在事件執行中有沒有呼叫event.preventDefault(),dispatchEvent回傳的值就會是true

一般我們會在傳遞事件的途中,去將事件攔截下來。增加監聽事件的程式碼如下

官網關於addEventListener的說明如下

addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void

傳入的參數意義如下:

  1. type(String):事件名稱
  2. listener(Function):要執行的Function
  3. useCapture(Boolean):這個和本篇要說的事件流有關,當今天傳入的值為true,則只能在補獲階段去抓取事件,在冒泡階段是聽不到的。如果將 useCapture 設置為 true,則偵聽器只在捕獲階段處理事件,而不在目標或冒泡階段處理事件。
    如果 useCapture 為 false,則偵聽器只在目標冒泡階段處理事件
    要在所有三個階段都偵聽事件,需註冊 addEventListener 兩次:一次將 useCapture 設置為 true,一次將 useCapture 設置為 false。當今天若useCapture為true時,並不會聽到target階段時的事件但若為false時,也可以聽到target階段時的事件

    假使今天我們用box.dispatchEvent(event);來發送事件,
    然後box的監聽事件的useCapture設為true。ex : box.addEventListener(MouseEvent.CLICK, rootClick, true);
    這樣rootClick事件並不會被呼叫到。

    若box的監聽事件的useCapture設為false 。ex : box.addEventListener(MouseEvent.CLICK, rootClick, false);
    這樣rootClick事件便被呼叫到。

  4. priority(int):若今天同時有很多個監聽器同時監聽同一事件,可用這個值來設定那一個監聽器應該被優先執行。數字愈高代表會愈快執行該事件偵聽程式。
  5. useWeakReference(Boolean):是否使用弱關連。若是用弱關連,當被監聽的物件所指到的變數被指向記憶體的其他地方,這個關係也會一併被取消(物件會被GC回收)。但如果傳進的值是false,當今天被監聽的物件的其他關連被指向別處時,該物件不會被GC回收,需要手動removeEventListener時,該物件才會被GC回收。

除了上述的幾個參數外,還有幾個函數可以中斷事件流的流程。
那就是stopPropagation()stopImmediatePropagation()

那這兩個函數有什麼差別呢?

下圖可以很清楚的解釋差異:
stopPropagation():會把同一階層的其他事件跑完才停止。
5MYe3

stopImmediatePropagation():立刻停止之後所有的事件流。
jleAX

假如今天box同時有兩個監聽函數如下

則由於priority值的關係,eventMouseDownHandler2會先被執行,eventMouseDownHandler1在之後才被呼叫
那若是在eventMouseDownHandler2裡面呼叫event.stopPropagation(),eventMouseDownHandler1還是會被執行,才將事件流中斷。
但是若在eventMouseDownHandler2裡面呼叫event.stopImmediatePropagation(),則eventMouseDownHandler1就不會被呼叫到

相關資源

  1. AS3.0的事件機制(詳細)
  2. AS3筆記-事件流(event flow)
  3. Event propagation
  4. Introduction to event handling in ActionScript 3.0

Flash時間軸運作相關注意點

遮罩相關:

當圖層在遮罩之下時,影格每次前進,所有的物件都會重新運算並重新產生

  1. 若有一個動態label在遮罩之下,若其原本的顯示的文字為”舊的值“,在影格1時,用script將其值設為”新的值“,當影格前進到下一個影格時,又會變回顯示”舊的值“(沒有關鍵影格也是)。
  2. 同理,如果我們在影格1時,在as裡指定某個MovieClip,當進入下一個影格時,原本在as指定的MovieClip會與當下畫面上的物件不同,而造成無法對其做操作的狀況,這時候應該要到某定點影格時,再在as裡對物件做指定的動作

關鍵影格相關:

每次重新經過一個關鍵影格,其物件都會重新產生。

  1. 若在影格1裡,有元件a b c,然後在影格2裡,有元件d e。當我們用as,在影格1裡指定a b c元件至某個變數裡,當時間軸跑到影格2,再跑回影格1。原本指定的變數裡面所存的影格,會是上一次影格1裡的元件,但會與現在在畫面上影格1顯示的元件不同
  2. 若某MovieClip的第1影格第5影格都是關鍵影格,在關鍵影格上的都有同樣的元件a b c。若我們在影格1時用AS指定元件abc,當影格跑到影格5時,畫面上所顯示的元件也會與影格1的元件不同(若沒關鍵影格的狀況下則會是相同的)。

效能相關:

  1. 形狀或顏色漸變,是每進入一次影格,便會整個圖層做重繪。因此他的漸變對象不能為元件,而需要是向量圖形。(耗效能)
  2. 傳統移動漸變,則是單純對元件的位置或大小做改變,元件本身不會重繪。因此對象一定要是元件。
  3. 重繪效能會與重繪區域大小有關,最耗效能的為向量漸層或色塊,因此若無必要,盡可能將元件改使用bitmap點陣形式。
  4. flv動畫在初始載入時會較花效能,但若是畫面上同時有多個動畫,則flv會較省效能。

AS3.0小數運算BUG

今天處理帶錢問題時,發現AS3的Number相減的一個問題
討論串如下
http://zengrong.net/post/1186.htm
http://blog.csdn.net/bill1315/article/details/8574109

因此在撰寫程式時,

應盡量避免直接將兩個帶小數點的NUMBER做運算
而應該以整數型態做運算,運算完後再做小數點的位移

或許對一些人而言這是已知的事
但還是希望能夠分享一些自己曾遇到的錯誤以避免大家再發生相同問題~

看下面的代碼:

簡單說,就是兩個一位小數相減,差為何不是一位小數?
經測試,乘法也有同樣的問題。

GOOGLE找到了Flex團隊的一個回覆:http://old.nabble.com/Float-number-calculation-in-AS3-td18447329.html

從回覆看,AS3的Number與JAVA或C++的double類似,採用二進制分數而非十進制分數保存浮點部分,因此會導致不夠精確。這是語言的設計思路所致。當然,從另一個思路看,它也是個BUG。

初探OSMF的plugs-in開發

這篇文章是我看完下面的網頁後,所寫的中文心得:
http://osmf.org/dev/osmf/OtherPDFs/osmf_plugin_dev_guide.pdf

建立一個plug-in的方式

1. 建立一個PluginInfo物件,並且寫一個下面的函數讓OSMF去取得PluginInfo的物件

2. 建立一個物件繼承PluginInfo,然後建立一個Vector.執行super(items)

ps: 在建立MediaFactoryItem時會傳入三個參數
第一個參數是一串string,這個MediaFactoryItem的id
第二個參數是一個函數,當有任何資源被指定給MediaFactory.createMediaElement時,這函數會被呼叫,
傳入被指定的資源,回傳一個布林值代表是否要呼叫第三個參數所傳入的函數

第三個參數也是一個函數,當有任何資源被指定給MediaFactory.createMediaElement且第二個參數傳回的值為true時會被呼叫
不會傳入東西,然後要傳回一個MediaElement

那建立plugin的方式有兩種:
1. 靜態建立(把plugin用includes進src裡或加進lib庫裡的方式去呼叫)

2. 動態建立(把plugin用載入swf檔的方式去呼叫)

下載osmf的資料夾裡面有一個sample,裡面有許多plugin的範例
可以研究其使用方式

Plug-in 種類有三種

OSMF簡介

OSMF是一個開源且免費的Action Script框架,用以建置多媒體播放器。

現今的播放器很多時候不止是播放器,它們可能需要連接至CDN、呈現廣告、補捉使用者事件給伺服器分析、在使用者介面上提供社交功能等…。每個功能可能都會需要該方面的專業人士去專門開發,而OSMF就是為了解決這個狀況,降低開發成本而產生的,在OSMF裡,開發人員所開發的播放器,可以透過一套標準的API,與第三方所開發的plug-in功能整合。

簡單來說,可能今天我的播放器想要與聊天功能整合、或特定事件引發特定廣告,我們可以用OSMF所提供的標準API去開發相關功能,第三方程式不需要關心播放器相關議題,只需透過OSMF所提供的API來做,以提高程式的重用性。

OSMF的開發核心思想是『hooks』,每一個插件都是可被動態加載的功能,可被宣告為動態或靜態載入。靜態載入是指編譯時就被編譯進去、而動態載入則是在播放時再動態載入SWF插件。

Plug-ins分成可視與非可視的元件,可視元件可在載入swf時覆蓋原本的使用者介面(或一個暫停按鈕),非可視元件可將使用者操作紀錄和分析資料傳送給分析伺服器。

在OSMF裡,plug-in是declarative(宣告性)的。plug-in的功能是經由OSMF的API去做宣告,framework只負責檢查插件的功能,然後建立插件與媒體播放器之間的關係。plug-in無法直接存取任何播放器的內容,而OSMF則是扮演著plug-in和media player之間的溝通者。這可以確定播放器和插件之間的溝通是有標準化的。

MediaFactory是處理media和plug-in之間的溝通者,開發player的developer用MediaFactory去載入plug-in並產生MediaElements。雖然我們也可以直接創建MediaElements,但使用MediaFactory產生的MediaElements可讓我們用載入的插件影響原來的輸出結果。

在as3裡取得函數呼叫者資訊

補充:這個方法只可以在flash player的debugger版本可用,
因為在一般非debugger的player無法用”e.getStackTrace()”來抓取歷史資訊

再補充:非debugger的版本只需在flex的compiler arguments裡加上

這串參數,就會可以正確的取得”e.getStackTrace()”的值了!

如果我們在某個函數兩要知道有關傳進的參數的相關資訊
可以使用arguments去取得

下面網址有介紹arguments的使用方式
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/arguments.html

但是過去在as2裡,arguments可以用caller去知道呼叫此函數的地方
在as3裡,卻將此一屬性拿掉了。
那我們在as3裡該如何知道是誰呼叫了這個函數呢?

我在一個別人的部落格裡找到一個別人寫好的解法
是先丟出一個錯誤訊息,然後去getStackTrace(),再去解析之前呼叫者的資訊

原始部落格在此
Looking up the calling function

原始碼如下:

AS3.0中三角函數的使用

先用Math.atan2()去求得圓的弧度
用法是弧度 = Math.atan2(x,y)
x與y指的是在坐標軸上形成三角型的那個點的x,y

然後再用弧度轉角度的公式去求出角度

//弧度轉角度
var arc:Number=1 //弧度
var angle=(arc*180)/Math.PI; //角度

//角度轉弧度
var angle:Number=30 //角度
var arc=angle/180*Math.PI; //弧度

另外Math.sin、Math.cos裡面要傳入的值,則是弧度
所以若我們要算sin60的值,需要這樣去代入
Math.sin(60*Math.PI/180);
那sin = 對邊/斜邊
所以要求出對邊就是 Math.sin(60*Math.PI/180) * 斜邊
其他的亦同。

語法結構:
Math.sin(數值或表達式);
Math.cos(數值或表達式);
Math.tan(數值或表達式);
Math.asin(數值或表達式);
Math.acos(數值或表達式);
Math.atan(數值或表達式);
Math.atan2(數值或表達式);
註:數值或表達式的結果值應介於-1和1之間。
範例:
myNum = Math.sin(60*Math.PI/180);
以表達式60*Math.PI/180的運算結果為參數求取正弦值並存入變量myNum中。
myNum = Math.cos(0.49);
以數值0.49為參數求取餘弦值並存入變量myNum中。
myNum = Math.tan(60*Math.PI/180);
以表達式60*Math.PI/180的運算結果為參數求取正切值並存入變量myNum中。
myNum = Math.asin(45*Math.PI/180);
以表達式45*Math.PI/180的運算結果為參數求取反正弦值並存入變量myNum中。
myNum = Math.acos(0..49);
以數值0.49為參數求取反餘弦值並存入變量myNum中。
myNum = Math.atan(30*Math.PI/180);
以表達式30*Math.PI/180的運算結果為參數求取反正切值並存入變量myNum中。
myNum = Math.atan2(30,50);
求取點50,30到X軸的角度並存入變量myNum中。

AS3.0中製作直式書寫的方式

在as3.0中有 FTE可針對文字去測量其寬度、高度等
針對個別的文字去做處理
相關官網的說明可見此
http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS6C0BB8ED-2805-467a-9C71-F9D757F33FB6.html

ElementFormat是定義文字的相關屬性(字型、大小、顏色等等)
然後再new一個TextElement設定文字格式和文字內容
再將其放到一個TextBlock裡面

TextBlock裡面則有許多相關的操作函數
可以對個別的文字做旋轉、定位等等
http://livedocs.adobe.com/flex/3_cn/langref/flash/text/engine/TextBlock.html
詳細的手冊說明在上面

裡面的lineRotation將角度設為九十度(TextRotation.ROTATE_90)時
文字就會呈現傳統的中文直式書寫方式呈現

下面的是將文字改為直式文字的範例程式