Search code examples
c++templatesunity-game-engineenumerationcomponent-based

How to index all the derived components in a base component list in Entity


I am trying to do a entity component system design for simulation. Here is what confuses me now. I am trying to make a Entity class

Entity.h
class Entity
{
public:
    Entity();
    virtual ~Entity() = 0;
    //---------------------Methods---------------------//
    void AddComponent(const shared_ptr<Component> component);

    template<class T>
    T* GetComponent() const;

    //---------------------Members---------------------//
    vector<shared_ptr<Component>>   m_components;
}

Entity.cpp
template<typename T>
T* Entity::GetComponent() const
{
    Component::component_type_t typeIndex = /*T::component_type*/
    T* returnPtr = dynamic_pointer_cast<T>(m_components[component_type].get());
    return returnPtr;
}

And the Component class looks like this

class Component
{
public:
    Component();
    virtual ~Component() = 0;
    //---------------------Methods---------------------//

    //---------------------Members---------------------//
    typedef enum component_type_t
    {
        MESH_T,
        RIGIDBODY_T,
        TRANSFORM_T,
        NUM_TYPES
    };
    component_type_t componentType;
};

They way I want to use GetComponent() is the same as in Unity3D

Transform* t = GetComponent<Transform>()

But as you can see I cannot find a way to do that yet. The way I used to do it was

class Entity
{
    .....
    Component* GetComponent(Component::component_type_t componentType) const
    {
      return m_components[component_type].get()
    };
}

And I could only use it as

Transform* t = dynamic_pointer_cast<Transform>(GetComponent(Component::component_type_t::TRANSFORM_T));

Clearly it is tedious.

So my question is can I use some form as this?

class Entity
{
    .....
    template<class T>
    T* GetComponent() const
    {
    Component::component_type_t typeIndex = /*T::component_type*/
    T* returnPtr = dynamic_pointer_cast<T>(m_components[component_type].get());
    return returnPtr;
    };
}

I feel the cleanness and the speed to keep using std::vector<> for storing all my component pointers, is that also a valid design?


Solution

  • Did something similar long time ago. I think you are doing this the hard way. You can easily modify the code below to suit your need. The Components are inherited. You can remove the inheritance and replace it with class inside class or all component classes inside the Component class.

    #include <iostream>
    
    struct Vector3
    {
        float x, y, z;
        Vector3(float x, float y){}
        Vector3(float x, float y,float z){}
    };
    
    template <class T>
    class Component
    {
    public:
        T t;
        void adNewComponent(){
    
        }
    };
    
    class Mesh{
    public:
        Mesh(){}
    };
    
    class Rigidbody{
    public:
        Rigidbody(){}
        void AddForce(float x,float y, float z){}
        void AddForce(Vector3 force){}
    };
    
    
    class Transform{
    public:
        Transform(){}
    };
    
    class Object{
    
    };
    
    class GameObject:Object,
        Component<Mesh>,
        Component<Rigidbody>,
        Component<Transform>
    {
    public:
         template <class T>
         T &GetComponent()
        {
            return this->Component<T>::t;
        }
    
         template <class T>
         T &AddComponent(){
             this->Component<T>::adNewComponent();
             return this->Component<T>::t;
         }
    };
    
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    
        GameObject gameObject;
        gameObject.AddComponent<Rigidbody>();
        Rigidbody rigidBody = gameObject.GetComponent<Rigidbody>();
        rigidBody.AddForce(9,0,0);
    
        std::cin.get();
        return 0;
    }