先談前端主架構,前端程式主架構有兩個主要任務:
1,要從架構高度合理劃分前端各模組,提出可行的實現方案;
2,從AS層級搭建程式架構(非文檔層級),制定前端編程規則和介面,規範程式各部分的職責劃分。這兩個任務其實包括很多具體工作,比如:遊戲啟動流程制定,確定哪些SWF檔案需要外部載入,那些功能可以從主程式剝離出去單獨實現,前端設定檔怎麼處理,公用素材怎麼處理,MVC三層怎麼劃分,主程式架構的選定,主程式怎麼和後台通訊,主程式如何與模組協作,哪些代碼應該放在主程式中,哪些代碼應該放在模組裡,主程式如何既能提供模組所需要的一切功能和資料,同時又相對模組自我保護等等等等。其實我談的還只是一些大的方面,具體到實現的層級,還有大量細節工作要做。而這些工作在項目啟動之初都是非常重要的,直接影響到項目中後期的開發和維護效率。
上面提到的那些點,我不可能全講一遍,不然就不叫“淺談FLASH WEB GAME”了!我只挑兩個比較核心的內容跟大家略做探討,就是前端AS架構和模組劃分的問題。先談前端架構:現在市面上流行很多前端架構,不管是針對“FLASH”的,“FLEX”的還是“通用的”都有。我們是否一定需要架構,或者必須使用某個架構,這完全是仁者見仁智者見智的事,從最終的結果上講,爭論這個問題意義不大,我相信一個5W行左右的項目,任何有5年以上編程經驗的人,不管用什麼作戰策略,最終都能攻下山頭,把項目做出來。但有一點至關重要:你必須能完全把握你的架構和你使用的架構,並能跟你的前端同事解釋清楚。那好壞架構的區別在哪裡呢?區別在於好的架構在開發過程中會更輕鬆,你不用天天擔心的你代碼,不用每天不停的寫文檔,以防止自己忘了複雜的邏輯,你可以在任何時間開始寫代碼,在任何時間去玩會兒遊戲然後回來接著寫;區別在於好的架構更符合業界標準,更容易被傳統和正統的程式員接受理解;區別在於你可以用很簡單的幾句話就把你的架構思想描述清楚,用幾個很簡單的文檔就能讓別人接手你的代碼,在人事變動和工作交接的時候讓自己更輕鬆;區別在於當你掌握了一種通用架構或者自己總結一套成熟的架構後,你幾乎可以套用以後的大部分項目,並不斷完善它,開發越來越輕鬆,速度卻越來越快!
我們的項目,主程式使用的是pureMVC架構,而主UI部分是自己寫的。主程式和主UI相互獨立,可以單獨編譯測試。主程式是純程式碼,用FLEX SDK編譯,而主UI則是介面和AS混寫並用FLASH編譯。這樣就把MVC中的V從物理層面上完全獨立了。pureMVC架構正如其名字,是一款“純粹”的MVC架構,在我看來,他只是幫我們實現了MVC的編程思想和套路,其它多餘的功能一點沒有,這使它具有更高的通用性,也是它最可愛的地方。根據我們的經驗,pureMVC單核心版就已經完全可以應對主程式有效代碼在10W行以下的項目了。但在我跟很多沒有用過架構的前端朋友聊天中,發現他們對這些架構本身就有抵觸心理,或者有些對MVC模式都理解的不深刻,用起MVC架構又怎能得心應手?還有一些更過分的朋友把自己的問題也歸結到架構上,說什麼用了pureMVC架構後,自己的項目編譯一下要十幾分鐘,我聽了之後哭笑不得,項目編譯慢一般是因為沒有合理劃分模組導致主程式過大才導致的,跟架構有什麼關係?如果因為大家的種種誤解和這些人的言論而導致一些新人錯過學習這麼一款優秀的架構,我覺得實為憾事!
pureMVC既然是一種MVC架構,這就意味著你首先要熟悉MVC。這種熟悉絕對不是對MVC的直譯:模型、視圖、控制器,而是要真正理解為什麼要把程式劃分成這幾部分,在劃分主程式模組時,要時刻能站在MVC的角度考慮問題,而當面對一段實際的代碼時,能快速準確的判斷,這段代碼應該放在MVC中的哪部分。《pureMVC最佳實務》這份短短几十頁的文檔中,可以說處處閃爍著MVC的思想火花,不但清楚地闡述了怎麼使用架構,而且時刻從MVC的角度告訴我們應該把哪些邏輯放在哪些部分中,應該注意什麼問題。這個文檔早已經有中文版,有興趣的朋友可以自己去看看,文中有的,我這裡就不贅述了。我只結合自己的體驗談一些文中可能沒有涉及的,也是在真正開發中才會碰到的問題。
1,模型部分在實際開發中除了儲存資料,還有其他作用嗎?是的,其實它的實際職責非常多。它要給Command和Mediator提供介面,響應使用者操作,進行資料操作或者請求遠端資料服務,進行資料的序列化和還原序列化,得到非同步資料後可能還要檢查資料合法化。但不管怎麼樣,它始終是在和資料打交道,同時也應該是你的主程式中唯一可以直接和資料打交道的管道,別的部分要想和資料有接觸,首先要問問它同意不同意。模型處理完資料會以Notification的訊息方式通知Command或者Mediator。但絕對不能在Proxy中直接調用Mediator,這是為了保證資料層的獨立性、可移植性和重用性,也簡化了你的架構思想。不過可移植性這個優勢,估計很多搞FLASH
WEB GAME的朋友暫時都沒啥機會體驗,呵呵。
2,Command,Command,Command!連叫三聲“Command”,希望可以引起大家的注意。因為Command的使用,在很大程度上反映著你對pureMVC架構的理解,甚至是對MVC模式的理解深度。在pureMVC架構中,各部分通訊是用Notification訊息,Proxy可以給Command和Mediator發訊息,Command可以給Command和Mediator發訊息,Mediator可以給Command和Mediator發訊息,怎麼樣?你現在是不是點暈了,這是正常的,其實我也有點暈!當你代碼寫到一定規模後,你會更暈。其實pureMVC架構這麼設計本來是為了讓MVC各部分盡量脫耦,但這帶來一個負面情況就是訊息發送與接收機制設計的太靈活了,靈活對小項目是好事,但對大項目來說,往往意味著混亂,甚至會導致災難。那怎麼辦呢?只能靠我們的自覺性自我約束,簡化架構思想了。根據《pureMVC最佳實務》中的建議,我的做法是這樣的,盡量使用Command,讓Command成為Mediator與Proxy之間通訊的唯一橋樑,Mediator和Proxy中發出的Notification,接收者一定是某個Command,然後再由Command處理並將結果轉寄給真正的訊息接收者,Command就算僅僅起一個轉寄作用,僅僅有不到10行代碼,也要建立一個Command類。這樣不僅使你的架構更加清晰,而且也更符合MVC思想,Command類的大量存在還使你架構的商務邏輯具有了更好的封裝性和擴充性,可謂是一箭三雕,何樂而不為?唯一的負面影響可能是你需要建立和維護更多的Command類檔案,但相對於優勢而言,這點影響不算啥。
3,我知道現在可能還有一些朋友在用FLASH IDE寫代碼,這些朋友的執著讓人欽佩,但我想任何一個熟練使用過FLEX BUDIER、FD或者FDT的朋友,都絕不會再回頭使用FLASH IDE寫代碼了。——不對啊?不是談pureMVC的嗎?怎麼扯到IDE上去了?這是因為我現在要討論的問題就和IDE有關,假如你現在用的還是FLASH IDE的話,除了隨時寫文檔外,我真的很難想出一個很好的方案可以讓你在沒文檔支撐的情況下,輕鬆掌握和隨時維護幾萬行代碼。可如果你使用的是FDT,就可以在沒有文檔的情況下,利用“ctrl
+ r”和“ctrl + 滑鼠左鍵”,以及全檔案搜尋等工具,瞬間搞清楚代碼之間的聯絡和邏輯,找出要修改的地方。OK,終於到pureMVC了。如果你使用的是FDT,並且開始嘗試使用pureMVC架構,可在使用的過程中,你發現你在寫主程式時,還是不停的使用“ctrl + 滑鼠左鍵”,而不是“ctrl + r”,這說明你必須重新審視你對pureMVC架構的理解了,請審查你的Mediator類,看裡面是不是充斥著大量的public方法,如果你的對象之間依舊是通過public方法進行引用,而不是通過Notification通訊的,那你也沒有必要繼續使用pureMVC架構了。
4,單例模式影響到底有多大?pureMVC是一個完全依賴單例模式的架構。單例模式似乎在AS界一直有很大爭議,這樣的話,pureMVC肯定也會有相應的爭議了。持反對意見的人,大多集中在“效能”和“團隊協作”方面,他們認為一個單例持有過多引用會帶來效能問題,而且生怕在團隊協作中自己的單例類被人無意修改,引發離奇的BUG。效能方面,我之前也沒做過10W以上的項目,不敢妄言,但10W行以下的項目,絕對沒有問題,如果你兩三萬行的架構就開始碰到主架構效能問題,估計十有八九是自己的代碼寫的有問題;團隊協作方面,我覺得pureMVC的Fa?ade模式是非常靈活好用的,大家可以略做討論,制定一個簡單的規則,比如模組只能通過fa?ade擷取資料和發送Notification,不能直接調用主程式其他CLASS,只要架構程式員不犯錯,模組程式員甚至連犯錯的機會都沒有,如果他們有,還是你的架構思路有問題,請繼續審視自己的代碼。反正單例模式的問題到底是什麼,我到現在也沒完全搞懂,主要是我們的項目沒碰到過此類問題,希望碰到過的朋友能再仔細跟火山說說,我也好弄清楚問題到底出在哪裡了,自己以後可以更好的避免此類問題發生。
額,架構部分先談上面4點吧,趕快進入下一個話題,模組劃分:模組劃分主要包括“核心模組劃分”和“子模組劃分”。核心模組的劃分思路是這樣的:它們是遊戲啟動所必須的,相互之間是緊密聯絡的,還要經常的被子模組調用;而相對的,子模組的劃分思路是:他們在遊戲啟動過程中不是必須的,可以在遊戲過程中再載入,子模組相互之間基本上完全沒有聯絡,一個子模組的增加和刪除不會影響到任何其他子模組,子模組可能需要調用主程式的介面或者獲得主程式的資料,但主程式絕對不應該依賴某個子模組。
明確了模組劃分思路再具體看看哪些部分應該劃分為核心模組,哪些部分應該劃分為子模組。一般情況下,核心模組按照遊戲啟動順序包括:一個殼子SWF → 設定檔包 → 登入註冊SWF → 主程式SWF → 主UI的SWF → 公用素材包。而子模組相對來說簡單很多,比如具體的某個小遊戲,某個情境,以及某個情境裡的觸發功能等等。下面我對核心模組逐一略做解釋。“一個殼子SWF”:這是一個體積很小,但意義很大的SWF;它後面總是跟著隨機變數,確保每次使用者載入的都是最新的;它裡面定義著一些需要經常更新而且每次更新都必須保證使用者也在第一時間就得到最新值的變數;它裡面最好有一個簡單背景圖,保證使用者在超低網速的時候輸入遊戲網址不至於長時間面對一片空白;它裡面有安全性原則的設定,是我們遊戲和很多第三方平台合作的基石;它裡面還定義著主程式被載入進來之前的遊戲啟動流程等等。“設定檔包”:核心模組版本號碼啊,全域文字說明啊,service介面定義啊,各個核心模組需要的配置資訊啊什麼的,一般是一些XML檔案。“登入註冊SWF”:這個簡單,在載入重量級的SWF前,先載入登入註冊SWF,可以保證使用者第一時間就能開啟登入註冊介面,而且可以有效節省伺服器頻寬。“主程式SWF”:這個就是我前面費了好大勁講的主程式部分了。“主UI”:主程式和主UI為什麼要分開兩個SWF,我前面已經提過了,後面還有說明,這裡暫時不講。“公用素材包”:公用素材包是一個遊戲不可缺少,但也不能過分依賴的東西。它包括一些全域的道具和效果,比如表情、技能特效、情境傳送門等等。公用素材包裡面最好就是一些簡單的動畫,體積小功能簡單,嚴禁在公用素材包裡添加上百K的東西,或者代碼上百行的小模組,公用素材包建議500K以下。
看了上面的講解,你可以能覺得核心模組分那麼多,太麻煩了。不錯,在我看來,對SWF載入流程的分解和控制,對非同步程式的掌控正是衡量一個AS程式員是否經驗豐富,是否足夠老道的重要指標,很多從其它語言轉到AS並有多年編程經驗的朋友,架構方面可能和AS程式員差不多,甚至比很多自學成才的AS程式員做的更好,但這方面往往不如長期與CPU和SWF體積搏鬥的老牌AS程式員。核心模組劃分的越合理,使用者體驗往往越好,後期編寫和維護代碼的效率會越高,但在前期會比較麻煩,而且對架構師的架構經驗和能力必須提出更高的要求。什麼都不分,主程式、素材、核心模組都弄在一個SWF裡,使用者一開始必須先下載完這個SWF,或者弄了一堆核心模組和超多公用素材,使用者一開始必須面對loading條不停的周而復始,必須等所有核心要素全部載入完成才能進行一些基本操作的做法,從架構角度上講,是最簡單的做法,因為不用過多考慮複雜的非同步和SWF拆分問題,但從使用者體驗和長遠的開發維護上講是非常不利的。根據我們的經驗,使用者登入前載入的所有資源體積應該控制在200K左右,而使用者進入遊戲主情境前,載入的資源總數應該控制在1M左右。還有前面提到過的那位用了pureMVC後項目編譯一下要十幾分鐘的朋友,估計就是把所有東西都弄到一個SWF裡的做法。主程式隨便改動測試一下,就要十幾分鐘,牽一髮而動全身,開發效率從何談起?根據我們的經驗,任何主程式、核心模組還有子模組的編譯,都必須在10秒以內,這才是合理的——我的機器是07年花了3000多買的戴爾品牌機。
→談完主架構,接著談主UI。主UI一般指主要的人機互動介面,這裡的主UI區分於主架構中的mediator,當你看過pureMVC文檔後,你就知道了,mediator只不過起到一個真正的V和pureMVC架構之間的橋樑作用,pureMVC裡的mediator其實並不實現什麼功能,真正的功能都是在主UI裡實現的。但主UI又不得不算是主程式的組成部分,因為它不像其他模組,基本上只需要調用主程式的介面就行了,本身並不需要對主程式提供介面。而主UI作為使用者操作介面,必須大量的向主程式的mediator提供介面,或者發送events。所以主程式和主UI之間的配合必須非常密切才行。
不同的遊戲類型,可以選擇的UI解決方案也不同。策略類非常適合用FLEX;MMORPG這類標準網遊,非常適合用ASWING;而像我們海底世界這類遊戲介面非常誇張,沒什麼標準規則,又不是太複雜的介面,還是適合自己開發。相信任何有過遊戲項目經驗的人都應該能理解,UI也是FLASH開發中的重頭戲,很多細節的處理非常麻煩,在項目早期具有很大的工作量。還是以我們的項目為例,我們的UI架構思路是這樣的:
1,所有的介面組件都是直接拖放在stage上的,其功能代碼大部分都是在發布時編譯的,基本上不用new的方式。這種方式的好處是方便編輯介面,從總體上直觀的把握所有的UI,減輕程式運行時的負擔,同時避免addToStage帶來的諸多問題。缺點是,當UI膨脹到一定規模時,可能會需要你有一台配置比較好的電腦——哎,說到這裡我就傷心啊,我那台玩魔獸效果全關還卡的電腦,一直陪伴我的整個UI開發曆程。
2,UI的FLA階層是這樣的:第一層是文檔類或者與UI主類關聯的某個MC,我們選用的是MC的方式,因為MC的方式更靈活;第二層是這個MC裡的所有組件,這些組件大部分是根據功能劃分在一起的一組元件,比如你的個人面板,而這個組件本身也是個MC;第三層是組件裡的所有元件或者共用組件,元件就是背景啊,按鈕啊什麼的,而共用組件比如捲軸啊翻頁組件啊什麼的;主要的就這三層,其實那些共用組件MC再往裡面雙擊還可以劃分一層。對應FLA的階層,AS的結構如下:文檔類或者主MC關聯的類是第一層,這個類裡持有所有的介面元件的引用;第二層是這些介面元件對應的組件CLASS,組件的功能都是在這裡實現的,比如個人面板的MC將會對應一個MyPanel的CLASS,這個CLASS裡實現MyPanel的所有功能。至於CLASS和元件之間是怎麼對應的,我用的是一種松耦合的代理模式,也就是將MyPanel對應的MC作為參數傳遞給MyPanel這個CLASS,而這個CLASS會有自己的私人變數記錄對應MC裡需要進行操作的元件,具體到功能實現時,從代碼層面上看,就好像CLASS操作的都是自己的私人變數,而不是直接操作介面元件,這樣,當介面元件修改名字時,CLASS的改動很小。而且這種代理模式可以實現一個CLASS代理不同的元件,當介面只是需要修改外觀,不需要修改功能時,非常方便。那麼這些CLASS是在哪裡初始化並獲得它要代理的MC呢?正是在主MC對應的UI主類中,比如當獲得MyPanel對應的MC後,就會立刻public
var myPanel:MyPanel = new MyPanel(myPanel_mc);當所有的組件註冊完成後,這個UI主類就持有了所有組件的引用,可以方便主程式調用;代碼的第三層其實就是共用組件,比較特殊的是,我的共用組件,比如捲軸,也是用代理模式寫的。
3,完全代理模式為我們創造了一種可能,就是把UI和UI對應的代碼分開編譯。這跟FLEX的皮膚更換機制有異曲同工之妙,只不過它的組件是要new出來的,布局是要代碼控制的,皮膚都是一個個CLASS,整體效果一般都要編譯後才能看出來;而我的組件是直接拖到舞台上的,布局大部分是直接在FLASH IDE裏手動布置好的,皮膚都是一個個命名過的MC,整體效果編譯之前基本上就能看出來。FLEX在更換皮膚的時候,CLASS名絕對不能變,而我的UI在更換皮膚時,MC的名字和階層不能變。FLEX關聯皮膚是在編譯時間完成的,而我的UI關聯皮膚是在運行時,當啟動程式載入完UI代碼的SWF和皮膚的SWF後,動態指定的。把皮膚和功能代碼分開編譯成兩個SWF有個好處,就是在實際開發過程中,我們會碰到有時候只需要修改代碼,而有時候只需要修改介面的情況,當然,就算你把代碼和介面一起編譯成一個SWF檔案了,也完全可以對應這種情況,無非是編譯一次的時間稍微長了一點點。可當你面對這樣的情況呢:某次遊戲版本更新出現狀況,需要你目前功能不變,但畫面必須退回到上一個版本。這時候你傻眼了吧?你開始對策劃們咆哮:“你們能不能想想好再讓我們做啊?”但你還是不得不重新開啟已經做好的UI,把裡面最新的介面再修改回老版本,同時還不敢把最新的刪了,因為下一個版本估計馬上又要替換回最新的畫面了。可如果你的皮膚和代碼是分開編譯成兩個SWF的,這種情況就簡單了,你可以讓營運從SVN上拉出上一個版本的皮膚SWF重新發布一下就好了,你所要做的只不過是動一下嘴皮。
4,最後談一下UI的資料層吧,UI是否需要資料層呢?答案是肯定的。儘管你可以從主程式那裡獲得任何你想要的資料,儘管大部分時間你只是需要資料來顯示一下而已,但UI自己記住某些資料會大大方便自己寫代碼。UI的資料層不需要主程式那麼複雜,每個組件有自己的資料變數,然後整個UI再有一個存放公用資料的地方就足夠了。
→談完主程式和主UI,最後再簡單談一下小遊戲、情境和模組。先說小遊戲吧,小遊戲是相對最獨立的一塊,可能只需要主程式提供使用者資料,並在遊戲結束後將分數發送給主程式就行了。所以這部分從管理的角度上來講是相對輕鬆的,但這不意味著小遊戲開發就簡單了,有時候,麻雀雖小五髒俱全,想開發出一個效能和使用者體驗俱佳的小遊戲絕非朝夕之功,要是碰到一些演算法複雜的小遊戲,那就有得頭痛了。其實對于海底世界這類兒童社區遊戲,小遊戲應該走創意和簡單路線,搞得太複雜了,既不好開發,小孩子又不一定玩得來。
相對於小遊戲,情境和模組就和主程式甚至是主UI關係密切了,但不管怎麼密切,大部分時候它們都是在所要資料,發送事件,或者觸發某個介面的顯示與隱藏。如果某個模組的修改需要經常波及到主程式,或者很多模組在做同一件事,重複寫著同一段代碼,這時候就必須重新審視架構,看是不是某些地方架構的不合理了,不合理的地方,只要時機允許,一定要儘快改掉,絕對不能放任自流,一塊兒小毒瘤最終可能引發癌症。模組和情境程式員在我們公司其實是非常累的,因為每周都需要發布新的版本,每次都很趕。在這種情況下,情境和模組程式員的責任心就非常重要,他們隨便哪裡隨意了一下,會直接導致糟糕的使用者體驗,因為大部分時間,使用者直接接觸的東西都是他們的作品。架構寫的再好,最終模組都做的很糟糕,對使用者來說沒有任何價值!所以,一個老道的,有責任心的,能夠快速開發出優良使用者體驗的AS模組程式員,完全有理由拿高薪,因為他們能做到的,一些所謂的純架構師未必做得到!