2010年7月5日 星期一

使用Embed來加速Flash編譯,並結合到Progression 4專案裡

2011/3/7更新

開發Flash網站時,需要不斷的修改-發佈來測試程式碼,而使用Flash IDE來發佈的話,每次Ctrl+Enter按下去就必須把元件與程式碼都一起重新編譯一次。在CS3以前,除了內嵌影片,這樣發佈倒還很快。但到了CS4,甚至到了CS5,這樣反覆的發佈可能會把開發者逼瘋,因為實在太~慢~啦~。為了加快開發的速度,這陣子找了不少資料,分析比較各種的方法,最後決定採用Embed的方式,將編譯好的元件「嵌進」程式碼,然後在FlashDevelop裡面發佈。在研究的過程中發現,其實沒有一個方法是十全十美的,每個方法都有優點及缺點,最後還是要依照自己開發的習慣來做取捨。以下先分析幾個我找到的方法,說明為何採用Embed,最後看看這方法如何結合到前面使用的Progression 4專案裡。

試著找出使Flash IDE編譯緩慢的原因
人總是有惰性,總是不太願意做改變,會想繼續使用Flash IDE來發佈。某次測試中意外發現,在使用import的時候,不要使用"*"將某個package全部引用進來,這似乎會讓Flash編譯的時候花很多時間去找到底這個package底下有哪些class是真正用到的。養成習慣用到幾個class就import幾個。例如使用:
import flash.display.MovieClip;
而不要用:
import flash.display.*;
這樣的確加快了編譯的速度。

使用SWC將元件與程式碼先編譯好
SWC可以將做好的元件與程式碼包起來,讓其他程式直接引用,也可以放到Flash的component路徑裡面,就可以在component面板裡直接使用。很多網路上的文章,包含FlashDevelop的文件裡面都建議這種方法。我試著研究過,SWC要將程式碼與視覺元件結合,可以使用程式碼繼承視覺元件,或視覺元件繼承程式碼,或者程式碼用合成的方式把視覺元件加進來。以AS3只能單一繼承來看,前兩種方式會讓我們少掉了一些自由度,第三種則可能要改變不少程式撰寫的習慣。而其中使用到的程式碼最好不要引用到太多其他的class,必須盡量只使用到Flash的基本class,如Sprite或MovieClip,這會讓想結合Progression或者使用自己開發的其他class變的相當複雜。而需要把一些程式碼包進去的主要原因是巢狀結構的元件。就我的理解,SWC是用在視覺元件與程式碼都幾乎不再更改的情形下,輸出成SWC方便重複使用,很多library除了source code,也都會提供SWC版供大家使用。而這其實跟我們一開始想要的:在開發過程中需要不斷修改程式碼然後快速發佈的需求似乎不是很符合,所以最後我放棄了這個方法。

使用Covert to Complied Clip
在Library面板的元件上按右鍵可以看到這個選項,就可以把視覺元件跟它連結的程式碼先Complie起來,這樣發佈的時候就會跳過這個元件,而這個元件的使用方法跟一般原件一樣。不過只要視覺或程式碼修改過,就必須再convert一次。它其實跟SWC差不多,所以也被我放棄了。

使用Embed
沒搞錯的話Embed是讓Flex加入一些圖片、聲音、SWF、SWF裡某個Symbol等用的。缺點是Embed進來後時間軸上的程式碼都不會保留,就連stop都沒有用。但這也相對是個優點:設計師可以先加一些簡單的程式碼在時間軸上,方便去測試動態效果,而當元件被開發者Embed進專案以後,這些測試用程式碼通通被忽略掉。另一個真正麻煩的問題是巢狀結構,這是Flash非常好用的一個優點,卻也是非常令人頭痛的一個缺點。我想一開始Embed的用法只考慮到獨立的Symbol,導致我們若要對巢狀結構的MovieClip兩層以上的地方下程式的話,會引發一些問題。第一是Flex SDK在Compile的時候會有快取的問題,可以參考Ticore這篇。第二是巢狀結構時,需要在parent的時間軸上宣告public參照時,宣告成程式碼的類別,來將內層的視覺元件轉成帶有程式碼的類別,後面再來解釋這如何做,而這就導致必須把巢狀結構的每一層都連結到一個類別,無形中可能就要多開好幾個類別了。不過綜合起來,Embed這方法是比較貼近我們想要的,設計師在Flash IDE裡先將視覺元件做好,發佈成SWF(別擔心,如果只有時間軸上一些基本的程式碼,即使是CS4發佈也很快),而開發者將這個SWF裡的Symbol Embed進程式碼,最後在FlashDevelop裡面使用Flex SDK編譯出網站真正要用的SWF。雖然是兩個步驟的編譯,但兩個步驟都很快,而且在分工上面也是很自然合理的。因此在衡量與取捨之後,決定採用這個方法。

前置作業
首先對平常使用Flash IDE發佈的人,得先介紹如何在FlashDevelop裡面利用Flex SDK來Compile專案。依照FlashDevelop首頁介紹,裝好Java 1.6 runtime (JRE)以及Flex SDK。Flex SDK下載解開後,在FlashDevelop裡的Tools->Program settings,打開後左邊選AS3Context,右邊的Flex SDK Location選好路徑。再來如果要在output視窗裡看到一些編譯的訊息以及trace的結果,要下載一下Flash Debugger,目前的版本要選擇Download the Windows Flash Player 10.1 Projector content debugger (EXE, 5.18 90 MB)這一個。下載完後可以將它放進Flash資料夾底下的Players資料夾裡面。然後進FlashDevelop裡面,一樣找Tools->Program settings,左邊選FlashViewer,右邊選External Player Path,然後選到Players路徑底下剛下載放進去的flashplayer_10_sa_debug.exe。FlashDevelop環境就設定到這邊,其它就屬於專案的設定了。

實際結合到Progression專案
我們就直接以之前的Progression 4 歡樂開發Flash網站範例來修改。首先再把架構圖貼一下:

FlashDevelop專案都有個.as3proj之類的專案檔,例如我們範例裡的MyProject_6.as3proj,我們先將它改名為Index.as3proj,點兩下打開專案。打開後選擇Project->Properties,把"No output, ..."取消掉,下面的輸出設定就會生效,可以注意一開始設定是發佈到bin-debug這個資料夾。最下面的Test Movie要選"Play in External Player"。都設定完後儲存。這個專案我們可以看到src/myproject/Index.as前面是一個綠色的Icon,這是當初progression專案自動幫我們設定的。在Project視窗的這個檔案上按右鍵,可以看到這個檔案的Always Compile是勾選的狀態,這表示專案在Compile的時候是以這個檔案為程式進入點。程式進入點的觀念在C#、JAVA裡面都有,Flash IDE發佈時其實也有,就是主時間軸或其對應的Document Class。所以Index.as自然就是我們預設的程式進入點。而FlashDevelop一個專案就只能發佈一個SWF,所以我們有幾個SWF要發佈,就要複製幾個專案檔出來。這邊注意所有程式原始碼都是共用的,不需要複製好幾份,只有剛剛的Index.as3proj檔案需要複製成Preloader.as3proj、About.as3proj、Product.as3proj,以及Contact.as3proj。然後每個檔案輪流打開,設定它Project->Properties裡面發佈出來的SWF檔名,還有在Project視窗裡,分別指定他們Always Compile的檔案為Preloader.as、About.as、Product.as以及Contact.as。這樣就完成專案檔的設定了。

接著我們開一個assets資產的資料夾,將所有的FLA檔案都先從src搬到這個資料夾裡面。然後打開FLA,先將Document Class的連結,例如myproject.Index等取消掉,以後新增專案則是這地方不需要設。另外發佈的路徑也直接改為這個資料夾底下發佈,因為FLA發佈出來的只是視覺元件,是assets資產,等著要被Embed進程式裡面,而不是最後網站的SWF。而Symbol的Linkage是等等要Embed時Symbol的名稱,因此仍然要填,我們保留之前依照package命名的規則不動,一來它已經連結不到程式(已搬到不同資料夾),二來方便我們之後對照程式碼時比較容易聯想在一起。Base Class都要改成MovieClip或Sprite,沒有辦法偷雞了。以前使用Flash IDE發佈,似乎視覺元件是主體,程式有點像是附加的;而現在程式架構是主體,視覺的Symbol則是assets等著被程式使用。

接著就進入到程式碼的修改了,而以後新開專案,這些修改則是新的程式撰寫習慣:
1.每個對應視覺元件的類別當然必須是可視物件,如MovieClip,或者繼承它們的子類別。以選單Navi.as為例,在類別宣告的前一行加上Embed語法:
[Embed (source = "../../../assets/index.swf", symbol = "myproject.index.Navi")]
public class Navi extends CastMovieClip {
語法說明一下:source是要Embed的SWF路徑,是從as檔案開始算起,例如我們從src/myproject/index/Navi.as往外三層,再進入到assets/index.swf。後面Symbol不加的話就會Embed整個index.swf,但這不是我們要的。Symbol要填的就是元件的Linkage,我們剛剛保留它的package完整名稱,可以跟程式類別比較容易聯想在一起,又可避免命名衝突。若是單一獨立的Symbol只要這樣加就可以了。
2.Navi底下有6個Button,在時間軸上我們幫他們加上instance name分別為index_mc、about_mc...等等。以前的做法,會在Navi.as裡面宣告跟這些instance name同名的public變數,當做這些元件的參照。其型別可能只用了MovieClip等基本類別,現在則必須將它改成實際程式碼的類別,例如IndexButton,AboutButton等。如此原本巢狀裡面只是單純的視覺元件,就可以指定為含有程式碼的類別。
3.IndexButton等類別還是要Embed相對應的Symbol才能完成程式碼的設定。
4.Flex SDK編譯時比較嚴謹,所有沒有return值的function都必須指定為void,否則編譯會出錯。
程式碼的修改只有這些,習慣後完全不成問題,只是必須撰寫的類別會多一點,而時間軸上也必須一一設定instance name。反過來想,若巢狀結構過於複雜,那可能要回過頭來跟設計師溝通,盡量去簡化,畢竟巢狀結構過於複雜也會導致效能降低。

最後來看發佈,前面設定都沒問題,在FlashDevelop裡按Ctrl+Enter應該就會直接編譯了,記得打開output面板看編譯訊息。至於重複發佈會有Ticore那篇提到的快取問題,我這邊不打算採用他的方法,不是不好,而是我覺得一般只寫AS的人恐怕沒幾個具備他那麼多方面的知識,若再沒看到他那篇,可能反而讓程式撰寫過於複雜,不知道為何要include那個檔案。我這邊用很笨的方法:快速切換專案來清除快取。因為FlashDevelop切換專案幾乎就是瞬間反應,加上移動滑鼠跟點擊.asproj檔的時間,每次發佈頂多多兩秒,一天發佈100次,也只多了3分多鐘。日積月累當然很多啦,不過這算是一個取捨,大家也可以依自己喜好去選擇。另外發佈有兩種,一種是Debug,一種是Release,在畫面中間上面有下拉選單可以選擇。開發時都選Debug就好,SWF會發佈到我們之前設的bin-debug資料夾底下。等開發完成要上線,再發佈release版,這時候會自動產生一個bin資料夾放SWF,再手動把html等其他檔案搬進去。另外debug/release的設定是每個專案不一樣的,也就是發佈每個SWF都要選一下喔。還有之前progression的release資料夾bin-release可以砍了。

最後,有兩個問題我還沒想通:第一,FlashDevelop發佈的Debug版檔案比較大,而FlashDevelop的Release版大小相當於Flash IDE發佈的Debug版,若使用之前提到的Progression Project面版上發佈Release版則更小,不知道有無方法也讓FlashDevelop發佈檔案達到最小。第二,切換Project來清快取的動作,或許可以從FlashDevelop的發佈設定去解決?若有熟悉FlashDevelop的高手知道方法的話請為我解惑,感激不盡。

以上兩個問題,第一個發佈Release版的方法,已經在Progression 4 歡樂開發Flash網站 ( 1 ) — 新增專案裡面補充完了。第二個問題,我現在通常會使用FlashDevelop快捷列上的Recent Projects,第一個應該會是現在開啟的專案,點一下就可以清快取了,清完旁邊Test Movie順便點下去測試。

以上介紹的方法不見得對每個人都是最好的方法,只是我個人針對自己習慣跟比較常面對的問題做出的取捨。事實上我覺得目前為止每種方法都不是最佳解法,希望Adobe以後能有更好的解決方案。

測試與下載
最後來看看結果:請按此
下載範例:請按此

5 則留言:

  1. 讚!!讚!!讚!!這篇寫得真是不錯!!!!一定要推一下!!!

    回覆刪除
  2. 測試是最辛苦的事,感謝分享~~~(讚!)

    回覆刪除
  3. Gray大,請問是否可以幫忙小弟將範例降成cs3,因小弟公司只有cs3版本,拜託><很想看一下的fla

    回覆刪除