ET框架的ECS架構是從ECS原生設計思想變形而來的(關於ECS架構的分析可以參考跳轉連結:《ECS架構分析》),其特點是:
ET框架是基於U3D的,它的Entity和System的有點像GameObject的資料和函數的拆分:Entity有GameObject的元件式設計、父子巢狀、序列化等,而函數則被分成了多個系統(或者說事件響應函數),即被分成了Awake、Start、Update等等多個事件。和GameObject一樣,這套ECS是ET使用者開發者開發業務的「基石」。
Entity繼承了IDisposable,在刪除的時候必須呼叫Dispose方法,以防非託管資源洩露
Scene是一個特殊的Entity,Entity是具有父子巢狀結構的,可以形成樹形結構,而Scene則被定義樹的根,它可以(注意是可以)沒有父節點,其他普通的(例如單例可能是例外)Entity必須有父節點或者作為元件掛載在Entity上。通過Scene來維護一棵Entity樹。
指向Entity所在的那棵樹的根節點,是指下述層次中的ZoneScene
Scene的Id,在伺服器端作為區服的索引id
先來看看常見的使用者端模組生命週期管理分層:
ET的使用者端和上述類似,有:
在伺服器上,則不太一樣:
ET框架的ECS架構的System,其最明顯的特徵它是響應式的,說是System,感覺更像是平時用的EventHandle事件處理常式
引述官方檔案的介紹:
ECS最重要的特性一是資料跟邏輯分離,二是資料驅動邏輯。什麼是資料驅動邏輯呢?不太好理解,我們舉個例子:
一個moba遊戲,英雄都有血條,血條會在人物頭上顯示,也會在左上方頭像UI上顯示。這時候伺服器端發來一個扣血訊息。我們怎麼處理這個訊息?第一種方法,在訊息處理常式中修改英雄的血數值,修改頭像上血條顯示,同時修改頭像UI的血條。這種方式很明顯造成了模組間的耦合。第二種方法,扣血訊息處理常式中只是改變血值,血值的改變丟擲一個hpchange的事件,人物頭像模組跟UI模組都訂閱血值改變事件,在訂閱的方法中分別處理自己的邏輯,這樣各個模組負責自己的邏輯,沒有耦合。
這裡的事件機制被賦予了更多的意義,也就是ECS的System的核心意義:使業務更加的內聚,感知不到多個元件聚合在Entity中帶來的耦合。
正規化為:
public class AAABBBSystem: BBBSystem<AAA>
{
public override void BBB(AAA aaa)
{
}
}
/*
- AAA是Entity的派生類
- BBB是ET框架內建的一些事件名,如Awake、Start、Update...
- 事件的丟擲可以帶N個引數,通過泛型處理的, 類似上述片段變形BBBSystem<AAA, Param1>這樣
例如型別為Player的Entity的Awake事件訂閱處理:
*/
public class PlayerAwakeSystem: AwakeSystem<Player>
{
public override void Awake(Player self)
{
//DoSomething
}
}
引述官方範例:
int oldhp = 10;
int newhp = 5;
// 丟擲hp改變事件
Game.EventSystem.Run("HpChange", oldhp, newhp);
// UI訂閱hp改變事件
[Event("HpChange")]
public class HpChange_ShowUI: AEvent<int, int>
{
public override void Run(int a, int b)
{
throw new NotImplementedException();
}
}
// 模型頭頂血條模組也訂閱hp改變事件
[Event("HpChange")]
public class HpChange_ModelHeadChange: AEvent<int, int>
{
public override void Run(int a, int b)
{
throw new NotImplementedException();
}
}
可以看到:
引述官方範例:
除此之外還有很多事件,例如訊息事件。訊息事件使用MessageHandler來宣告,可以帶引數指定哪種伺服器需要訂閱,更具體的訊息事件可以參考訊息模組。
[MessageHandler(AppType.Gate)]
public class C2G_LoginGateHandler : AMRpcHandler<C2G_LoginGate, G2C_LoginGate>
{
protected override void Run(Session session, C2G_LoginGate message, Action<G2C_LoginGate> reply)
{
G2C_LoginGate response = new G2C_LoginGate();
reply(response);
}
}
元件的組裝可以封裝起來,比如工廠模式,這裡只是示意
// 首先得有一個父節點
Entity parent = XXX
Human human = parent.AddChild<Human>();
Head head = human.AddComponent<Head>();
head.AddComponent<Eye>();
head.AddComponent<Mouse>();
head.AddComponent<Nose>();
head.AddComponent<Ear>();
class Eye: Entity
{
public string Color { get; set; }
}
// 訂閱Eye的Awake事件處理(AddComponent時丟擲的)
public class EyeAwakeSystem: AwakeSystem<Eye>
{
public override void Awake(Eye self)
{
self.Color = "Black";
}
}
// ...