Implementing Component system from Unity in c++

后端 未结 4 1688
有刺的猬
有刺的猬 2020-12-04 18:23

I\'ve been experimenting with making a component based system similar to Unity\'s, but in C++. I\'m wondering how the GetComponent() method that Unity implement

4条回答
  •  没有蜡笔的小新
    2020-12-04 19:20

    Apologies if this is not what you are looking for, but I had an idea to use the unordered map with a type index and, with the help of some metaprogramming and TR2, place multiple pointers to the component into the map, including its direct base classes as additional keys. So getComponent() and getComponent() along with a down-cast will have the same pointee.

    #include 
    #include 
    #include 
    #include 
    #include 
    
    class Component {
    public:
      virtual ~Component() {}
    };
    
    class GameObject {
    public:
      template 
      void addComponent(T *component);
    
      template 
      T *getComponent();
    
      std::unordered_map components;
    };
    
    template 
    struct direct_bases_as_tuple {};
    
    template 
    struct direct_bases_as_tuple> {
      typedef std::tuple type;
    };
    
    template 
    struct AddComponent {
      GameObject *owner;
    
      explicit AddComponent(GameObject *owner) : owner(owner) {}
    
      void operator()(ComponentType *component) {
        AddComponent{owner}(component);
    
        using BaseType = std::tuple_element::type;
    
        owner->components[typeid(BaseType)] = component;
      }
    };
    
    template 
    struct AddComponent<0u, ComponentBases, ComponentType> {
      GameObject *owner;
    
      explicit AddComponent(GameObject *owner) : owner(owner) {}
    
      void operator()(ComponentType *component) {
        return;
      }
    };
    
    template 
    void GameObject::addComponent(T *component) {
      using ComponentBases = direct_bases_as_tuple::type>::type;
    
      constexpr classCount = std::tuple_size::value;
    
      AddComponent{this}(component);
    
      components[typeid(T)] = component;
    }
    
    template 
    T * GameObject::getComponent() {
      auto iter = components.find(typeid(T));
    
      if (iter != std::end(components)) {
        return dynamic_cast(iter->second);
      }
    
      return nullptr;
    }
    
    class Collider : public Component {};
    class SphereCollider : public Collider {};
    
    int main() {
      GameObject gameObject;
      gameObject.addComponent(new SphereCollider);
    
      //get by derived class
      SphereCollider *sphereColliderA = gameObject.getComponent();
    
      //get by subclass
      SphereCollider *sphereColliderB = dynamic_cast(
        gameObject.getComponent()
      );
    
      if (sphereColliderA == sphereColliderB) {
        std::cout << "good" << std::endl;
      }
    }
    

    I created the AddComponent struct to recurse through the component base classes at compile-time and insert the pointer (value) with the corresponding class (key) each iteration. The helper struct direct_bases_as_tuple was inspired by Andy Prowl's answer to change the direct bases into a tuple. I compiled this using GCC 4.9.2 using C++11 features.

提交回复
热议问题