【发布时间】:2017-12-21 17:41:58
【问题描述】:
我最近遇到了游戏引擎中经常使用的实体组件系统。我决定自己用 C++ 实现它,但很快遇到了一个熟悉的问题。一切都开始天真无邪。我从以下课程开始:
class Entity
{
private:
int entityId;
std::map<std::string, Component*> components;
public:
Entity();
~Entity();
void AddComponent(Component *component);
void RemoveComponent(std::string name);
bool HasComponent(std::string name);
Component* GetComponent(std::string name);
};
class Component
{
public:
int componentId;
};
然后我决定创建一些特定的组件类型:
class Input : public Component
{
public:
void process();
}
class Physics : public Component
{
public:
void update();
}
我开始对此进行测试。我创建了一个实体:
Entity *entity = new Entity();
entity->AddComponent(new Input());
entity->AddComponent(new Physics());
这就是事情开始出错的地方。然后我想到了如何从实体中获取组件。如果我想做类似的事情怎么办:
Physics *physics = entity->GetComponent("Physics");
physics->update();
但是 GetComponent("Physics") 返回基类 Component,而不是派生类 Physics!我在互联网上进行了一些搜索,但在 c++ 中找不到显示如何解决此问题的示例。在查看了 Unity 的功能后,我发现它们似乎只是进行了向下转换。例如,在 Unity 中,代码 (C#) 将是:
Physics physics = entity.GetComponent("Physics") as Physics;
这不是很糟糕吗?在设计实体组件系统时,如何在 C++ 中解决这个问题?或者如何执行向下转换?
【问题讨论】:
-
也许
process和update使用相同的函数名,并使其成为Component类中的纯虚函数以被子类覆盖?您确实了解virtual函数吗?它是 C++ 继承和多态性的重要组成部分。 -
是的,但这是一个简单的例子。如果我有 100 种不同的组件类型,每一种都有几十种不同的方法会怎样。然后组件必须有 100 个虚拟方法。
-
如果将
GetComponent声明为模板方法T GetComponent<T>(const std::string& name),则目标可能是可以实现的。有点无聊,但允许隐藏方法中的所有血腥细节 -
@James 我可能会选择一种设计来查询和访问类似于 COM 接口的某些接口类型(纯抽象类)。
-
或许你应该研究一下访问者模式?
标签: c++