C++ 分层组件存储结构
我已经在脑子里反复思考这个问题好几天了,还没有得出任何令人满意的结论,所以我想我应该征求so工作人员的意见。对于我正在开发的游戏,我使用了一个组件对象模型,如下所述和所示。它实际上进展得相当顺利,但我当前的存储解决方案却受到了限制(我只能按组件的类名或任意“族”名称请求组件)。我想要的是能够请求给定的类型并遍历该类型的所有组件或从该类型派生的任何类型 考虑到这一点,我首先实现了一个简单的RTTI方案,该方案按照这个顺序通过派生类型存储基类类型。这意味着,例如,sprite的RTTI将是:component::renderable::sprite。这使我能够轻松地比较类型,通过比较B的所有元素,查看类型A是否派生自类型B:即component::renderable::sprite派生自component::renderable,而非component::timer。简单、有效且已实施 我现在想要的是一种以表示该层次结构的方式存储组件的方法。首先想到的是使用类型作为节点的树,如下所示:C++ 分层组件存储结构,c++,stl,C++,Stl,我已经在脑子里反复思考这个问题好几天了,还没有得出任何令人满意的结论,所以我想我应该征求so工作人员的意见。对于我正在开发的游戏,我使用了一个组件对象模型,如下所述和所示。它实际上进展得相当顺利,但我当前的存储解决方案却受到了限制(我只能按组件的类名或任意“族”名称请求组件)。我想要的是能够请求给定的类型并遍历该类型的所有组件或从该类型派生的任何类型 考虑到这一点,我首先实现了一个简单的RTTI方案,该方案按照这个顺序通过派生类型存储基类类型。这意味着,例如,sprite的RTTI将是:comp
component
/ \
timer renderable
/ / \
shotTimer sprite particle
在每个节点上,我将存储该类型的所有组件的列表。这样,请求“component::renderable”节点将使我能够访问所有可渲染组件,而不管派生类型如何。问题是我希望能够使用迭代器访问这些组件,这样我就可以执行以下操作:
for_each(renderable.begin(), renderable.end(), renderFunc);
map<string,set<componenet *>> myTypeList
从renderable向下遍历整个树。我使用了一个非常难看的map/vector/tree节点结构和一个自定义的前向迭代器来跟踪我所在位置的节点堆栈。然而,在实施的过程中,我觉得必须有更好、更清晰的方法。。。我就是想不出一个:(
所以问题是:我是否不必要地把这件事复杂化了?我是否缺少一些明显的简化,或者我应该使用预先存在的结构?或者这只是一个继承的复杂问题,我可能已经做得很好了
感谢您的宝贵意见!您应该考虑一下需要多长时间执行以下操作:
- 横穿这棵树
- 从树中添加/删除元素
- 您需要跟踪多少对象
for_each(renderable.begin(), renderable.end(), renderFunc);
map<string,set<componenet *>> myTypeList
通过在多个列表中注册每个obejct,可以很容易地对给定类型和子类型的所有对象执行操作
for_each(myTypeList["renderable"].begin(),myTypeList["renderable"].end(),renderFunc);
请注意,std::set和my std::map构造可能不是最佳选择,这取决于您将如何使用它
或者可能是一种混合方法,只在树中存储类继承权
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))
映射myTypeList;
映射您应该考虑您需要多长时间执行以下操作:
- 横穿这棵树
- 从树中添加/删除元素
- 您需要跟踪多少对象
哪个更频繁将有助于确定最佳解决方案
也许不需要创建一个复杂的树,只需要拥有一个所有类型的列表,并为其派生的每种类型添加一个指向对象的指针。类似如下:
for_each(renderable.begin(), renderable.end(), renderFunc);
map<string,set<componenet *>> myTypeList
通过在多个列表中注册每个obejct,可以很容易地对给定类型和子类型的所有对象执行操作
for_each(myTypeList["renderable"].begin(),myTypeList["renderable"].end(),renderFunc);
请注意,std::set和my std::map构造可能不是最佳选择,这取决于您将如何使用它
或者可能是一种混合方法,只在树中存储类继承权
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))
映射myTypeList;
map答案取决于你需要它们的顺序。你几乎可以选择前序、后序和顺序。因此,在广度优先和深度优先搜索中有明显的相似之处,一般来说,你很难打败它们
现在,如果你把问题限制在一个小范围内,有许多老式的算法可以将任意数据树存储为数组。在FORTRAN时代,我们经常使用它们。其中一个关键技巧是将a的子元素,比如A2和A3,存储在索引(a)*2,索引(a)处*2+1.问题是如果你的树是稀疏的,你就浪费了空间,而且你的树的大小受到数组大小的限制。但是,如果我没记错的话,你可以通过简单的DO循环得到元素的宽度优先顺序
看看Knuth第三卷,里面有很多这样的东西。答案取决于你需要它们的顺序。你几乎可以选择前序、后序和顺序。因此,在广度优先和深度优先搜索中有明显的相似之处,一般来说,你很难打败它们
现在,如果你把问题限制在一个小范围内,有许多老式的算法可以将任意数据树存储为数组。在FORTRAN时代,我们经常使用它们。其中一个关键技巧是将a的子元素,比如A2和A3,存储在索引(a)*2,索引(a)处*2+1.问题是如果你的树是稀疏的,你就浪费了空间,而且你的树的大小受到数组大小的限制。但是,如果我没记错的话,你可以通过简单的DO循环得到元素的宽度优先顺序
看看Knuth第3卷,里面有很多这样的东西。如果你想看到现有实现的代码,牛仔编程页面中引用的游戏编程Gems 5文章附带了我们用于组件系统的代码的精简版本(我对那篇文章中描述的系统的设计和实现做了大量工作)
我需要返回并重新检查代码,这是我现在不能做的,我们没有按照您所展示的方式在层次结构中表示事物