Programming/Implementation

C++로 GetComponent<T>() 구현하기

cloudstudyJ 2024. 10. 22. 12:27

유니티에서 컴포넌트를 찾아주는 GetComponent<T>() 를 C++로 어떻게 구현할 수 있을지 고민했다.

 

먼저 GetComponent 함수의 형태를 보면 인자가 없으며 template를 사용한다는 것을 알 수 있고,

함수의 결과로 컴포넌트를 찾아서 형변환하여 포인터 혹은 레퍼런스로 반환할 것이라는 예상을 할 수 있다.

 

그런데, 레퍼런스는 선언과 참조를 같이 해야하므로 사용할 수 없다. 따라서, 포인터로 component를 저장한다.

 

template <typename T>
T* getComponent() {
    ...
    
    return static_cast<T*>(...);
}

 

그럼, 여기서 "컴포넌트를 어떻게 구분하여 찾을 것인가?" 를 고민해봐야 한다.

 

객체는 다수의 컴포넌트를 가질 수 있으므로 삽입, 삭제, 검색 속도가 빠르면 좋을 것이고,

컴포넌트 구분을 위해서 각 컴포넌트에 ID를 부여하는 식으로 해시 테이블을 사용하자는 결론을 내렸다.

 

각 컴포넌트에 ID를 부여하고, 키 (Key) 로는 ID 값을, 값 (Value) 은 컴포넌트의 포인터를 가지면 된다고 생각했다.

또한, ID 값은 정적 함수 (static) 를 사용하여 각 컴포넌트 클래스마다 하나의 ID 값만 가질 수 있도록 제한한다.

 

class Component {
    public:
        unsigned short id() const noexcept { return mID; }
        
    ...
        
    protected:
        unsigned short mID{ };
        
    ...
};

class Transform: public Component {
    public:
        using super = Component;

    public:
        static unsigned short componentID() {
            static const unsigned short id = 1;
            
            return id;
        }
    
    ...
};
class Texture: public Component {
    public:
        using super = Component;

    public:
        static unsigned short componentID() {
            static const unsigned short id = 2;
            
            return id;
        }
    
    ...
};

class GameObject {
    public:
        void addComponent(Component* component) { mComponents.emplace(component->id(), component); }
        template <typename T> void removeComponent() {
            auto data = mComponents.find(T::componentID());
            
            if (data != mComponents.end()) {
                delete data->second;
                
                mComponents.erase(T::componentID());
            }
        }
        
        template <typename T> T* getComponent() {
            auto data = mComponents.find(T::componentID());
            
            return (data != mComponents.end()) ? static_cast<T*>(data->second) : nullptr;
        }
        
        ...

    private:
        unordered_map<unsigned short, Component*> mComponents;
        
        ...
};

 

여기서, 추가된 모든 컴포넌트는 Component 클래스와 상속관계이므로 형 변환에 static_cast를 사용해도 된다.

 

이로써, 간단한 컴포넌트 관리 및 getComponent<T>() 를 구현했다.

부족한 점은 있겠지만 가볍게 사용하고 싶다면 이런 방식도 괜찮을 것이라 생각한다.