Search
Duplicate

GameObject 이벤트 시스템(event system)

애플리케이션 이벤트 시스템 문서

이전까지는 따로 이벤트 시스템을 만들지 않고 반복문 안에서 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에 직접 접근하지 않음
테스트 용이
이벤트 단위로 단위 테스트 가능
디버깅 용이
미처리 이벤트 로깅 기능 내장