카테고리 없음

0807

iosers0564 2026. 8. 7. 21:10

미니맵 구현 / 자원 HUD 연동 / Common UI 시작

날짜 : 2026.08.07

오늘은 기존 선택 시스템 작업 이후에 이어서 미니맵 기능을 프로토타입 수준까지 구현하고, 자원 HUD를 실제 GameState 데이터에 연결한 뒤 Common UI 기반 건물 UI 작업을 시작했다.


1. 미니맵 좌표 변환 구현

먼저 WBP_SFMinimapPanel과 SFMinimapPanelWidget을 만들고 월드의 위치를 미니맵 UI 좌표로 변환하는 기능을 구현했다.

미니맵에서 사용할 월드 범위를 지정하기 위해:

MapWorldMin
MapWorldMax
 

를 만들었고, 이를 기반으로:

WorldToMinimapPosition()
 

함수를 작성했다.

문제

처음에는 월드 X/Y를 그대로 미니맵 X/Y로 사용했는데 실제 플레이에서는:

위로 이동 → 미니맵 오른쪽
오른쪽 이동 → 미니맵 위쪽
 

처럼 방향이 90도 돌아가 있었다.

해결

RTS 카메라의 화면 방향과 실제 World Axis가 다르다는 것을 확인하고:

미니맵 좌우 ← World Y
미니맵 상하 ← World X
 

로 변경했다.

또 UMG는 아래쪽으로 Y값이 증가하므로 세로축을 반전했다.

배운 점

월드 좌표를 UI 좌표로 변환할 때 단순히 X→X, Y→Y로 생각하면 안 되고, 실제 게임의 카메라 방향과 UI 좌표계까지 같이 고려해야 한다.


2. 카메라 위치를 미니맵에 표시

PlayerViewIndicator를 만들어 현재 플레이어가 보고 있는 위치를 미니맵에 표시했다.

처음에는 Camera Pawn의:

GetActorLocation()
 

을 사용했다.

문제

카메라는 월드 중앙 (0, 0)을 바라보고 있는데도 미니맵 Indicator가 구석에 표시됐다.

원인

RTS 카메라는 공중에서 지면을 비스듬하게 바라보기 때문에:

카메라의 실제 위치
≠
플레이어가 화면에서 보고 있는 지면 위치
 

였다.

해결

GetPlayerViewPoint()
→ Camera Location
→ Camera Rotation
→ View Direction
→ Ground Z와 교차점 계산
 

을 통해 카메라가 실제로 바라보고 있는 지면 좌표를 구했다.

그 위치를 WorldToMinimapPosition()으로 변환하면서 실제 화면 이동과 미니맵 Indicator 위치가 맞게 되었다.


3. 미니맵 Indicator 영역 이탈 문제 해결

카메라 Indicator가 미니맵 끝까지 이동하면 Border의 절반이 Canvas 밖으로 나가는 문제가 있었다.

처음에는 좌표 자체만:

0 ~ MinimapSize
 

로 Clamp했지만 해결되지 않았다.

원인

Indicator의 위치는 위젯의 중심점을 기준으로 하기 때문에 좌표가 끝점에 도달하면 위젯 크기의 절반은 밖으로 나가게 된다.

해결

Marker의:

Size
Alignment
MinimapSize
 

를 모두 고려해 실제 이동 가능한 최소/최대 좌표를 계산했다.

공통 함수:

SetMarkerLayerPosition()
 

을 만들고 카메라 Indicator와 다른 미니맵 Marker가 모두 이 함수를 사용하도록 정리했다.

배운 점

UI Clamp는 좌표만 막는 것이 아니라 Widget Size와 Alignment까지 고려해야 한다.


4. 미니맵 Actor Marker 구현

미니맵에 월드 Actor를 표시하기 위해 먼저 테스트 건물 하나를 사용했다.

Actor Tag:

MinimapTest
 

를 이용해 Actor를 찾고:

Actor Location
→ WorldToMinimapPosition()
→ Marker Position
 

으로 표시했다.

처음에는 Marker가 나타나지 않았는데, MinimapTest를 Actor Tag가 아니라 Component Tag에 넣었던 것이 원인이었다.

Actor Tag로 옮긴 뒤 정상적으로 표시됐다.


5. 다중 Marker 및 종류 구분

한 개의 건물만 표시하던 구조를 여러 Actor를 표시할 수 있도록 확장했다.

SFMinimapMarkerInfo.h를 만들고:

ESFMinimapMarkerType

None
Ally
Enemy
Building
 

을 정의했다.

Marker 하나의 정보를:

FSFMinimapMarkerEntry

TargetActor
MarkerWidget
MarkerType
 

로 묶어 배열로 관리하도록 변경했다.

이후 테스트용 Tag도:

MinimapAlly
MinimapEnemy
MinimapBuilding
 

으로 분리했다.

각 타입마다:

Marker Size
Marker Color
 

를 다르게 지정해 아군 / 적군 / 건물을 미니맵에서 구분할 수 있게 했다.

미니맵 현재 완료 상태

☑ World → Minimap 좌표 변환
☑ 카메라 시야 위치 표시
☑ 카메라 이동 실시간 갱신
☑ Marker Clamp
☑ 여러 Actor Marker 표시
☑ Ally / Enemy / Building 구분
☑ Marker 색상 구분
☑ Marker 크기 구분
 

6. 미니맵 최종 구조 방향 결정

런타임에 새로 생산되는 유닛이나 건물을 표시하기 위해 처음에는 일정 시간마다 Actor를 다시 검색하는 방식을 생각했다.

하지만:

1초마다 GetAllActorsWithTag()
 

를 반복하는 것보다 건물이 지어지거나 유닛이 생성되는 순간 Delegate를 받는 방식이 더 적절하다고 판단했다.

최종적으로는:

건물 완공
→ Delegate
→ Building Marker 추가

유닛 생산
→ Delegate
→ Ally / Enemy Marker 추가

사망 / 파괴
→ Delegate
→ Marker 제거
 

방식으로 변경할 예정이다.

현재는 프로토타입 범위에서는 Tag 기반 테스트까지만 완료하고 Delegate 연결은 이후 작업으로 남겼다.


7. 자원 HUD 실제 GameState 연결

기존 Resource HUD는:

광물 800 +5/s
에테르 800 +5/s
인구수 6 / 100
 

같은 테스트 값을 사용하고 있었다.

팀에서 자원 Getter가 구현되어 다음 값을 사용할 수 있게 되었다.

GetMineral()
GetEther()

GetMineralIncomeRate()
GetEtherIncomeRate()

GetCurrentPopulation()
GetMaximumPopulation()
 

SFResourcePanelWidget에서 NativeConstruct() 시 SFGameState를 가져와 최초 자원 값을 HUD에 표시하도록 구현했다.

또한:

OnMineralChanged
OnEtherChanged
OnPopulationChanged
 

Delegate도 바인딩할 수 있도록 구조를 연결했다.


8. GameState 연결 오류 해결

코드를 연결한 뒤에도 HUD에 기존 테스트 값이 계속 남아 있었다.

로그를 확인하니:

SFGameState is invalid.
 

가 출력됐다.

원인

테스트 맵에서 사용하던 GameMode의:

Game State Class
 

가 실제 SFGameState로 설정되어 있지 않았다.

해결

GameMode의 GameState Class를:

SFGameState
 

로 변경했다.

그 후 실제 초기값:

광물 0
에테르 0
인구수 0 / 0
 

이 HUD에 정상적으로 표시됐다.

배운 점

UI 코드가 정상이어도 테스트 맵의 GameMode / GameState 설정이 다르면 원하는 데이터를 받을 수 없다. 데이터 연결 문제를 볼 때 코드뿐 아니라 현재 World가 실제로 어떤 클래스들을 사용하고 있는지도 확인해야 한다.


9. Common UI 기반 건물 UI 시작

건설 카탈로그나 생산 건물 UI처럼 열고 닫히면서 입력 상태를 가지는 화면을 위해 Common UI 작업을 시작했다.

프로젝트에서:

Common UI 활성화
CommonGameViewportClient 적용
 

을 완료했다.

기존 Root HUD는 계속 일반 UserWidget으로 유지하고 Common UI 화면을 올릴 공간만 추가했다.

WBP_SFInGameHUD
├ SelectionPanel
├ ResourcePanel
├ MinimapPanel
└ BuildingUIStack
 

BuildingUIStack은:

CommonActivatableWidgetStack
 

으로 제작했다.


10. WBP_SFBuildingMode 제작

실제 건물 관련 Common UI 화면:

WBP_SFBuildingMode
 

를 CommonActivatableWidget 기반으로 제작했다.

HUD에서는:

OpenBuildingMode()
CloseBuildingMode()
 

를 구현하고:

OpenBuildingMode()
→ BuildingUIStack
→ WBP_SFBuildingMode Push
 

흐름으로 연결했다.

테스트 결과 실제 게임 HUD 위에 Building Mode가 정상적으로 표시되는 것을 확인했다.


11. Common UI 활성화 후 게임 입력이 막히는 문제

Building Mode를 Push한 뒤 갑자기:

WASD X
Mouse Wheel X
기존 IMC 입력 X
 

상태가 됐다.

처음에는 전체 화면 Stack이나 Canvas가 마우스 입력을 막는 문제라고 생각했지만, Hit Test 설정을 변경해도 해결되지 않았다.

원인

CommonActivatableWidget이 활성화되면서 Activation Focus를 가져가고 있었다.

해결

WBP_SFBuildingMode의:

활성화 포커스 지원
 

을 비활성화하자 기존 Enhanced Input이 다시 정상 작동했다.

앞으로의 입력 구조

IMC_SFBase
→ 항상 유지
→ WASD / Zoom

IMC_SFGameplay
→ 일반 플레이

IMC_SFBuildPlacement
→ 건설 배치
 

Common UI 상태와 IMC 전환은 실제 건설 기능을 연결할 때 마무리하기로 했다.

배운 점

Common UI에서 입력이 막혔다고 해서 항상 Hit Test 문제는 아니다. ActivatableWidget의 Activation / Focus / Input Routing도 함께 확인해야 한다.


12. 생산 건물 HUD 틀 제작 시작

Common UI 자체에 생산 UI를 전부 넣기보다는 실제 정보를 보여주는 패널을 별도 UserWidget으로 분리했다.

WBP_SFBuildingMode
→ Common UI 활성화 및 입력 관리

WBP_SFProductionBuildingPanel
→ 생산 건물 정보 표시
 

생산 건물 HUD에 필요한 영역을 먼저 정리했다.

건물 이름
건물 Tier

생산 가능 유닛 버튼

현재 생산 Progress
생산 시간

생산 Queue 최대 5칸
 

처음에는 VerticalBox 안에 모든 영역을 배치했지만 UI가 위에서 아래로 강제로 늘어지는 느낌이 강했다.

그래서 큰 영역은 원하는 위치에 자유롭게 배치할 수 있도록:

ProductionCanvas
 

기반으로 변경하기로 했다.

최종 구조 방향:

ProductionCanvas
├ BuildingHeader
├ UnitProductionArea
├ ProductionProgressArea
└ ProductionQueueArea
 

각 영역 내부의 반복 요소만:

HorizontalBox
 

를 사용한다.

즉:

Canvas Panel
→ 큰 덩어리의 자유로운 위치 배치

Horizontal Box
→ 유닛 버튼 / Queue 등 반복 요소 정렬
 

방식으로 UI를 구성한다.


오늘 완료한 내용

[미니맵]
☑ 좌표 변환
☑ 카메라 Indicator
☑ Marker Clamp
☑ 다중 Marker
☑ Ally / Enemy / Building Marker 구분
☑ Marker 색 / 크기 구분

[자원 HUD]
☑ 실제 GameState Getter 연결
☑ 광물 / 에테르 / 인구수 최초 값 표시
☑ Delegate 바인딩 기반 구현
☑ 잘못된 GameState 설정 문제 해결

[Common UI]
☑ Common UI 기본 설정
☑ CommonGameViewportClient 적용
☑ BuildingUIStack 제작
☑ WBP_SFBuildingMode 제작
☑ Stack Push 테스트
☑ Open / Close 함수 구현
☑ Common UI 입력 포커스 문제 확인 및 해결

[생산 건물 UI]
☑ WBP_SFProductionBuildingPanel 생성
☑ 필요한 정보 영역 정의
☑ 기본 UI 틀 제작
☑ Canvas 기반 자유 배치 방식 결정
 

오늘 배운 점

  • World 좌표와 UI 좌표는 카메라 방향과 좌표축 차이를 고려해야 한다.
  • UI 영역 Clamp 시 Widget의 Size와 Alignment도 계산해야 한다.
  • RTS 카메라 위치를 표시할 때는 Camera Actor 위치보다 실제 카메라가 바라보는 지면 위치가 중요하다.
  • 실시간 생성 대상을 찾기 위해 반복 검색하는 것보다 이벤트/Delegate 기반 등록 구조가 적합하다.
  • UI가 데이터를 못 받는다면 코드뿐 아니라 현재 GameMode/GameState 설정도 확인해야 한다.
  • Common UI에서 입력이 막히는 경우 Hit Test뿐 아니라 Activation Focus와 입력 라우팅을 확인해야 한다.
  • 전체 레이아웃을 VerticalBox에 의존하기보다 Canvas + 내부 Box 조합을 사용하면 자유도와 정렬을 동시에 가져갈 수 있다.

다음 작업

1. Production Building Panel 레이아웃 완성
2. 생산 건물 선택 시 BuildingMode 표시
3. 건물 이름 / Tier 실제 데이터 연결
4. 생산 가능 유닛 UI 연결
5. 생산 Progress / Queue 연결
6. 유닛 생산 요청 연결
7. BuildingMode와 IMC 상태 전환
8. 자원 Delegate 실제 변화 테스트