我对游戏编程的实体/组件风格很感兴趣,并且我想出了一个C++的设计,我想对此进行评论。
我决定使用一个相当纯的实体系统,其中实体只是一个ID号。组件存储在一系列向量中--每个组件类型一个。
但是,我不想为我添加到游戏中的每一个新组件类型添加样板代码。我也不想使用宏来做这件事,坦率地说,这让我感到害怕。因此,我提出了一个基于模板和类型提示的系统。但是,在我花很长时间写这篇文章之前,我想检查一些潜在的问题(我是一个缓慢的程序员!)
所有组件都是从Component
基类派生的。这个基类有一个受保护的构造函数,它接受一个字符串参数。编写新派生组件类时,必须以字符串形式使用新类的名称初始化基。当您第一次实例化一个新的DerivedComponent
时,它会将字符串添加到映射到唯一整数id的组件内部的静态哈希映射中。当您随后实例化相同类型的更多组件时,将不采取任何操作。结果(我认为)应该是一个静态hashmap,其中包含至少实例化一次的组件派生的每个类的名称,映射到一个惟一的id,该id可以通过静态方法Component::getTypeId ("DerivedComponent")
获得。呼。
下一个重要部分是TypedComponentList<typename PropertyType>
。这基本上只是一个带有一些有用方法的std::vector<typename PropertyType>
包装器。它还包含一个实体ID号到数组中插槽的哈希映射,这样我们就可以通过它们的实体所有者找到组件。关键的是,TypedComponentList<>
是从非模板类ComponentList
派生的。
这允许我在我的主ComponentList
中维护指向ComponentManager的指针列表,它实际上指向具有不同模板参数的TypedComponentList
s (鬼怪)。
组件管理器具有模板功能,如:
template <typename ComponentType>
void addProperty (ComponentType& component, int componentTypeId, int entityId)
以及:
template <typename ComponentType>
TypedComponentList<ComponentType>* getComponentList (int componentTypeId)
它处理从ComponentList到正确的TypedComponentList的转换。
因此,要获得您调用的特定类型组件的列表:
TypedComponentList<MyComponent>* list = componentManager.getComponentList<MyComponent> (Component::getTypeId("MyComponent"));
我承认看起来很丑。
你认为如何?好的比坏的重要吗?
发布于 2013-06-30 05:44:29
我认为这是一个很好的第一次实现,但我要指出一些事情。
Component::getTypeId ("DerivedComponent")
上面的代码让我担心有几个原因。
现在,对您来说,这可能是一个“黑客”,但我有许多组件,我可以证明它运行得很好:
static const ComponentType GetType()
{
static char uniqueVariable = 0;
return reinterpret_cast<ComponentType>(&uniqueVariable);
}
应该清楚的是,对于每种类型,类型都是唯一的。严格地说,我有时想知道内存分配的某些边缘情况是否会破坏系统,但到目前为止我还没有遇到任何问题。
更有甚者,我知道对于宏的使用有很大的协同努力,但是任何有经验的程序员都会告诉你,在某些情况下,他们是不那么邪恶的。我想这里就是这样的。我将上面的方法封装在宏中,这样我就可以简单地定义类。
#define COMPONENT_TYPE_INFORMATION static const ComponentType GetType() { static char uniqueVariable = 0; return reinterpret_cast<ComponentType>(&uniqueVariable); }
如果您担心宏,那么请考虑阅读这,以便尽可能地保护自己不受bug的影响。上面显示的宏只是为了说明。
下面是一个组件和用法的示例:
class Component
{
public:
virtual ComponentType GetType() = NULL;
//etc etc
}
class DerivedComponent : public Component
{
public:
DerivedComponent(/*pass some arguments here perhaps*/) { }
void MyMethod() { } //do something
int MyAccessor() { return 1; }
COMPONENT_TYPE_INFORMATION
private:
};
//ComponentList stores a list of pointers to Component for the inheritance goodness
template<typename T>
bool DoesComponentMatchExist(ComponentList list)
{
for(std::size_t i = 0; i < list.size(); i++)
{
if(list[i]->GetType() == T::GetType())
return true;
}
return false;
}
注意,如果T:: GetType ()没有实现纯虚拟组件基类指定的GetType,它将很方便地返回编译器错误。
关于文章的其余部分,虽然我用内存池解决了问题,但在我看来,您的方法似乎很好,尽管不断地传递组件管理器可能会带来麻烦。然而,这取决于你,这样做有好处也有缺点。
不过,我要提到的一件事是,您可能希望将实体中的组件列表从其他实体中分离出来,而不是有一个超级列表。在幕后,您仍然可以将组件存储在一个内存池中,但您可能应该考虑支持它。将父对象的列表传递到每个组件的构造函数中是非常强大的。你可以这样做:
MyComponent::MyComponent(ComponentList parentContainer)
{
//we check whether another component exists in the container.
//in this way we can have dependencies on components
//without grouping together unnecessary functionality
//for instance, you may want a renderable to require a graphics core component
}
https://gamedev.stackexchange.com/questions/58348
复制相似问题