MMO游戲?qū)ο髮傩栽O(shè)計(jì)
Author: |
Kevin Lynx |
Date: |
5.2.2011 |
一般的MMORPG中,游戲?qū)ο笾饕ü治锖屯婕摇_@兩類(lèi)對(duì)象在經(jīng)過(guò)游戲性方面的不斷“進(jìn)化”后,其屬性數(shù)量及與之相關(guān)的邏輯往往會(huì)變得很巨大。如何將這一塊做得既不損失效率,又能保證結(jié)構(gòu)的靈活、清晰、可維護(hù)?本文將提供一種簡(jiǎn)單的結(jié)構(gòu)。
原始結(jié)構(gòu)
最原始的結(jié)構(gòu),極有可能為這樣:
Player: +---------------+
| property-1 |
+---------------+
| property-2 |
+---------------+
| ... |
+---------------+
| operator-1 |
+---------------+
| operator-2 |
+---------------+
| ... |
+---------------+
也就是,一個(gè)對(duì)象為一個(gè)C++類(lèi),然后里面直接塞滿了各種屬性名,然后是針對(duì)這個(gè)屬性的邏輯操作(函數(shù))。其結(jié)果就是Player成為巨類(lèi)。針對(duì)這個(gè)情況,一直以來(lái)我覺(jué)得可以使用一種簡(jiǎn)單的方法來(lái)拆分這個(gè)類(lèi)。冠以官腔,稱(chēng)之為Entity-Component-based Desgin。產(chǎn)生這種想法和我的個(gè)人技術(shù)積累有一定關(guān)系,見(jiàn)下文。
Policy-based Design
Policy-based Design,基于決策的設(shè)計(jì)。這個(gè)概念來(lái)源于<Modern C++ Design>。雖然這本書(shū)講述的是針對(duì)C++模板的使用及設(shè)計(jì)技巧。但這種思想依然被我潛意識(shí)般地用在其他地方。Policy大致來(lái)說(shuō)就是一個(gè)小的組件(Component)。它努力不依賴(lài)于其他東西,它可能就是個(gè)簡(jiǎn)單的類(lèi),它擁有極少的數(shù)據(jù)結(jié)構(gòu),及針對(duì)這些數(shù)據(jù)的極少操作接口。舉例而言,玩家MP的自動(dòng)回復(fù)功能,就可封裝為一個(gè)Policy。將許多Policy組合起來(lái),就可完成一個(gè)復(fù)雜的功能。
這種思想還可指導(dǎo)很多程序結(jié)構(gòu)方面的設(shè)計(jì)。例如在做功能的接口拆分時(shí),就將每個(gè)函數(shù)設(shè)計(jì)得足夠小,小到單純地完成一個(gè)功能。一個(gè)功能的入口函數(shù),就將之前實(shí)現(xiàn)的小函數(shù)全部組合起來(lái),然后共同完成功能點(diǎn)。
當(dāng)然,<Modern C++ Design>里的Policy在表現(xiàn)形式上有所不同。但其核心思想相同,主要體現(xiàn)在 組合 特點(diǎn)上。
Entity-Component-based Design
Entity-Component-based Design按照google到的文章,嚴(yán)格來(lái)說(shuō)算是與OOP完全不同的軟件設(shè)計(jì)方法。不過(guò)在這里它將按照我的意思重新被解釋。
如果說(shuō)Policy-based Design極大可能地影響著我們平時(shí)的細(xì)節(jié)編碼,那么Entity-Component則是直接對(duì)游戲?qū)ο蟮慕Y(jié)構(gòu)設(shè)計(jì)做直接的說(shuō)明。 一個(gè)游戲?qū)ο缶褪且粋€(gè)Entity。 Entity擁有很少的屬性,也許僅包含一個(gè)全局標(biāo)示的ID。 一個(gè)Component則是Entity的某個(gè)行為、或者說(shuō)某個(gè)組成部分。 其實(shí)說(shuō)白了,以玩家為例,一個(gè)玩家對(duì)象就是一個(gè)Entity,而一個(gè)MP的自動(dòng)回復(fù)功能就可被包裝為一個(gè)Component。這個(gè)Component可能包含若干與該功能相關(guān)的數(shù)據(jù),例如回復(fù)時(shí)間間隔,每次的回復(fù)量等。我們往玩家對(duì)象這個(gè)Entity添加各種Component,也就是給玩家添加各種邏輯功能。
但是,Component之間可能會(huì)涉及到交互,玩家對(duì)象之外的模塊可能也會(huì)與玩家內(nèi)的某個(gè)Component交互。子功能點(diǎn)的拆分,不得不涉及到更多的膠水代碼,這也算一種代價(jià)。
游戲?qū)ο髮傩栽O(shè)計(jì)
這份屬性結(jié)構(gòu)設(shè)計(jì),基本就是參考了上面提到的設(shè)計(jì)思想。整個(gè)系統(tǒng)有如下組件:
Entity: +-------------------+
| property-table |
+-------------------+
| component-table |
+-------------------+
Property: +-------------------+
| observer-list |
+-------------------+
Component: +--------------------+
| logic-related data |
+--------------------+
| logic-related func |
+--------------------+
意即,所有Entity都包含一個(gè)屬性表和組件表。這里的屬性表并非硬編碼的屬性數(shù)據(jù)成員集合,而是一個(gè)key-value形式的表。Property包含一個(gè)觀察者列表,其實(shí)就是一系列回調(diào)函數(shù),但是這些觀察者本質(zhì)上也是組件,后面會(huì)提到。Component正如上文描述,僅包含Component本身實(shí)現(xiàn)的功能所需要的數(shù)據(jù)和函數(shù)。整個(gè)結(jié)構(gòu)大致的代碼如下:
class Entity {
private:
GUID id;
std::map<std::string, IComponent*> components;
std::map<std::string, Property*> properties;
};
class Property {
private:
std::string name;
Value val;
std::vector<IComponent*> observers;
};
class IComponent {
public:
virtual bool Operate (const Args &args) { return false; }
virtual void OnNotify (const Property &property, const Args &args) {}
protected:
std::string name;
Entity *entity;
};
屬性本身是抽象的,這完全是因?yàn)槲覀儗傩越y(tǒng)一地放在了一個(gè)表里。從而又導(dǎo)致屬性的值也需要繼續(xù)閱讀