Search
Duplicate

LV24 UIManager

게임 UI ( user interface ) 게임과 유저 사이의 의사소통을 위해서 물리적 가상적 매개체로 가시적으로 보이는 화면 ( 버튼, 스크룰 )과 터치, 드래그 와 같은 조작 방법을 이야기 한다.
1.
풀 화면 : 하나의 장면이 스크린에 꽉 채워지는 형태를 말한다.
[ 브롤스타즈 풀 화면 ] 2. 팝업 화면 ( 팝업 : 갑자기 툭 튀어 나오는 이라는 뜻을 가짐 ) : 하나의 장면이 기존 화면 위에 팝업으로 뜨는 형태를 말한다.
[ 브롤스타즈 팝업 화면 ]
게임에서는 화면 단위로 UI를 제공하고 컨트롤 함으로써 유저와의 상호작용이 일어난다.
: 타이틀 화면에서 게임 스타트 버튼을 눌러서 게임에 들어가는 것, 로비 화면에서 버튼을 눌러서 게임을 시작하는 것 등 게임의 시작부터 끝까지 모두 화면으로 시작해서 화면으로 끝나는 것을 알 수 있다.
어떻게 게임에서는 이런 화면 단위를 관리해야하는가?
: Android에서 Activity를 관리하는 방식을 생각한다면 쉽게 UI를 어떻게 관리할지 알 수 있다.
: Android에서는 Activity ( = 하나의 화면이라고 할 수 있는데) 를 관리할 때에 Stack 구조를 사용하여 관리한다.
● Stack 이란?
: Last-In-First-Out 형태의 데이터 구조
: 위의 그림에서 볼 수 있듯이 Activity 1이 있는 상태에서 Activity 2가 들어오면 Activity 1 위에 쌓이고, Activity 3이 들어오면 Acivitiy 2 위에 쌓이는 형태에서, Back 버튼을 누르면 가장 위에 있는 Activity3 이 없어지는 것을 볼 수 있다.
게임 UI Stack 구조를 적용 한다면 어떤 모습이 되는가.
: 예를 들어 킹오파 익스트림 매치의 플로우를 본다면
게임 UI Stack 구조를 적용 한다면 어떤 모습이 되는가.
: 예를 들어 킹오파 익스트림 매치의 플로우를 본다면
1. 로비 화면 ( 풀 화면 )
2. 로비에서 스테이지 버튼 클릭 -> 스테이지 화면 ( 풀 화면 )
3. 스테이지 화면에서 스테이지 선택 -> 스테이지 정보 화면 ( 팝업 화면 )
4. 스테이지 정보 화면에서 순위 버튼 선택 -> 순위 화면 ( 팝업 화면 )
5. 순위 화면을 닫은 경우 ( 3번 화면으로 이동 )
6. 스테이지 정보 화면을 닫은 경우 ( 2번 화면으로 이동 )
1.
스테이지 화면을 닫은 경우 ( 1번 화면으로 이동 ) : 위의 플로우를 도식화 한다면 다음과 같다.
: 유저의 상호작용에 의해 변화되는 화면이 안드로이드의 Activity 관리 ( Stack )와 동일함을 알 수 있다.
혹여 같은 프레임 안에서 동시에 UI가 실행될수 있기 때문에 스택에 담기전에 메세지큐를 통해서 한번더 받아두고 한프레임단위로 UI를 화면에 띄어준다.
void UIManager::Update() { std::stack<UIBase*> uiBases = mUIBases; while (!uiBases.empty()) { UIBase* uiBase = uiBases.top(); if (uiBase) { uiBase->Update(); uiBases.pop(); } } if (mRequestUiQueue.size() > 0) { eUIType requestUI = mRequestUiQueue.front(); mRequestUiQueue.pop(); OnLoad(requestUI); } } void UIManager::OnLoad(eUIType type) { std::unordered_map<eUIType, UIBase*>::iterator iter = mUIs.find(type); if (iter == mUIs.end()) { OnFail(); return; } OnComplete(iter->second); }
C++
복사
모든 화면을 Stack으로만 관리한다면 UI를 쉽게 관리할 수 있을까.
Stack만으로는 게임의 로직에 맞는 화면 구성을 할 수 없다.
위에서 정의한 풀 화면 또는 팝업 화면에 따라 관리하는 방식에 약간의 차이가 있기 때문에 해당 부분을 처리해 주어야 한다.
: 풀 화면이 2개 이상 쌓여있는 경우, 가장 최상단의 풀 화면만을 볼 수 있기 때문에 하위의 풀화면은 최적화를 위해 숨김 처리를 해주어야 한다.
: 팝업 화면이 2개 이상 쌓여있는 경우도 팝업 화면의 집중도를 위해 최상단의 팝업 화면을 제외한 하위의 팝업 화면은 최적화를 위해 숨김 처리를 해주어야 한다.
: 킹오파 익스트림 매치, 브롤스타즈 등의 게임에서 화면이 어떻게 처리되고 있는지를 보면 위와 같이 처리됨을 알 수 있다.
void UIManager::OnComplete(UIBase* addUI) { if (addUI == nullptr) return; addUI->Initialize(); addUI->Active(); addUI->Update(); // 만약에 현재 추가된 ui가 전체화면이라면 // 전체화면인 ui 말고 나머지를 전부 비활성화 if (addUI->IsFullScreen()) { std::stack<UIBase*> uiBases = mUIBases; while (!uiBases.empty()) { UIBase* uiBase = uiBases.top(); uiBases.pop(); if (uiBase) { uiBase->InActive(); } } } mUIBases.push(addUI); mActiveUI = nullptr; }
C++
복사
마찬 가지로 UI를 POP해줄떄에도 이전에 숨김처리 해둔 UI들을 전부 활성화시켜준다.
void UIManager::Pop(eUIType type) { if (mUIBases.size() <= 0) return; // 해당 ui 한개만 스택에서 뺴줘야한다. std::stack<UIBase*> tempStack; UIBase* uibase = nullptr; while (mUIBases.size() > 0) { uibase = mUIBases.top(); mUIBases.pop(); if (uibase->GetType() != type) { tempStack.push(uibase); continue; } if (uibase->IsFullScreen()) { std::stack<UIBase*> uiBases = mUIBases; while (!uiBases.empty()) { UIBase* uiBase = uiBases.top(); uiBases.pop(); if (uiBase) { uiBase->Active(); break; } } } uibase->UIClear(); } while (tempStack.size() > 0) { uibase = tempStack.top(); tempStack.pop(); mUIBases.push(uibase); } }
C++
복사