오늘 한 것은 작업 목표 체크리스트 HUD를 서버 데이터에 연결한 것이다. 위젯이 ACWGameState를 직접 구독하지 않고 ViewModel을 한 겹 두는 구조로 갔다.
GameState ─델리게이트→ ObjectiveViewModel ─FieldNotify→ ObjectivePanel
위젯이 GameState를 직접 알게 하면 화면이 늘어날 때마다 같은 구독·해제 코드가 복사된다. 한 겹을 두면 그 책임이 ViewModel 한 곳으로 모인다. 대신 ViewModel이 떠안아야 할 것이 생긴다. 정리한 것은 아래 네 가지다.
Edit → Plugins에서 UMG Viewmodel을 체크했다. 그런데 나중에 .uproject를 열어보니 항목이 없었다. Saved/, Intermediate/, Binaries/ 어디에도 흔적이 없었다. 체크만 하고 재시작을 하지 않아 프로젝트 파일에 저장되지 않은 상태였다.
{
"Name": "ModelViewViewModel",
"Enabled": true
}
에디터 UI의 체크 상태와 파일 내용은 별개다. 직접 열어서 봐야 한다.
그리고 플러그인만 켜면 블루프린트 MVVM까지만 된다. UMVVMViewModelBase를 상속한 C++ 클래스를 만들려면 모듈이 따로 필요하다.
"ModelViewViewModel",
"FieldNotification", // 세트다. 하나만 넣으면 링크 에러
ViewModel을 쓰기로 했으면 위젯이 받을 수 있는 모양이 뭔지부터 봐야 한다. 서버 구조체를 열었다.
USTRUCT(BlueprintType)
struct FCWStageObjective
{
UPROPERTY(BlueprintReadOnly) FText DisplayName;
UPROPERTY(BlueprintReadOnly) int32 CompletedCount = 0;
UPROPERTY(BlueprintReadOnly) int32 TotalCount = 0;
UPROPERTY(BlueprintReadOnly) int32 WorkOrder = 0;
};
TArray로 온다. 즉 줄 수가 가변이다. 기획서에 목표가 4줄이라 Entry를 4개 고정 배치해 뒀는데, 실제 로그는 3줄이었다.
[Stage] 작업 존 11개를 찾았습니다.
[Stage] 목표 3줄 (존 1/11)
존 11개를 이름으로 묶어 3줄이 됐고, 맵마다 배치가 다르니 줄 수도 달라진다. 배열을 그대로 받으려면 위젯이 먼저 바뀌어야 했다.
| 전 | 후 |
|---|---|
| Entry 4개 고정 배치 | 빈 VerticalBox |
| 목표별 setter 4개 | RefreshObjectiveEntries(배열) |
| 디자이너에 목표 이름 입력 | 서버 DisplayName 그대로 표시 |
지우는 데 시간이 걸린 건 Entry가 아니라 그래프에 박혀 있던 SetGuidelineProgress() 같은 목표별 setter 4개였다. "목표 = 가이드라인"이 컴파일 타임에 박혀 있는 셈이라 가변 배열과 공존할 수 없다.
완료 여부도 변수로 저장하지 않고 TotalCount > 0 AND CompletedCount >= TotalCount로 매번 계산한다. 이 게임은 차량이 젖은 페인트를 밟으면 끝났던 존이 다시 열린다. 완료가 되돌아가는 데이터는 플래그로 들고 있으면 안 된다.
ViewModel을 만들기 전에 위젯만 더미 배열로 먼저 검증했다. 3개·4개·5개를 넣어 줄 수와 중복 생성만 확인하는 단계인데, 이걸 먼저 끊어두니 이후에 목록이 안 뜰 때 "위젯 문제인가 ViewModel 문제인가"를 고민할 필요가 없었다.
ViewModel 본체다.
UCLASS(BlueprintType)
class UCWObjectiveViewModel : public UMVVMViewModelBase
{
private:
UPROPERTY(BlueprintReadOnly, FieldNotify, meta = (AllowPrivateAccess = "true"))
TArray<FCWStageObjective> Objectives;
};
FieldNotify를 붙였으니 값만 바꾸면 되는 줄 알았는데 아무 일도 일어나지 않았다.
Objectives = InGameState->GetObjectives(); // 알림 안 감
FieldNotify는 필드 ID를 만들어줄 뿐이고, 브로드캐스트는 전용 매크로가 한다.
```cpp
void UCWObjectiveViewModel::SetObjectives(const TArray& InObjectives)
{
// 값이 실제로 변경된 경우에만 MVVM 알림 발생
UE_MVVM_SET_PROPERTY_VALUE(Objectives, InObjectives);
}
| 팀프로젝트 07. 리슨 서버에서 "나"를 찾는 법, 인게임 HUD 플레이어 목록 연동 (0) | 2026.08.11 |
|---|---|
| 팀프로젝트 06. 리슨 서버 대기실 만들면서 마주친 문제 (0) | 2026.08.10 |
| 팀프로젝트 04. 공개 API를 늘렸을 때 깨지는 전제 (0) | 2026.08.06 |
| 팀프로젝트 03. 인게임 HUD 구조와 입력 모드 (0) | 2026.08.05 |
| 팀프로젝트 02. UI 공통 구조 구현 (0) | 2026.08.04 |