애플리케이션 이벤트 시스템 문서
이전까지는 따로 이벤트 시스템을 만들지 않고 반복문 안에서 if문을 통해서 특정 기능을 실행하였습니다. 예를 들어 게임오브젝트를 생성하거나 삭제하거나하는 작업들입니다.
작은 프로그램에서는 그렇게 진행해도 큰 문제는 없지만 프로그램의 규모가 커지고 클래스가 다양해지며 복잡해지면 점점 복잡해지고 결합도가 증가합니다. 이를 이벤트 시스템으로 만들면 장점이 뛰어납니다.
•
이벤트 시스템은 유지보수성과 확장성이 뛰어나고, 성능이 최적화될 수 있음.
•
if 문 방식은 간단한 프로젝트에서는 빠르게 구현 가능하지만, 규모가 커지면 유지보수가 어려워짐.
작은 규모에서는 if 문도 가능하지만, 이벤트 시스템을 구축하면 코드의 확장성과 유지보수성을 높일 수 있음. 
개요
이 문서는 Application 클래스에서 구현된 이벤트 시스템에 대한 상세 설명을 제공합니다. 이 시스템은 이벤트 큐와 이벤트 디스패처를 활용하여 이벤트를 효율적으로 관리할 수 있도록 설계되었습니다. 이를 통해 이벤트 핸들러 등록, 이벤트 처리 및 처리되지 않은 이벤트를 다룰 수 있습니다.
Application 클래스
Application 클래스는 애플리케이션의 주요 진입점 역할을 합니다. 초기화, 이벤트 처리, 업데이트, 렌더링 및 종료를 관리합니다.
클래스 선언
class Application
{
// ...
private:
bool mbLoaded;
bool mbRunning;
std::unique_ptr<graphics::GraphicDevice_DX11> mGraphicDevice;
Window mWindow;
// 이벤트 큐
EventQueue mEventQueue;
};
C++
복사
주요 메서드
•
InitializeEventHandlers(): 특정 이벤트 유형에 대한 핸들러를 등록합니다.
•
EndOfFrame(): 프레임이 끝날 때 이벤트 큐를 처리하고 이벤트를 실행합니다.
•
PushEvent(Event* e): 이벤트를 이벤트 큐에 추가합니다.
이벤트 큐 시스템
EventQueue 클래스는 이벤트를 큐에 저장하고 처리하는 역할을 합니다.
클래스 선언
class EventQueue
{
public:
EventQueue();
~EventQueue();
template<typename T>
void RegisterHandler(std::function<bool(T&)> handler)
{
mHandlers[T::GetStaticType()] = [handler](Event& e) -> bool {
return handler(static_cast<T&>(e));
};
}
void Push(Event* event);
void Process();
void SetCallback(const EventCallbackFn& callback);
private:
std::queue<Event*> mQueue;
EventCallbackFn mCallback;
std::unordered_map<eEventType, HandlerCallbackFn> mHandlers;
};
C++
복사
주요 메서드
•
RegisterHandler<T>(): 특정 이벤트 유형에 대한 핸들러를 등록합니다.
•
Push(Event* event): 이벤트를 큐에 추가합니다.
•
Process(): 큐에 있는 모든 이벤트를 처리하고 적절한 핸들러를 실행합니다.
이벤트 시스템
Event 클래스와 EventDispatcher를 사용하여 이벤트를 정의하고 적절히 분배합니다.
Event 클래스
class Event
{
public:
virtual ~Event() = default;
bool Handled = false;
virtual eEventType GetEventType() const = 0;
virtual const char* GetName() const = 0;
virtual int GetCategoryFlags() const = 0;
virtual std::string ToString() const { return GetName(); }
bool IsInCategory(eEventCategory category);
};
C++
복사
Event 디스패처
class EventDispatcher
{
public:
EventDispatcher(Event& event);
template<typename T, typename F>
bool Dispatch(const F& func);
private:
Event& mEvent;
};
C++
복사
이벤트 유형 정의
enum class eEventType
{
None = 0,
WindowClose, SetWindowResize, WindowFocus, WindowLostFocus, WindowMoved,
AppUpdate, AppLateUpdate, AppRender,
KeyPressed, KeyReleased, KeyTyped,
MouseButtonPressed, MouseButtonReleased, MouseMoved, MouseScrolled,
GameObjectDestroyed, GameObjectCreated,
};
C++
복사
이벤트 처리 과정
1.
Push(Event* e)를 사용하여 이벤트가 EventQueue에 추가됩니다.
2.
Process() 함수가 실행되면서 큐에 있는 이벤트를 순차적으로 처리합니다.
3.
해당 이벤트 유형에 등록된 핸들러가 있다면 실행됩니다.
4.
이벤트가 처리되지 않은 경우 기본 콜백이 실행됩니다.
요약
이 이벤트 기반 시스템은 애플리케이션 내에서 이벤트를 처리하는 모듈화된 접근 방식을 제공합니다. EventQueue, EventDispatcher 및 이벤트 유형 정의를 결합하여 효율적인 이벤트 관리를 가능하게 하며, 애플리케이션의 유연성과 확장성을 보장합니다.
GameObject 생성/파괴 이벤트 기반 처리 시스템
개요
SceneManager가 직접 게임 오브젝트를 생성하거나 파괴하지 않고,
이벤트(Event)를 통해 오브젝트의 생성과 제거를 처리하도록 리팩토링함.
리팩토링 전 문제점
•
SceneManager나 다른 시스템이 직접 scene->AddGameObject() 또는 EraseGameObject() 호출 → 의존성 증가
•
게임 오브젝트 생성 시 부가적인 후처리(예: 로깅, 이펙트 생성 등)를 넣기 어려움
•
테스트 및 디버깅이 어려움
리팩토링 후 구조
오브젝트 생성/파괴 시 SceneManager에 이벤트를 Push →
SceneManager::InitializeEventHandlers()에서 각 이벤트를 핸들링 →
내부에서 scene->AddGameObject() 또는 EraseGameObject() 수행
흐름 요약
[1] 오브젝트 생성
T* gameObject = new T();
gameObject->SetLayerType(type);
SceneManager::PushEvent(new ya::GameObjectCreatedEvent(gameObject, activeScene));
C++
복사
[2] 이벤트 큐 처리
mEventQueue.RegisterHandler<GameObjectCreatedEvent>([](GameObjectCreatedEvent& e) -> bool {
SceneManager::GameObjectCreated(e.GetGameObject(), e.GetScene());
return true;
});
C++
복사
[3] 최종 처리
void SceneManager::GameObjectCreated(GameObject* gameObject, Scene* scene) {
scene->AddGameObject(gameObject, gameObject->GetLayerType());
}
C++
복사
예제 코드
GameObject 생성 유틸 함수
template <typename T>
T* Instantiate(Vector3 position, eLayerType type)
{
T* gameObject = new T();
gameObject->SetLayerType(type);
Transform* tr = gameObject->template GetComponent<Transform>();
tr->SetPosition(position);
Scene* activeScene = SceneManager::GetActiveScene();
SceneManager::PushEvent(new ya::GameObjectCreatedEvent(gameObject, activeScene));
return gameObject;
}
C++
복사
SceneManager 이벤트 초기화
void SceneManager::InitializeEventHandlers()
{
mEventQueue.RegisterHandler<GameObjectCreatedEvent>([](GameObjectCreatedEvent& e) -> bool {
SceneManager::GameObjectCreated(e.GetGameObject(), e.GetScene());
return true;
});
mEventQueue.RegisterHandler<GameObjectDestroyedEvent>([](GameObjectDestroyedEvent& e) -> bool {
SceneManager::GameObjectDestroyed(e.GetGameObject(), e.GetScene());
return true;
});
mEventQueue.SetCallback([](Event& e) {
std::cout << "[Application] Unhandled Event: " << e.ToString() << std::endl;
});
}
C++
복사
GameObjectCreated/Destroyed 처리
cpp
복사편집
void SceneManager::GameObjectCreated(GameObject* gameObject, Scene* scene)
{
scene->AddGameObject(gameObject, gameObject->GetLayerType());
}
void SceneManager::GameObjectDestroyed(GameObject* gameObject, Scene* scene)
{
scene->EraseGameObject(gameObject);
}
C++
복사
장점 요약
항목 | 효과 |
확장성 | 새로운 후처리를 GameObjectCreatedEvent 핸들러에만 추가하면 됨 |
결합도 감소 | 생성자가 Scene에 직접 접근하지 않음 |
테스트 용이 | 이벤트 단위로 단위 테스트 가능 |
디버깅 용이 | 미처리 이벤트 로깅 기능 내장 |