改變專案結構
之前的用的範例,我們把IndexScene當作Root Scene,雖然很直覺,但是當我們想直接輸入網址進入到某個內頁,就必須把index的內容都讀取完,才能進入內頁。可以建一個MainScene當作Root Scene,而IndexScene則變成其中一個內頁。相對的,就有個Main.as當作CastDoument來建立progression。在這個檔案裡面,我們要加一些code,當網址上沒有指定要進入哪個Scene的話,要直接進入IndexScene(因為預設現在會進入MainScene了,而MainScene我們只會放選單、背景等共用元件,沒有其他內容)。修改這個檔案的atReady():
if (manager.syncedSceneId.equals(new SceneId("/main"))) { manager.goto( new SceneId("/main/index")); }else{ manager.goto( manager.syncedSceneId ); }manager.syncedSceneId可以抓到網址列上指定要進入的Scene的SceneId,SceneId有個equals方法可以去判斷是否相等,而"/main"是我們Root Scene的id,不會出現在網址列上。所以第一行的判斷式的意思是「如果沒有指定進入哪個內頁的話」,這時候我們希望進入IndexScene,所以接著我們就執行manager.goto,除了使用CastButton來切換Scene,就是用manager.goto或GOTO這個command來切換,裡面則丟入要前往的SceneId。else裡面處理的就是原來的進入網址同步的Scene裡面。
而MainScene的atSceneLoad()裡面就是加入各個子Scene,以及每個Scene都會用到的元件,如選單、背景等。
依要前往的哪個Scene來決定做哪些退場
在SceneObject裡面有atSceneGoto()與atSceneUnload()來做退場動作,但有時候網站比較複雜,需要依照要前往哪個Scene的不同來做不一樣的事情,可以使用manager.destinedSceneId來抓到要前往的SceneId,就可以用剛剛提到的equals方法來判斷要前往哪個Scene,例如:
if(manager.destinedSceneId.equals(new SceneId("/main/product"))) { //當前往ProductScene時要做的事情 }else{ //前往其他Scene要做的事情 }反之,可以用manager.departedSceneId來知道是從哪個Scene過來的。
取得Progression manager的參照
上面一直在用的manager是個Progression類別,在progression相關的類別,例如SceneObject,CastXXXX等,都有個manager可以取得它。如果我們在一般的類別也想取得它,例如我們做一個一般的button,click之後想跳到某個Scene,可以這樣用:
//需要import的package import jp.progression.getManagerById; import jp.progression.Progression; import jp.progression.scenes.SceneId; //... private function onClick(e:MouseEvent.CLICK):void { var manager:Progression = getManagerById("main"); if (manager != null) { manager.goto(new SceneId("/main/product")); } }使用getManagerById就可以取得progression的manager了,而為了怕在子Scene裡使用個別的progression,造成無法取得"main"這個manager(例如在子Scene是使用"about"這個progression),因此最好加上判斷是否為null,裡面的goto就不用解釋了。
取得CastObject的參照
假設我們有一個DisplayObject,例如選單,開放了一些public方法讓其他類別來呼叫。而在複雜的DisplayList裡面,最麻煩的就是取得這個DisplayObject的參照。一般的專案我會把這個DisplayObject的參照存入一個Singleton,需要的時候再從Singleton去取得,或者直接在Singleton裡開方法去執行。在Progression裡,有個比較快的方法,前提是這個DisplayObject必須是CastObject,然後設定它的id,例如我們把選單的設定成:
navi.id = "navi";然後當我們需要取得選單時:
//需要import的package import jp.progression.casts.getInstanceById; //... var navi:Navi = Navi(getInstanceById("navi")); if (navi!= null) { navi.publicMethod(); }getInstanceById會回傳DisplayObject,需要轉型。同樣最好判斷是否為null,排除無法取得參照的狀況。接著就可以去使用navi裡所定義的ublicMethod了。
取得Resource的參照
Progression在讀取外部資料,如圖片、SWF、聲音、等等有一些LoadXXX的command可以使用,用這類方法讀取完的資料會變成Resource類別存下來。好處是它們是command,可以排入command list裡面去依序處理,而當Resource存下來後,同一個檔案就只會去讀取一次,第二次嘗試去Load時,就會直接抓那個檔案的Resource。以讀取一個外部XML為例,假設我們希望一個Cast進場前先讀取XML資料,那麼在atCastAdded()裡:
loadListCommand = new LoadURL(new URLRequest("campaign_list.xml")); loadListCommand.addEventListener(ExecuteEvent.EXECUTE_COMPLETE, listLoaded); addCommand( loadListCommand, new DoTweener(this, { alpha:1, time:0.5 } ) );我們設定了一個loadListCommand去讀取外部XML,然後偵聽它的EXECUTE_COMPLETE事件去處理XML資料,也可以不用偵聽,改成在loadListCommand後面加個Func command去處理。然後才做進場動畫DoTweener,alpha值漸變為1。
而執行完loadListCommand的事件處理函式:
private function listLoaded(e:ExecuteEvent):void { var listXML:XML = XML(getResourceById("campaign_list.xml").data); //或 //var listXML:XML = getResourceById("campaign_list.xml").toXML); //或 //var listXML:XML = XML(this.latestData); //... }我們可以使用getResourceById("campaign_list.xml")去取得Resource,然後Resource.data就是我們要的資料,將它轉成XML;或者Resource.toXML轉成XML。也可以使用this.latestData去取得最後一個取得的Resource的data。而id則是我們去讀取的檔案的url。
當然這樣的機制有時候反而有弊。例如我們要去讀取的不是一個.xml檔,而是一個動態產生XML的後台程式,這樣Resource機制就只會在第一次讀到新資料,第二次以後就是直接抓上次的資料,而這不是我們要的。解決的方法有兩種:第一,不要讓這個Load command讀取的資料存成Resource,可以設定:
loadListCommand.cacheAsResource = false;這個參數預設是true,所以不想存成Resource的話就要設為false。這樣一來,就不會產生Resource,所以我們在抓取資料的地方要改成:
var listXML:XML = XML(e.target.data);直接從事件的target去取得data。前面三種取得Resource的方法就不能用了。
第二種方法是避免Resource Cache,要改設定:
loadListCommand.preventCache = true;如此一來,每次Load command都會去執行,重新產生Resource,取得資料的方法就是四種都可以。當然用getResourceById的方法就可以在其他元件裡快速取得資料,算是一種不錯的資料管理方式。
其他Instance參照取得方法
getSceneById可以取得SceneObject的參照,getCommandById可以取得有設定id的Command的參照。除了取得單一的instance以外,也有一系列的getXXXXsByGroup()來取得設定相同group屬性(Resource要設定resGroup)的instance陣列。總共有getManagersByGroup()、getInstancesByGroup()、getCommandsByGroup()、getScenesByGroup()、getResourcesByGroup()。
補充Load系列command如何去設定讀取完Resource resGroup的方法:
loadListCommand = new LoadURL(new URLRequest("campaign_list.xml"), {resGroup:"xml"});在Load系列command都會有一個initObject可以去設定屬性,位置通常是最後一個,但不一定是第二個。
想知道現在在哪個Scene
有些元件,例如選單、背景等,會是所有或者多個Scene都存在的,有時候會需要知道現在是哪個Scene,可以用manager.current取得現在的SceneObject,或用manager.currentSceneId取得現在Scene的SceneId。沒有manager的可以如前面所講的用getManagerById取得。
想知道C Scene是否為P Scene的子Scene
SceneObject有個contains方法可以檢查另一個SceneObject是否為自己的子Scene,有點像DisplayObject的contains方法,會回傳true or false。
if(pScene.contains(cScene)) { //... }如果自己檢查自己(例如pScene.contains(pScene))也會回傳true。
改變Log語系
之前介紹Progression時有提到,現在的訊息只有日文版是完整的。為了看到這些訊息,除了大費周章去改變作業系統語系,其實也可以在Progression去設定。打開CastDocument,先import:
import jp.nium.core.I18N.Locale;然後在建構式裡加上:
Locale.language = Locale.JAPANESE;javascript:void(0)
這樣就可以輸出日文訊息了,快把系統語系改回中文吧!!
就先記錄這些,有想到再來寫。
太讚了啦
回覆刪除提供一下自已的心得
回覆刪除如果 Progression 有設置 id 的物件
都可以使用 getXXXXById 得到實體
如果要 destory 該物件, 需要執行 xxx.dispose()
不然參照永遠都會在
如果要每次都強制 preventCache 的話, 可以直接改
LoadCommand.preventCache = true;
這樣只要是使用 LoadSWF , LoadURL 都可以不要有快取
LoadCommand.cacheAsResource 也可以透過靜態屬性一次改
To milkmidi:
回覆刪除Cool~!
太讚啦 感謝分享
回覆刪除改變log語系這個實在太威了!! 雖然還是日文,但至少有log可以google翻譯一下,不然不改日文語系的windows,log有跟沒有一樣 囧
回覆刪除感謝分享!!! :)
可以問一下,將元件拆出去後,會遇到2大問題:
回覆刪除1.對位問題
2.直接開swf測試卻看不到任何東西
想請問前輩有什麼好的解決方式嗎?
To 吸膠の男孩:
回覆刪除這系列的文章在介紹progression的用法,範例用意在幫助理解用,所以所謂的"將元件拆出去"這個我就不知道會發生啥問題了
對位作法跟一般flash的作法一樣,若你對於一般flash的對位作法有問題可以搜尋相關的文章
另外因為你"將元件拆出去",所以"直接開swf測試卻看不到任何東西"我也沒辦法知道是甚麼問題喔
To Gray:
回覆刪除太感謝了,問題解決了:P
對了,manager.destinedSceneId.equals(),裡面的形別是sceneId不是String
To 吸膠の男孩:
回覆刪除已修正,感謝你~
To Gray:
回覆刪除我將scene的樹狀結構改成如下圖示的結構後,遇到一個很怪的問題,de了好久到現在還是沒頭緒~~~>"<
root
|
------+-+-+-...
| | | |
home a b c
就是明明有設好manager.sync = true,在atReady也是有判斷式來決定manger.goto(),但我發佈後在broswer上看,它第一次開啟永遠只會進到/root/home,就算進入的網址早是http://aaa.com/#/a;我也在atReady打上了log來追manager.syncedSceneId,從vizzy上看到的log,不管網址怎打,它真的一開始就是/root,請問您有遇過嗎?
To 吸膠の男孩:
回覆刪除應該不會有問題才對
manager.sync = true
這行是放在manger.goto()的前面嗎?
可能要再檢查一下你的atReady裡面執行的東西
剛發現一開始的判斷式可以寫這樣
回覆刪除if(manager.syncedSceneId.equals(manager.root.sceneId))
用manager的root去比對就好了,感覺會更generic一點~~~
嗯,確定是放在goto前面,附上專案檔(可以直接發佈的版本,不需另外設lib path),可以幫我看看嗎?
回覆刪除http://www.box.net/files#/files/0/f/0/1/f_604439711
To 吸膠の男孩:
回覆刪除連過去沒有看到檔案耶
我的Main.as的atReady
override protected function atReady():void {
Debugger.addTarget( manager );
manager.sync = true;
Locale.language = Locale.JAPANESE;
if (manager.syncedSceneId.equals(new SceneId("/main"))) {
manager.goto( new SceneId("/main/index"));
}else{
manager.goto( manager.syncedSceneId );
}
}
其他要修改的就是在MainScene的atSceneLoad()加上下面的子Scene而已了
To Gray:
回覆刪除後來問題解決了,你的方法是正確的,沒問題~~~後來我拿同樣的檔案回家看,或是上傳到test server看就又正常了,感覺是Broswer或Flash發佈時有cache的樣子@@;