1. 程式人生 > >《爐石傳說》架構設計賞析(2):Scene管理

《爐石傳說》架構設計賞析(2):Scene管理

歡迎來的我的酒館,快來火爐旁暖暖你的靴子。哈哈,我們繼續欣賞爐石的程式碼。歡迎轉載,請註明作者【燕良@遊戲開發】及原文地址:http://blog.csdn.net/neil3d/article/details/39231541

上篇文章我們分析到SceneMgr處理了Scene的載入工作,今天我們主要分析一下爐石這款遊戲中一共有哪些Scene,他們各自負責什麼,以及它內部的邏輯、UI的處理方式。

在正式開始之前,我來對前文中提到的Scene切換再做一些補充分析。前文中我們看到SceneMgr是呼叫了“ Application.LoadLevelAdditiveAsync(this.sceneName);”,那記憶體中的東西豈不是越搞越多嗎?我們再仔細看一下SceneMgr:SwitchMode()函式,它是一個Coroutine,他主要進行了下面這幾個步驟的操作:

  1. 呼叫當前Scene的Scene:PreUnload()函式;
    傳送FireScenePreUnloadEvent事件;
    等待直到Unload過程走完(通過檢測LoadingScreen的階段);
  2. 呼叫Scene:Unload()函式;
    傳送FireScenePreUnloadEvent事件;
    呼叫成員函式PostUnloadCleanup()函式,它呼叫了兩個關鍵的函式:
    • 首先是成員函式:DestroyAllObjectsOnModeSwitch(),這個函式查詢到所有的GameObject(Object.FindObjectsOfType(typeof(GameObject))),然後進行了篩選(通過成員函式ShouldDestroyOnModeSwitch),除了一些全域性物件之外(主要是SceneMgr、PegUI、Box、DefLoader),全都刪除了(通過呼叫Object.DestroyImmediate())。
    • 然後呼叫了:Resources.UnloadUnusedAssets();
  3. 然後是呼叫前文提到過的成員函式:LoadModeFromModeSwitch(),進行了LoadLevelAdditiveAsync()操作;
綜上所述,爐石的Scene切換主要是包含兩步:1刪除所有非全域性物件,解除安裝未引用的Asset;2載入新的Scene。(我倒是想到另外一個土鱉一點的替代方案:建立一個完全空的scene,呼叫LoadLevel載入它,那麼所有沒有設定"DontDestroyOnLoad"的物件就都被刪除了。)
除了前文提到的Login,我們可以看到Scene還有很多派生類,詳見下圖:


這是我猜測的這些類和遊戲內容的對應關係,沒有太仔細分析,可能有些對應是錯誤的:

下面我們就挑選一個簡單的Scene來分析一下它的內部運作機制,我們來看一下AdventureScene吧。Adventure相關的Class很多,我們只做一個粗略的分析,只涉及到下面這幾個類和介面:

首先我推測,在Hub螢幕中點選中間的【Solo Adventure】(冒險模式)按鈕之後,通過我們前文分析的LoadScene流程,載入了一個冒險模式相關的Scene。它裡面有一個GameObject綁定了“AdventureScene”這個指令碼,我們可以看到AdventureScene:Awake()方法中主要是註冊了很多事件的回撥。 我們可以看到有一個“AdventrueSubScenes”列舉,它基本上對應了下圖中的按鈕:
public enum AdventureSubScenes
{
    Chooser,
    Practice,
    MissionDeckPicker,
    NormalHeroic,
    ClassChallenge
}


接下來還有一個"AdventureSubScene"是處理子場景對應的一些邏輯的。

我們看到有“AdventureChooserTray”這個類:

  • 我推測這個類就是用來處理上面這個遊戲畫面的UI互動操作的;
  • 這個類在Awake時,通過呼叫“CreateAdventureChooserButton()”方法建立了上圖中的上部分那幾個冒險遊戲內容模式相關的按鈕;
  • 這些按鈕都綁定了事件回撥:AdventureChooserTray.ButtonModeSelected();當這些按鈕被點選時,主要是呼叫:
    • AdventureConfig:SetSelectedAdventureMode(),此函式修改內部資料之後觸發事件:FireSelectedModeChangeEvent()
    • AdventureChooserTray通過OnSelectedModeChange()響應此事件,這也就是點選上面那幾個按鈕之後要做的一些處理:包括更新左側的畫面、設定Choose按鈕狀態等等;
      其中呼叫了PlayMakerFSM,主要是向其傳送事件“Burst”;通過這裡,我們可以確定爐石使用了PlayerMaker外掛。
    • AdventureScene也通過OnSelectedModeChanged()相應了此事件;
  • 它裡面還有一個“PlayButton m_ChooseButton”成員變數,並把為它添加了EventListener,用來呼叫ChangeSubScene()方法。這就和遊戲實際的操作對應上來:在上面選擇模式,然後點選下面的【Choose】按鈕,就進行到下一步的選擇了。
  • AdventureChooserTray:ChangeSubScene()通過Coroutine的方式呼叫了AdventureConfig:ChangeSubSceneToSelectedAdventure(),然後呼叫了AdventureConfig:ChangeSubScene();它主要觸發兩個事件:
    • FireSubSceneChangeEvent:AdventureScene通過OnSubSceneChange()函式響應它,主要是呼叫AdventureScene:LoadSubScene(),內部主要是呼叫AssetLoader.LoadUIScreen();
    • FireAdventureModeChangeEvent:AdventureScene通過OnAdventureModeChanged()響應它。
通過上面的分析,我們大致瞭解了上面這個遊戲截圖中的操作實現邏輯。 這次的分析算是一次熱身,接下來重點分析有兩個方面:
  • 遊戲邏輯的組織,特別是技能的資料、邏輯組織;這可能需要經過多次嘗試,慢慢接近;
  • 遊戲的Asset資源管理、載入機制;
OK,今天的分析就到這裡,歡迎大家拍磚。後續分析敬請期待! 順便來秀一下我的魚人部隊,別看這些1點學的小東西,加在一起還蠻歡樂的!