2009-04-15 97 views
1

我一直在腦海中敲打這個問題幾天,現在還沒有得到任何令人滿意的結論,所以我想我會問SO員工的意見。對於我的工作我使用的組件對象模型所描述的herehere遊戲。它實際上運行得很好,但是我目前的存儲解決方案已經變得非常有限(我只能通過他們的類名或任意的「家族」名稱來請求組件)。我想要的是能夠請求一個給定的類型並遍歷該類型的所有組件或從它派生的任何類型。分層組件存儲的結構

在考慮這個,我第一次實施了通過存儲的順序派生類型的基類的簡單RTTI方案。這意味着sprite的RTTI例如:component :: renderable :: sprite。這使我簡單地通過比較B的所有元素很容易地比較類型,查看是否是由B型派生類型A:即組分::渲染::子畫面是從組分::渲染但不組分::計時器的。簡單,有效,並已實施。

我現在想要的是組件存儲在表示層次結構中的生活方式。 ,想到的第一件事是使用類型爲節點樹,像這樣:

 component 
    /  \ 
    timer   renderable 
/   / \ 
shotTimer sprite  particle 

在我會存儲類型的所有組件的列表中的每個節點。這樣請求「component :: renderable」節點將允許我訪問所有可渲染組件,而不管派生類型如何。該擦的是,我希望能夠有一個迭代器訪問這些組件,這樣我就可以做這樣的事情:

for_each(renderable.begin(), renderable.end(), renderFunc); 

,並有從渲染下是遍歷整個樹。我有這個幾乎用真難看圖/矢量/樹節點結構和跟蹤的我一直在節點堆棧中的自定義前向迭代工作。雖然我一直在實施,但我覺得必須有更好,更清晰的方式......我只是想不出一個:(

所以問題是:我是否過度複雜化這是不必要的嗎?一些明顯的簡化我的思念,或預先存在的,我應該使用結構?或者這只是inheritly一個複雜的問題,我可能做就好了嗎?

感謝任何輸入你!

回答

1

您應該考慮您需要多久執行以下操作:

  • 遍歷樹
  • 添加/從樹上
  • 多少對象,你需要保持

哪個更頻繁的軌道上拆下元素將有助於確定最佳解決方案

也許來代替化妝一個複雜的樹,只是有一個所有類型的列表,並添加一個指向它的派生類型的對象的指針。事情是這樣的:

map<string,set<componenet *>> myTypeList 

那麼對於一個對象,它是類型的組件::渲染::精靈

myTypeList["component"].insert(&object); 
myTypeList["renderable"].insert(&object); 
myTypeList["sprite"].insert(&object); 

通過註冊多個列出每個obejct,它就變得很容易做到的東西全部給定類型的對象和亞型

for_each(myTypeList["renderable"].begin(),myTypeList["renderable"].end(),renderFunc); 

注意,標準::設置和我的std ::地圖結構未必是最佳選擇,這取決於你將如何使用它。

或許一種混合的方法僅僅存儲類層次結構樹中的

map<string, set<string> > myTypeList; 
map<string, set<component *> myObjectList; 

myTypeList["component"].insert("component"); 
myTypeList["component"].insert("renderable"); 
myTypeList["component"].insert("sprite"); 
myTypeList["renderable"].insert("renderable"); 
myTypeList["renderable"].insert("sprite"); 
myTypeList["sprite"].insert("sprite"); 

// this isn't quite right, but you get the idea 
struct doForList { 
    UnaryFunction f; 
    doForList(UnaryFunction f): func(f) {}; 
    operator()(string typename) { 
     for_each(myTypeList[typename].begin();myTypeList[typename].end(), func); 
    } 
} 

for_each(myTypeList["renderable"].begin(),myTypeList["renderable"].end(), doForList(myFunc)) 
+0

我需要遍歷比添加/刪除多了,我計劃在幾百到幾千部件的時間,視情況而定。我已經考慮過你說過的方法,但仍然沒有排除它,但我不是存儲要求的忠實粉絲。不過,可能是最好的選擇。 – Toji 2009-04-15 03:17:41

0

答案取決於你需要他們的順序。你幾乎別無選擇序,後序,以及序。因此在廣度上有明顯的相似性,並且首先進行深度搜索,而且一般來說,您將難以擊敗它們。

現在,如果你約束問題痘痘,也有用於存儲任意數據的樹木作爲陣列的一些老式的算法。我們在FORTRAN的日子裏用了很多。其中一人有鑰匙訣竅是要存儲的孩子,說A2和A3,在指數(A)* 2,指數(A)* 2 + 1。問題是,如果你的樹稀疏,你會浪費空間,並且樹的大小受到數組大小的限制。但是,如果我記得這一點,您可以通過簡單的DO循環獲得廣度優先的元素。

看一看克努特第3卷,有這些東西一噸那裏。

0

如果您想查看現有實現的代碼,牛仔編程頁面中引用的Game Programming Gems 5文章附帶了一些我們用於組件系統的代碼的簡化版本(我做了相當一部分該文章中描述的系統的設計和實施)。

我需要回去重新檢查代碼,我不能這樣做,現在,我們並不代表在你帶路層次的東西。雖然組件位於代碼中的類層次結構中,但運行時表示形式是一個扁平列表。組件剛剛宣佈了他們實現的接口列表。用戶可以查詢接口或具體類型。

所以,在你的榜樣,雪碧和粒子會宣佈他們實施的渲染界面,如果我們想做一些所有renderables,我們剛剛通過活性成分的列表循環,檢查各一個。表面上看起來效率不高,但實際情況並不理想。這不是問題的主要原因是它實際上不是一個非常常見的操作。比如像renderables,例如,增加自己在創建渲染場景,所以全球現場管理器維護它自己的渲染對象的列表,並從來不需要查詢組件系統爲他們。與物理學和碰撞組件以及類似的東西類似。