1. 程式人生 > >把組合(樹)模式整合到遊戲伺服器引擎中去!

把組合(樹)模式整合到遊戲伺服器引擎中去!

什麼是組合模式?

組合模式是一種設計模式,和物件組合的概念是不同的。物件組合是相對於物件派生而言的一種物件間協作的關係。而組合模式是將物件組合成樹形結構以表示整體-部分的層次結構,使得使用者對單個物件和組合物件的使用更加一致的一種設計模式。( compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.)

組合模式對於具有樹狀結構的物件進行整體-部分的管理時,具有特別明顯地作用。

可以看到當用戶訪問一個物件Component時,它會級聯訪問所有的Child(同時也是Component),直到每一層的所有物件都完成指定的Action。類似Unity的物件樹,就是採用了這種組合樹的模式。

常見的應用場景

資料夾結構的訪問。Action包括

  • 列印檔案列表
  • 設定訪問許可權,級聯設定
  • 刪除檔案

人事管理系統的設計。Action包括

  • 列印組織架構圖
  • 部門考勤/分組考勤

這裡應用組合設計模式時,有兩個主要的共同點

  1. 樹狀結構
  2. 級聯呼叫

遊戲引擎對組合模式的依賴

在我之前的部落格(links:口水遊戲主迴圈和元件)中,我講到了遊戲架構中,我們需要協同很多元件一起來完成複雜的遊戲邏輯。而且元件層次很深,比如玩家有很多平行的元件,揹包,任務,戰鬥,而元件又有自己的元件,比如揹包有整理元件Sorter,任務有元件主線任務,日常任務。於是,我們面臨了2個問題,找到我要的元件在哪一層的哪個位置,以及如果有一些共有的邏輯,該如何去更好的管理和維護。

於是,我們考慮,是不是可以引入元件模式,更好的為伺服器架構來做一些服務。

class LogicComponent
{
    private List<LogicComponent> childList;
    public void Add(LogicComponent item);
    public void Remove(LogicComponent item);


    public int Id { get;set }
    public int Name { get;set }
    public bool Enable { get;set }

    private List<IService> serviceList;
    public T GetService<T>(string name);
    public void SendMsg(string func, string msg);
}

我們嘗試把這種元件的關係,定義成一顆LogicComponent樹,它本身不提供具化的功能,而僅僅扮演著架構枝幹的功能。具體的工作則交給IService的物件來完成。比如IService可以是BagService,QuestService等等。

這種情況下明顯的區別是,我們訪問節點的方式變成了,Player.Root.GetService<BagService>(),而不是之前的Player.BagService。直接的好處是,我們解了物件之間的耦合,壞處是效能可能會變差(但關鍵的高頻訪問函式其實我們可以通過直接訪問的方式來提高效果)。那再讓我們看看,除了這些改變,我們還能獲得些什麼。

其他功能

模組開關

我們可以按需設定每一個元件的開啟條件,如果出現玩家非法的訪問(等級不到10級,卻去pvp了),底層框架處就可以直接處理了。並且,因為每個元件知道自己的子元件是誰,完成級聯的開關設定也會變得非常容易。

模組查詢

通過GetService方法就可以找到自己要的服務,而他在哪一個級別,根本無所謂。這種設計方式,對元件的隱藏性支援非常好,元件再多層次再複雜,對外部訪問人員來說也是毫無區別。

公共事件觸發

公有行為的呼叫,比如Update,Init,NewDay,等事件的觸發訊息,就可以直接通過組合樹的根節點發送下去,變得非常方便。

除錯路徑和效能

可以把一次請求訪問的所有模組的路徑打印出來,比如PlayerItem->PlayerPeople->PlayerItem->PlayerQuest。我們可以統計出一個模組的訪問次數等效能引數。這個功能還是非常酷的,也是老的模式下無法想象的。

直接呼叫和訊息傳遞模式

我們有了2種物件間呼叫的方式,一種通過GetService找服務,這可能是一種相對實時的操作。一種是SendMsg的操作,可以理解為傳遞了一個資訊,但可以延遲處理(比如更新了排行榜,傳遞了一條訊息個日誌模組)。因為SendMsg是同時對所有子物件遞迴呼叫的,如果我們對元件PlayerQuest傳送Msg("invoke", "123"),這時我們可以通過PlayerQuest的子元件LogResender來收集這個Msg,並完成其他的監控操作。