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>() 를 구현했다.
부족한 점은 있겠지만 가볍게 사용하고 싶다면 이런 방식도 괜찮을 것이라 생각한다.