Company
교육 철학

GUI(Editor) 설계 및 기능

개요

게임 에디터는 개발자가 효율적으로 게임을 제작할 수 있도록 돕는 핵심 도구입니다. 본 문서에서는 에디터의 구조를 구성하는 핵심 클래스들과 그 설계 철학, 그리고 각 컴포넌트 간의 상호작용 방식을 상세히 다룹니다.

핵심 아키텍처

에디터는 크게 세 가지 핵심 클래스로 구성됩니다:
EditorApplication: 에디터의 생명주기와 전체 흐름을 제어하는 중앙 관리자
EditorWindow: 독립적인 UI 창을 구현하여 다양한 도구를 제공
Editor: 특정 컴포넌트의 Inspector 기능을 커스터마이징

1. EditorApplication 클래스

개념

EditorApplication은 에디터의 진입점이자 중앙 제어 시스템입니다. 모든 에디터 창과 기능을 관리하며, 메인 루프를 통해 전체 에디터의 생명주기를 제어합니다.

핵심 책임

생명주기 관리
에디터의 초기화부터 종료까지 전 과정을 관리
ImGui 라이브러리 초기화 및 렌더링 파이프라인 구성
프레임 버퍼와 렌더 타겟 관리
윈도우 관리 시스템
여러 EditorWindow 객체를 중앙에서 관리
템플릿 기반 GetWindow<T>() 메서드로 타입 안전한 윈도우 검색 제공
윈도우의 생성, 삭제, 상태 관리
프로젝트 및 씬 관리
프로젝트 열기/저장
씬 생성, 저장, 불러오기
에셋 파일 시스템 관리
뷰포트 제어
뷰포트 크기 및 경계 관리
포커스 및 호버 상태 추적
풀스크린 모드 지원

주요 메서드

// 에디터 생명주기 Initialize() // ImGui 초기화, 윈도우 생성, 리소스 로딩 Update() // 로직 업데이트 (매 프레임) OnGUI() // UI 렌더링 (매 프레임) Run() // 메인 루프 실행 Release() // 리소스 정리 및 종료 // 프로젝트 관리 OpenProject() // 기존 프로젝트 열기 NewScene() // 새 씬 생성 SaveScene() // 현재 씬 저장 SaveSceneAs() // 다른 이름으로 씬 저장 OpenScene() // 특정 씬 파일 열기 // 윈도우 관리 GetWindow<T>(name) // 이름으로 특정 타입의 윈도우 반환
C++
복사

설계 패턴

싱글톤 패턴: 모든 메서드가 static으로 선언되어 전역 접근 가능
파사드 패턴: 복잡한 에디터 시스템을 단순한 인터페이스로 제공
관찰자 패턴: 뷰포트 상태 변화를 추적하고 반응

상태 관리

enum class eState { Disable, // 비활성화 상태 Active, // 실행 중 Destroy, // 종료 대기 };
C++
복사

코드 구조

#pragma once #include "guiEditor.h" #include "guiEditorWindow.h" #include "..\\YamYamEngine_SOURCE\\yaRenderTarget.h" namespace gui { class EditorApplication { public: enum class eState { Disable, Active, Destroy, }; template <typename T> T* GetWindow(const std::wstring& name) { auto iter = mEditorWindows.find(name); if (iter == mEditorWindows.end()) return nullptr; return dynamic_cast<T*>(iter->second); } static bool Initialize(); static void Update(); static void OnGUI(); static void Run(); static void Release(); static void OpenProject(); static void NewScene(); static void SaveScene(); static void SaveSceneAs(); static void OpenScene(const std::filesystem::path& path); private: static bool imGguiInitialize(); static void imGuiRender(); static std::map<std::wstring, EditorWindow*> mEditorWindows; static ImGuiWindowFlags mFlag; static ImGuiDockNodeFlags mDockspaceFlags; static eState mState; static bool mFullScreen; static ya::math::Vector2 mViewportBounds[2]; static ya::math::Vector2 mViewportSize; static bool mViewportFocused; static bool mViewportHovered; static ya::graphics::RenderTarget* mFrameBuffer; }; }
C++
복사

2. Editor 클래스

개념

Editor 클래스는 Inspector 커스터마이징을 위한 기반 클래스입니다. 특정 컴포넌트나 객체에 대해 에디터에서 표시되는 UI와 편집 기능을 개발자가 원하는 대로 재정의할 수 있게 합니다.

핵심 책임

Inspector UI 커스터마이징
기본 Inspector 대신 커스텀 UI 제공
특정 타입의 컴포넌트에 최적화된 편집 인터페이스 구현
속성 값 검증 및 제약 조건 적용
생명주기 이벤트 처리
활성화/비활성화 시점에 필요한 작업 수행
리소스 할당 및 해제 관리
상태 변화에 따른 적절한 반응

사용 시나리오

예시 1: Transform Editor
Position, Rotation, Scale을 시각적으로 편집
드래그 앤 드롭으로 값 조정
Reset 버튼으로 기본값 복원
예시 2: Material Editor
텍스처 미리보기
색상 피커로 색상 선택
쉐이더 속성 실시간 조정

주요 메서드

Initialize() // 에디터 초기화 (리소스 로딩) Update() // 로직 업데이트 OnGUI() // 커스텀 Inspector UI 렌더링 OnEnable() // 활성화 시 호출 OnDisable() // 비활성화 시 호출 OnDestroy() // 소멸 시 호출 (리소스 정리)
C++
복사

상태 시스템

enum class eState { Paused, // 일시정지 (편집 불가) Active, // 활성 상태 (편집 가능) Disabled, // 비활성화 Destroyed // 파괴됨 };
C++
복사

코드 구조

#pragma once #include "guiEntity.h" namespace gui { class Editor : public Entity { public: enum class eState { Paused, Active, Disabled, Destroyed }; Editor(); virtual ~Editor(); virtual void Initialize(); virtual void Update(); virtual void OnGUI(); virtual void OnEnable(); virtual void OnDisable(); virtual void OnDestroy(); private: eState mState; }; }
C++
복사

3. EditorWindow 클래스

개념

EditorWindow독립적인 도구 창을 만들기 위한 기반 클래스입니다. Scene Hierarchy, Inspector, Asset Browser, Console 등 다양한 에디터 도구를 이 클래스를 상속하여 구현합니다.

핵심 책임

독립적인 UI 창 관리
창의 크기, 위치, 상태 관리
도킹 및 탭 시스템 지원
최소화/최대화/닫기 등 창 제어
도구별 특화 기능 제공
각 도구에 맞는 고유한 인터페이스 구현
독립적인 업데이트 및 렌더링 로직
사용자 입력 처리
워크플로우 확장
새로운 도구 추가를 통한 에디터 기능 확장
플러그인 시스템의 기반
사용자 정의 워크플로우 지원

대표적인 EditorWindow 예시

Hierarchy Window
씬의 모든 게임 오브젝트를 트리 구조로 표시
드래그 앤 드롭으로 부모-자식 관계 설정
검색 및 필터링 기능
Inspector Window
선택된 오브젝트의 컴포넌트와 속성 표시
실시간 값 편집 및 미리보기
Add Component 버튼으로 컴포넌트 추가
Asset Browser
프로젝트의 모든 에셋 파일 탐색
썸네일 미리보기
에셋 임포트 및 관리
Console Window
로그, 경고, 에러 메시지 출력
로그 레벨 필터링
메시지 클릭 시 해당 코드로 이동
Profiler Window
CPU/GPU 성능 모니터링
메모리 사용량 추적
프레임 타임 분석

주요 메서드

Initialize() // 창 초기화 Update() // 로직 업데이트 OnGUI() // 창 UI 렌더링 Run() // 창 실행 (메인 루프) OnEnable() // 창 활성화 시 호출 OnDisable() // 창 비활성화 시 호출 OnDestroy() // 창 소멸 시 호출 // Getter/Setter GetFlag() // ImGui 창 플래그 반환 GetState() // 현재 상태 반환 SetState() // 상태 설정 GetSize() // 창 크기 반환 SetSize() // 창 크기 설정
C++
복사

상태 관리

enum class eState { Disable, // 비활성화 (숨김) Active, // 활성 상태 (표시) Destroy, // 파괴 대기 };
C++
복사

ImGui 플래그

ImGuiWindowFlags를 통해 창의 동작을 세밀하게 제어:
타이틀 바 표시/숨김
리사이즈 가능 여부
이동 가능 여부
스크롤바 표시
메뉴바 포함
도킹 가능 여부

코드 구조

#pragma once #include "guiEntity.h" namespace gui { class EditorWindow : public Entity { public: enum class eState { Disable, Active, Destroy, }; EditorWindow(); virtual ~EditorWindow(); virtual void Initialize(); virtual void Update(); virtual void OnGUI(); virtual void Run(); virtual void OnEnable(); virtual void OnDisable(); virtual void OnDestroy(); ImGuiWindowFlags GetFlag() const { return mFlag; } eState GetState() const { return mState; } void SetState(eState state) { mState = state; } ImVec2 GetSize() { return mSize; } void SetSize(ImVec2 size) { mSize = size; } private: ImGuiWindowFlags mFlag; eState mState; ImVec2 mSize; }; }
C++
복사

4. GUILayout 클래스

개념

GUILayout자동 레이아웃 시스템을 제공하는 유틸리티 클래스입니다. 개발자가 UI 요소의 정확한 위치와 크기를 계산하지 않아도, 자동으로 적절한 배치를 수행합니다.

핵심 책임

자동 레이아웃 계산
요소 간 간격 자동 조정
창 크기에 따른 반응형 배치
일관된 여백 및 패딩 적용
배치 패턴 제공
수평/수직 레이아웃
그리드 레이아웃
플렉스 박스 스타일 배치
UI 구성 단순화
선언적 UI 코드 작성
코드 가독성 향상
유지보수 용이성 증대

레이아웃 타입

Horizontal Layout (수평 배치)
GUILayout::BeginHorizontal(); GUILayout::Label("Name:"); GUILayout::TextField(nameBuffer); GUILayout::Button("Submit"); GUILayout::EndHorizontal();
C++
복사
UI 요소들을 가로로 나란히 배치
자동 간격 조정
남은 공간 균등 분배
Vertical Layout (수직 배치)
GUILayout::BeginVertical(); GUILayout::Label("Settings"); GUILayout::Toggle("Enable Sound", &soundEnabled); GUILayout::Toggle("Enable Music", &musicEnabled); GUILayout::Slider("Volume", &volume, 0.0f, 1.0f); GUILayout::EndVertical();
C++
복사
UI 요소들을 세로로 쌓아서 배치
각 요소의 높이 자동 계산
스크롤 영역 자동 생성
Grid Layout (그리드 배치)
GUILayout::BeginGrid(3, 3); // 3x3 그리드 for (int i = 0; i < 9; i++) { GUILayout::Button($"Button {i}"); } GUILayout::EndGrid();
C++
복사
고정된 행과 열로 요소 배치
각 셀의 크기 자동 조정
인벤토리, 스킬 트리 등에 적합

간격 및 여백 제어

Space (고정 간격)
GUILayout::Space(20.0f); // 20픽셀 간격
C++
복사
FlexibleSpace (유연한 간격)
GUILayout::BeginHorizontal(); GUILayout::Button("Left"); GUILayout::FlexibleSpace(); // 남은 공간 모두 차지 GUILayout::Button("Right"); GUILayout::EndHorizontal();
C++
복사
Padding (여백)
GUILayout::BeginPadding(10, 10, 10, 10); // 상하좌우 10픽셀 // 내용 GUILayout::EndPadding();
C++
복사

정렬 옵션

// 좌측 정렬 GUILayout::BeginHorizontal(Alignment::Left); // 중앙 정렬 GUILayout::BeginHorizontal(Alignment::Center); // 우측 정렬 GUILayout::BeginHorizontal(Alignment::Right); // 양쪽 정렬 (Justify) GUILayout::BeginHorizontal(Alignment::Justify);
C++
복사

반응형 디자인

창 크기 변화 대응
창 크기가 변경되어도 레이아웃 자동 재계산
최소/최대 크기 제약 적용
스크롤바 자동 표시
동적 요소 추가/제거
요소가 동적으로 추가/제거되어도 레이아웃 유지
애니메이션 효과 지원
부드러운 전환

실제 사용 예시

Inspector 창 구현
void InspectorWindow::OnGUI() { GUILayout::BeginVertical(); // 컴포넌트 목록 for (auto& component : selectedObject->GetComponents()) { GUILayout::BeginHorizontal(); // 컴포넌트 이름 GUILayout::Label(component->GetName()); GUILayout::FlexibleSpace(); // 활성화 토글 bool enabled = component->IsEnabled(); if (GUILayout::Toggle("", &enabled)) { component->SetEnabled(enabled); } GUILayout::EndHorizontal(); // 컴포넌트 속성 GUILayout::BeginPadding(20, 0, 0, 0); component->OnInspectorGUI(); GUILayout::EndPadding(); GUILayout::Space(10); } GUILayout::EndVertical(); }
C++
복사
Toolbar 구현
void ToolbarWindow::OnGUI() { GUILayout::BeginHorizontal(); if (GUILayout::Button("Play")) StartGame(); if (GUILayout::Button("Pause")) PauseGame(); if (GUILayout::Button("Stop")) StopGame(); GUILayout::FlexibleSpace(); GUILayout::Label($"FPS: {GetFPS()}"); GUILayout::EndHorizontal(); }
C++
복사

최적화 기법

레이아웃 캐싱
자주 사용되는 레이아웃 구조를 캐싱
재계산 빈도 최소화
메모리와 성능 사이의 균형
Dirty Flag 패턴
변경이 있을 때만 레이아웃 재계산
불필요한 연산 방지
프레임 드롭 최소화

코드 구조

#pragma once namespace gui { class GUILayout { public: // 레이아웃 시작/종료 static void BeginHorizontal(); static void EndHorizontal(); static void BeginVertical(); static void EndVertical(); static void BeginGrid(int rows, int cols); static void EndGrid(); // 간격 및 여백 static void Space(float pixels); static void FlexibleSpace(); static void BeginPadding(float left, float right, float top, float bottom); static void EndPadding(); // UI 요소 static void Label(const std::string& text); static bool Button(const std::string& text); static bool Toggle(const std::string& label, bool* value); static void TextField(char* buffer, size_t bufferSize); static void Slider(const std::string& label, float* value, float min, float max); // 정렬 enum class Alignment { Left, Center, Right, Justify }; static void SetAlignment(Alignment alignment); private: // 내부 레이아웃 상태 관리 static std::stack<LayoutContext> mLayoutStack; static Alignment mCurrentAlignment; static bool mDirty; // 레이아웃 계산 static void CalculateLayout(); static void ApplyLayout(); }; }
C++
복사

클래스 간 관계 및 협력

계층 구조

상호작용 흐름

1. 에디터 시작
2. 메인 루프
3. 객체 선택 시
4. 씬 저장

설계 원칙

단일 책임 원칙 (SRP)
각 클래스가 하나의 명확한 책임만 가짐
EditorApplication은 전체 관리, EditorWindow는 개별 창 관리
개방-폐쇄 원칙 (OCP)
새로운 EditorWindowEditor를 추가해도 기존 코드 수정 불필요
상속을 통한 확장
의존성 역전 원칙 (DIP)
EditorApplication은 구체적인 창이 아닌 추상 EditorWindow에 의존
느슨한 결합으로 유연성 확보

확장 가능성

플러그인 시스템

새로운 기능을 플러그인 형태로 추가할 수 있는 구조:
// 커스텀 에디터 윈도우 플러그인 class MyCustomWindow : public EditorWindow { public: void Initialize() override { SetSize(ImVec2(400, 300)); } void OnGUI() override { GUILayout::BeginVertical(); GUILayout::Label("My Custom Tool"); // 커스텀 기능 구현 GUILayout::EndVertical(); } }; // 에디터에 등록 EditorApplication::RegisterWindow<MyCustomWindow>("MyTool");
C++
복사

스크립팅 지원

향후 스크립팅 언어(Lua, Python 등)로 에디터를 확장할 수 있는 기반 마련

테마 시스템

다양한 색상 테마를 적용할 수 있는 구조

성능 최적화

레이지 로딩
창이 실제로 활성화될 때만 초기화
메모리 사용량 감소
더티 플래그
변경된 내용만 업데이트
불필요한 재렌더링 방지
오브젝트 풀링
UI 요소 재사용
동적 할당 최소화
멀티스레딩
에셋 로딩을 별도 스레드에서 처리
메인 스레드 블로킹 방지

결론

본 에디터 아키텍처는 확장성, 유지보수성, 성능을 모두 고려한 견고한 설계입니다. 각 클래스가 명확한 책임을 가지고 있으며, 서로 느슨하게 결합되어 있어 새로운 기능을 추가하거나 기존 기능을 수정하기 용이합니다.
특히 GUILayout 클래스를 통한 선언적 UI 작성 방식은 개발 생산성을 크게 향상시키며, EditorWindowEditor의 분리는 도구와 컨텐츠 편집 기능을 독립적으로 발전시킬 수 있게 합니다.
향후 플러그인 시스템, 스크립팅 지원, 협업 기능 등을 추가하여 더욱 강력한 게임 개발 환경을 구축할 수 있을 것입니다.