오늘 할 일
어제 만든 테이블 시스템 위에 플레이어 스탯 데이터 + 컴포넌트 올리고 UI 추가해서 스탯 소비/회복 연결되는지까지 확인하기
확실히 이전에 한 번 해봤다고 금방금방 쳐내지네 ㅋㅋ
UI 시스템
언리얼의 CommonUI 시스템은 너무 잘 갖춰져있어서 가이드대로 따라하면 될 것 같다. 아래는 이전 humanity 프로젝트에 개발된 UI의 문제점 분석임
| 구현 완료 | 구현 미완료 | |
|---|---|---|
| Stack | o | |
| Layer | o | - Stack 한 층당 Layer가 무조건 깔리고, Layer에 Window/HUD/Popup/Widget 순으로 배치될 수 있게 조정해야 함 |
| Window | - 타이틀 창 - 사망 창 - 스킬트리 창 | - Esc 창 - 로딩 창 - 사망 창 → 오버레이 변경 필요 - Retry/Title 버튼 삭제 - 화면 전체 덮는 UI → 반투명 오버레이 - 3초 띄우고 로딩 창 전환 |
| HUD | - 플레이어 스탯 - 퀵슬롯 - 미니맵 - 도움말 - 대형 몬스터 체력 바 | - 미니맵 제거 필요 - 퀵슬롯 데이터 연동 필요 - 재화 상태 |
| Popup | x(사실상 Layer 역할을 수행 중, 잠깐 떴다 사라지는 작은 크기의 UI로 좁혀야 함) | - 상호작용 버튼 팝업 - 보스 클리어 팝업 - NPC/몬스터 대화 팝업 - 사망 팝업 |
| Widget | - 락온 타겟 위젯 - 몬스터 체력 바 | - 대형 몬스터 체력바 - 위젯 → HUD로 변경 필요 |
| 시스템 | - Stack 자료구조 - LayerBase - SubSystemUI(UI 관리자) - PopupBase | - 이름이 Layer인데 UUserWidget 상속 중이거나 반대인 경우 정리 필요 - UBaseWidget ← UUserWidget 인데 UUserWidget 상속하는 곳 있음 - LayerBase 만들어놓고 안쓰는 중 |
| 조작 | - 마우스 - 호버(포커스 효과) - 클릭 효과 - 클릭 이벤트 | - 키보드 화살표 이동 - 게임패드 화살표/스틱 이동 |
| 특이사항 |
- 전반적으로 위계 분리/개념 정의가 되다 말았음
- Popup의 정의가 매우 광범위함
- 화면에 뜨는 모든게 팝업으로 뭉뚱그려져있음
- 텍스트, 상호작용 버튼 팝업 뿐만 아니라 화면 전체를 뒤덮는 ‘창’까지 팝업 상속 중
- 팝업은 화면에 아주 잠깐 떴다 사라지는 작은 크기의 UI를 의미해야 함
- 화면 전체를 덮는 UI는 Window(창)로 재정의 필요
- 대화/사망 텍스트 뒤에 깔릴 Overlay(오버레이) 개념 필요
- Popup의 정의가 매우 광범위함
- 키보드마우스 말고 게임패드 조작이 고려되지 않았음
- QA할 때 임시로 기워넣다보니 여기저기 파편화돼있는 게임패드 조작 기능 모듈화 필요
CommonUI
언리얼의 CommonUI를 사용하면 플랫폼/입력기기 상관없이 Activatable Widget을 Tree of Nodes로 변환해 Input Routing을 구현하고, 플랫폼별 입력 키/아이콘 매핑도 한 곳에서 집중적으로 관리할 수 있게 된다. 굳이 내가 적었던 것처럼 커스텀 계층구조를 만들 필요가 없단 뜻!
전체 흐름
- 월드초기화 후 UI Subsystem 초기화
- 초기화할 때 Root Layer 생성, Layer는 각각 WindowStack, HUDLayer, PopupStack, WidgetLayer로 구성됨
- Layer 생성 후 HUD 켜기, 플레이어 사망 델리게이트 함수 바인딩하기
핵심 개념
Activatable Widget
- 화면에 올라와 있는 UI의 활성상태와 입력 소유권을 관리하는 위젯.
- 단순히 화면에 그려지는 위젯이 UUserWidget이라면 UCommonActivatableWidget은 열림/닫힘, 포커스, 입력 모드까지 관리함.
- Window와 Popup의 부모 클래스.
- HUD나 월드 위젯처럼 계속 떠 있거나 입력 점유가 필요 없는 UI는 일반 Widget/Overlay로 두고, 메뉴/창/팝업처럼 열리고 닫히며 입력을 소유하는 UI만 Activatable로 만듦.
여기서 헷갈리는 점:
- 일반 Widget이 입력을 아예 못받거나 하는건 아님. Activatable Widget과의 차이점은 활성화, 입력 라우팅 관리가 필요없어 항상 떠있는 차이. 일반 Widget도 입력을 받을 수 있음
- 그래서 HUD의 퀵슬롯은 MVWidgetBase를 상속해야 함. CommonUI로 활성/비활성 토글할 기능이 아니기 때문
- 타이틀 메뉴, 설정 메뉴처럼 입력 소유권을 아예 점유해야한다거나, 화면에 띄워졌을 때 해당 UI에만 입력을 제한함으로써 하위 UI에 입력이 흐르는 현상을 방지할 때 Activatable Widget을 쓴다고 이해하면 됨.
UI ↔ 데이터 플로우
UI를 만들었으면 UI에 채울 데이터가 있어야 함. 데이터를 UI에 꽂아줄 때 설계를 "UI가 직접 데이터를 찾아 읽기"보다 "게임 시스템이 UI에 띄우고 싶은 데이터만 전달"하는 방식으로 설계하는게 바람직함.
데이터는 3종류로 나뉜다고 한다.
- 정적 데이터(메뉴 항목 이름, 아이템/스킬/도움말 텍스트 등)
- 런타임 데이터(현재 스탯, 아이템 개수, 재화, 현재 상호작용 대상, 스킬 해금 상태 등)
- 표시용 데이터(ViewData; 퀵슬롯 아이템, 상호작용 팝업 등)
정적 데이터의 경우:
- String Table : 로컬라이징 대상 텍스트
- DataTable : 스킬, 아이템, 몬스터 등 표로 관리하기 편한 데이터들
- PrimaryDataAsset : 메뉴 정의, UI 프리셋 등 에셋 단위로 관리할 데이터
런타임 데이터의 경우:
- Component나 Subsystem이 들고 있는 값을 이벤트로 받아 갱신하기만 하면 됨(Delegate)
표시용 데이터의 경우:
- 각 UI가 알아야하는 최소한의 정보를 갖춘 구조체를 만들고 해당 Payload로 UI에 전달함
메뉴 윈도우는 보통 바뀔 일이 적기 때문에 굳이 DataTable로 안빼도 됨. 빼고싶다면 DataAsset정도로 구조화시키는 정도?
팝업 메시지는 AI가 추천하길 2개의 경로를 두고 즉석 메시지와 테이블 기반 메시지를 노출할 수 있게 하라고 했음.
상호작용 테스트

- InteractionPromptPopup이 DialoguePopup 재생 중에 안사라짐
- DialoguePopup이 상호작용 키 여러번 누르는만큼 중첩됨
내일 할 일
- HUD 달기
- 스테미너 소모 로직 달기
- 상호작용 완성하기
- 대미지 시스템 설계하기