• Home
  • About
    • 게임 개발자 유정룡 photo

      게임 개발자 유정룡

      포트폴리오

    • Learn More
    • Email
    • Github
    • Bitbucket
  • Projects
    • All Projects
    • All Tags

DirectX11 공부 9일차(Component 패턴)

09 Jun 2021

Reading time ~3 minutes

Actor

Component들을 갖고있는 클래스 이다.

이 Component들을 실행시키고, 업데이트시키는등 다루는 클래스이다.

Actor

class Actor final
{
public:
	Actor();
	~Actor();

	void Initialize();
	void Update();
	void Destroy();

	/*
	* Property
	*/
	const std::string& GetName() const { return name; }
	void SetName(const std::string& name) { this->name = name; }

	bool IsActive() const { return is_active; }
	void SetActive(const bool& is_active) { this->is_active = is_active; }

	const std::shared_ptr<class Transform>& GetTransform() const { return transform; } 
	class Transform* GetTransform_Row() { return transform.get(); }

	/*
	* Component
	*/
	const std::shared_ptr<IComponent> AddComponent(const ComponentType& type);

	template <typename T>
	const std::shared_ptr<T> AddComponent();

	template <typename T>
	const std::shared_ptr<T> GetComponent();

	template <typename T>
	T* GetComponent_Row();

	template <typename T>
	const std::vector<std::shared_ptr<T>> GetComponents();

	const std::vector<std::shared_ptr<IComponent>>& GetAllComponents() const { return components; }

	bool HasComponent(const ComponentType& type);

	template <typename T>
	bool HasComponent();

	template <typename T>
	void RemoveComponent();

private:
	std::string name;
	bool is_active = true;

	std::shared_ptr<class Transform> transform;
	std::vector<std::shared_ptr<IComponent>> components;
};

지금까지는 일반 포인터(Row Pointer : *)만 사용했지만, 이번엔 메모리 해제를 좀 더 편하게 하기 위해 스마트 포인터를 사용했다.

그리고 컴포넌트중에 Transform 을 따로 뺀 이유가 Transform을 이동할때마다 컴포넌트중에 찾는건 너무 비효율적이라 따로 빼서 사용한다.

주요 함수들만 살펴보자

void Actor::Initialize()
{
	for (const auto& component : components)
	{
		component->Initialize();
	}
}

void Actor::Update()
{
	if (!IsActive())
	{
		return;
	}

	for (const auto& component : components)
	{
		if (component->IsEnabled())
		{
			component->Update();
		}
	}
}

void Actor::Destroy()
{
	for (const auto& component : components)
	{
		component->Destroy();
	}
}

이 컴포넌트들의 생명주기를 실행한다.

template<typename T>
inline const std::shared_ptr<T> Actor::AddComponent()
{
	static_assert(std::is_base_of<IComponent, T>::value, "Provider type dose not implement IComponent");

	ComponentType type = IComponent::DeduceComponenetType<T>();

	if (HasComponent(type))
	{
		return GetComponent<T>();
	}

	components.emplace_back(std::make_shared<T>(this, transform.get()));

	T new_component = std::static_pointer_cast<T>(components.back());

	if constexpr(std::is_same<T, class Transform>::value)
	{
		transform = new_component;
	}

	return new_component;
}

대부분 클래스가 IComponent에 상속되있는지 확인하고 있으면 만드는 식이다.

만들때 스마트 포인터로 선언했기 때문에 캐스팅을 한 뒤 추가한다. 하지만, Transform이 들어오면 Transform을 바꿔준다.

template<typename T>
inline const std::shared_ptr<T> Actor::GetComponent()
{
	static_assert(std::is_base_of<IComponent, T>::value, "Provider type dose not implement IComponent");

	ComponentType type = IComponent::DeduceComponenetType<T>();

	for (const auto& component : components)
	{
		if (component->GetComponentType() == type)
		{
			return std::static_pointer_cast<T>(component);
		}
	}

	return nullptr;
}
template<typename T>
inline void Actor::RemoveComponent()
{
	static_assert(std::is_base_of<IComponent, T>::value, "Provider type dose not implement IComponent");

	ComponentType type = IComponent::DeduceComponenetType<T>();

	for (auto iter = components.begin(); iter != components.end();)
	{
		auto component = *iter;
		if (component->GetComponentType() == type)
		{
			component->Destroy();
			component.reset();
			iter = components.erase(iter);
		}

		else
		{
			iter++;
		}
	}
}

제거 하는 함수이다. 컴포넌트 돌아서 찾으면 제거한다.

나머지는 거의 이것의 연장선이다.

Smart Pointer

이거 한번 다루고 가야 될것 같다.

DXD3와 큰 상관은 없지만, 많이 사용되기도 하고 스마트 포인터를 몰라서 필기 테스트 알고리즘은 전부 맞았는데 떨어진 기억이 있다 ㅠㅠ

모던 C++에는 3개의 포인터가 있다

unique_ptr, shared_ptr, week_ptr

하나하나 알아보도록 하자

  • unique_ptr
    • 포인터의 소유자는 한명이다.
    • 포인터에 대한 소유권을 이전 할 순 있지만, 복사나 대입과 같은 공유는 할 수 없다.
    • 유니틐 포인터 객체가 소멸될 때 원시 포인터도 소멸된다.
  • shared_ptr
    • 유니크 포인터와 달리 소유권을 다른 공유 포인터와 공유할 수 있다.
    • 참조 카운팅 기반으로 동작하는 포인터이다.
    • 원시 포인터를 가리키는 포인터 1개와, 참조 카운팅 관리를 위한 포인터 1개를 가진다.
    • 원시 포인터에 대한 소유자가 0명이 되면 자동으로 메모리가 해제된다.
    • 공유 포인터 전달
      • 값 형식으로 전달
        • call by value로 전달하면 개체에 대한 복사 생성자를 호출하므로 명백한 개체의 복사이며, 공유이다.
        • 따라서 원시 포인터를 가리키는 개체가 1개 증가하여, 참조 카운트가 1증가한다.
      • 참조 형식으로 전달
        • call by reference로 전달하면 값의 복사가 일어나지 않으며, 원본을 그대로 참조한다.
        • 따라서 새로운 소유자가 생기지 않으며, 참조 카운트 또안 증가하지 않는다.
  • weak_ptr
    • 하나 이상의 shared_ptr 인스턴스가 소유하는 객체에 대한 접근을 제공하지만, 소유자의 수에는 포함되지 않는 포인터
    • 서로 상대방을 가리키는 shared_ptr을 갖고 있다면, 참조 횟수가 0이 되지 않는 순환참조를 해결하기 위해 나온 포인터이다.


DirectX Share Tweet +1