객체에 특정 기능을 하는 컴포넌트들을 조립하여 기능들을 사용할 수 있도록 구현
=>
컴포넌트 패턴
GameObejct 클래스
게임에서 생성되는 모든 객체를 나타내는 클래스
컴포넌트들을 조립하여 사용
class GameObject : public enable_shared_from_this<GameObject>
{
public:
...
void AddComponent(shared_ptr<Component> component);
private:
array<shared_ptr<Component>, FIXED_COMPONENT_COUNT> _components;
vector<shared_ptr<MonoBehaviour>> _scripts;
};
shared_from_this() 함수를 사용하기 위해 enable_shared_from_this 상속
객체마다 하나만 들고 있을 수 있는 고정 컴포넌트들의 배열 _components는
사용할 수 있는 FIXED_COMPONENT_COUNT만큼 생성해둔다.
반면 몇 개가 사용될지 모르는 컴포넌트 스크립트는 vector로 생성
void GameObject::AddComponent(shared_ptr<Component> component)
{
component->SetGameObject(shared_from_this());
...
}
this 자체는 shared_ptr 데이터가 아니기 때문에
shared_ptr을 파라미터로 this를 넘겨주어야 할 때
항상 shared_from_this() 함수로 넘겨야 함
Component 클래스
GameObejct가 사용할 수 있는 기능들을 담당하는 클래스로
컴포넌트는 크게 두 가지 타입으로 볼 수 있음
enum class COMPONENT_TYPE : uint8
{
TRANSFORM,
MESH_RENDERER,
// ...
MONO_BEHAVIOUR,
END,
};
MONO_BEHAVIOUR를 제외한 컴포넌트들은 객체가 1개씩만 소유할 수 있는 FIXED_COMPONENT와
몇 개가 있던 상관이 없는 스크립트 컴포넌트인 MONO_BEHAVIOUR
class Component
{
...
protected:
COMPONENT_TYPE _type;
weak_ptr<GameObject> _gameObject;
};
컴포넌트들은 자신이 속한 게임 객체를 알 수 있는데,
게임 객체와 컴포넌트의 포인터 순환구조로 인해 메모리 누수가 발생할 수 있기 때문에
weak_ptr로 게임 객체를 가지고 있도록 구현
Transform 컴포넌트
해당 컴포넌트는 모든 게임 객체가 생성될 때 기본으로 가지고 있게 된다.
=>
모든 객체는 위치 및 회전, 크기 값을 가지고 있음
GameObejct에서 transform 컴포넌트를 가지고 올 수 있고,
반대로 transform 컴포넌트를 알면 GameObejct를 가지고 올 수 있음
MonoBehaviour 클래스
스크립트 컴포넌트 클래스
MeshRenderer 클래스
mesh와 material을 관리하는 컴포넌트 클래스
void Game::Init(const WindowInfo& info)
{
gameObject->Init(); // transform
shared_ptr<MeshRenderer> meshRenderer = make_shared<MeshRenderer>();
...
gameObject->AddComponent(meshRenderer);
...
}
gameObject를 만들고 객체에 컴포넌트들을 조립하여 사용