[CommonUI] Start (호스트) / Ready (클라이언트)

김여울·2025년 12월 2일

내일배움캠프

목록 보기
125/139

위젯 안에서 Start / Ready 버튼 나누기

가시성만 바꾸기

WBP_Lobby 안에:

  • StartButton
  • ReadyButton

두 개 다 넣어두고, 가시성만 바꾸는 방식 사용하기

Event Construct
 → Branch (bIsHost)
   ├─ true (호스트일 때)
   │    StartButton → Set Visibility: Visible
   │    ReadyButton → Set Visibility: Collapsed (또는 Hidden)
   └─ false (클라이언트일 때)
        StartButton → Set Visibility: Collapsed
        ReadyButton → Set Visibility: Visible

UI는 기본적으로 각 클라이언트 로컬에만 렌더링되기 때문에
이렇게 분기해도 서로 화면이 섞이지 않는다.

Common UI - Push

CommonUI 쓰는 경우, 구조는 똑같고 "어디에 위젯을 Push 하느냐"만 따로 생각하기

  • Create Widget (WBP_Lobby)
    Push ContentPush Layer로 CommonActivatableWidget 띄우기
  • 여전히 bIsHost bool은 Exposed on Spawn으로 넘겨주면 동일하게 작동함

"ViewPort에 직접 올리냐 / CommonUI 스택에 올리냐" 만 다름

WBP_Lobby


Overlay

  • 자식들을 정확히 같은 공간에 겹쳐서 배치하는 컨테이너
  • Canvas와 다르게 자동 위치 중심 정렬이 더 깔끔함
  • 겹쳐놓고 Visibility 토글하면 완벽히 한 버튼처럼 보임

LobbyWidget.cpp

void ULobbyWidget::NativeConstruct()
{
	Super::NativeConstruct();

	// Host 여부 확인 GS에서 GI로 수정
	if (ULNGameInstance* GI = GetGameInstance<ULNGameInstance>())
	{
		bIsHost = GI->IsLocalHost();
	}

	// 버튼 이벤트 바인딩
	if (Btn_Back)
	{
		Btn_Back->OnClicked().AddUObject(this, &ULobbyWidget::OnBackClicked);
	}

	if (Btn_Start)
	{
		Btn_Start->OnClicked().AddUObject(this, &ULobbyWidget::OnStartClicked);
	}

	if (Btn_Ready)
	{
		Btn_Ready->OnClicked().AddUObject(this, &ULobbyWidget::OnReadyClicked);
	}

	// Host / Client - UI 분기
	if (bIsHost)
	{
		Btn_Start->SetVisibility(ESlateVisibility::Visible);
		Btn_Ready->SetVisibility(ESlateVisibility::Collapsed);
	}
	else
	{
		Btn_Start->SetVisibility(ESlateVisibility::Collapsed);
		Btn_Ready->SetVisibility(ESlateVisibility::Visible);
	}
    
	// ...
}

void ULobbyWidget::OnStartClicked()
{
	UE_LOG(LogTemp, Warning, TEXT("START clicked"));

	if (!bIsHost) 
	{
		return;
	}

	APlayerController* PC = GetOwningPlayer();
	if (!PC) 
	{
		return;
	}

	if (ALNControllerInGame* C = Cast<ALNControllerInGame>(PC))
	{
		C->Server_RequestStart(); 
	}
}

void ULobbyWidget::OnReadyClicked()
{
	UE_LOG(LogTemp, Warning, TEXT("READY clicked"));

	APlayerController* PC = GetOwningPlayer();
	if (!PC) 
	{
		return;
	}

	if (ALNControllerInGame* C = Cast<ALNControllerInGame>(PC))
	{
		C->ToggleReadyState();
	}

	// Ready 버튼만 비활성화 (UI 유지)
	// Btn_Ready->SetIsEnabled(false);
}

// 플레이어 모두 Ready -> Start 버튼 활성화
void ULobbyWidget::OnAllReadyChanged(bool bReady)
{
	UE_LOG(LogTemp, Warning, TEXT("AllReady Changed: %d"), bReady ? 1 : 0);

	if (bIsHost && Btn_Start)
	{
		Btn_Start->SetIsEnabled(bReady);
		Btn_Start->SetRenderOpacity(bReady ? 1.0f : 0.4f);
	}
}

PlayerCount

접속한 플레이어 수를 보여주는 UI
CommonText로 만들기

// LobbyWidget.h
FTimerHandle PlayerCountTimerHandle;

UFUNCTION()
void UpdatePlayerCount();
    
// LobbyWidget.cpp
void ULobbyWidget::NativeConstruct()
{
	// ...
    
	ALNGameState* GS = GetWorld()->GetGameState<ALNGameState>();
	if (GS)
	{
		// ...
		// 0.2초마다 UI 자동 갱신
		GetWorld()->GetTimerManager().SetTimer(
			PlayerCountTimerHandle,
			this,
			&ULobbyWidget::UpdatePlayerCount,
			0.2f,
			true
		);
	}
    
 void ULobbyWidget::UpdatePlayerCount()
{
	if (!CT_PlayerCount) 
	{
		return;
	}

	ALNGameState* GS = GetWorld()->GetGameState<ALNGameState>();
	if (!GS)
	{
		return;
	}

	const int32 CurrentPlayers = GS->PlayerArray.Num();
	const int32 MaxPlayers = GS->MaxPlayers;

	CT_PlayerCount->SetText(
		FText::FromString(FString::Printf(TEXT("%d / %d"), CurrentPlayers, MaxPlayers))
	);
}

0개의 댓글