如何改善GetComponent <>方法

时间:2019-08-25 21:28:25

标签: c++ components

我一直在调试另一个问题中的方法,即使它已得到固定的注释,但仍使我相信我做得不好,但没人能提出建议来改进它。

>

我正在编写的渲染引擎使用受团结启发的组件系统。 GetComponent<>()方法从对象中返回给定类型的组件(如果存在),如果给定组件不存在,则返回nullptr。所有组件都从通用的Component基类继承。

代码如下:

template <typename CompType>
inline CompType getComponent()
{
    for(Component * currComp : compStruct.components)
    {
        CompType currentEntry = dynamic_cast<CompType>(currComp);
        if (currentEntry != nullptr)
        {
            return currentEntry;
        }
    }
    return nullptr;
}

用法如下:

//Returns a valid RenderConditions pointer if the object has that component
RenderConditions* rc = go->getComponent<RenderConditions*>();

dynamic_cast显然是不可取的,尤其是因为此系统可以多次用于RenderComponents,AnimationComponents,Transform或其他任何帧。关于每种类型的组件都有一个固定的“ ID”,我有一些想法,但关键是我希望该游戏引擎的用户能够为系统编写自己的组件,而无需使用dynamic_cast我发现它只能与object.h中包含的组件一起使用。

任何想法如何改进?

1 个答案:

答案 0 :(得分:1)

潜在的陷阱

请勿使用此方法存储持久性(例如,在磁盘上)类型的元数据,因为typeid可能在同一程序的不同调用之间改变。


请注意:GetComponent方法在Unity中确实很昂贵,因此不建议您频繁调用它(即每帧多次)。相反,您应该获取它并将结果缓存在其他位置。


因此,要实现此目的,您可以使用标准提供的typeid运算符和std::type_indexstd::type_index对于每种类型都是唯一的,并且可以用作映射键(请参见this reference page)。

这是一个示例实现:

#include <typeindex>
#include <map>
#include <cassert>

//Base class of all components
class Component {
public:
    virtual ~Component() = default;
    Component(const Component& rhs) = delete;
protected:
    Component() = default;
};

class Rigidbody : public Component {

};

class MeshRenderer : public Component {

};

class Text : public Component {
public:
    std::string text;
};

class GameObject {
private:
    std::multimap<std::type_index, Component*> m_components;
public:
    template<typename T, 
        typename = std::enable_if_t<std::is_base_of_v<Component, T>>>
    T* add_component() {
        return dynamic_cast<T*>(m_components.insert(std::make_pair(std::type_index(typeid(T)), new T))->second);
    }

    template<typename T, 
        typename = std::enable_if_t<std::is_base_of_v<Component, T>>>
    T* get_component() {
        auto it = m_components.find(std::type_index(typeid(T)));
        if (it == m_components.end()) return nullptr;
        return dynamic_cast<T*>(it->second);
    }
};

int main() {
    GameObject gm;
    gm.add_component<MeshRenderer>();
    auto& text = *gm.add_component<Text>();
    assert(gm.get_component<MeshRenderer>() != nullptr);
    assert(gm.get_component<Rigidbody>() == nullptr);
    text.text = "Hello, World!";
    std::cout << gm.get_component<Text>()->text << std::endl;
    return 0;
}

输出应为:Hello, World!