
메뉴 UI 만들기
HP HUD 만들기

![]() | ![]() |
|---|
![]() | Start Game은 잠시 후 구현할 함수![]() |
|---|
// header
UCLASS()
class SPARTPROJECT_API AMyPlayerController : public APlayerController
{
// ... //
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Menu")
TSubclassOf<UUserWidget> MainMenuWidgetClass;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Menu")
UUserWidget* MainMenuWidgetInstance;
UFUNCTION(BlueprintCallable, Category = "HUD")
void ShowHUD();
UFUNCTION(BlueprintCallable, Category = "Menu")
void ShowMainMenu(bool bIsRestart);
UFUNCTION(BlueprintCallable, Category = "Menu")
void StartGame();
};
ShowMainMenu : 게임 재시작일 경우 Start대신 Restart 텍스트 출력StartGame : Start 또는 Restart 버튼 클릭 시 게임 시작시키는 함수// cpp
void AMyPlayerController::BeginPlay()
{
// ... //
// 레벨(맵)이 바뀔 때마다, 플레이어컨트롤도 새롭게 생성됨
// 따라서 여기 BeginPlay에서 현재 Menu일경우, 메뉴 UI 띄우도록 설정
FString CurrentMapName = GetWorld()->GetMapName();
if (CurrentMapName.Contains("MenuLevel"))
{
ShowMainMenu(false);
}
}
void AMyPlayerController::ShowMainMenu(bool bIsRestart)
{
// HUD가 화면에 띄어져있다면, 화면에서 제거
if (HUDWidgetInstance)
{
HUDWidgetInstance->RemoveFromParent();
HUDWidgetInstance = nullptr;
}
// MainMenu도 혹시나 있다면, 제거
if (MainMenuWidgetInstance)
{
MainMenuWidgetInstance->RemoveFromParent();
MainMenuWidgetInstance = nullptr;
}
// MainMenuWidget 생성
if (MainMenuWidgetClass)
{
MainMenuWidgetInstance= CreateWidget<UUserWidget>(this, MainMenuWidgetClass);
if (MainMenuWidgetInstance)
{
MainMenuWidgetInstance->AddToViewport();
// 메뉴창에서는 마우스 커서가 보여야 함
bShowMouseCursor = true;
// Input이 UI에만 영향줘야지, 게임에 영향주면 안 됨
SetInputMode(FInputModeUIOnly());
// 텍스트 연동
if (UTextBlock* TextBlock = Cast<UTextBlock>(MainMenuWidgetInstance
->GetWidgetFromName("StartButtonText")))
{
if (bIsRestart)
TextBlock->SetText(FText::FromString(TEXT("Restart")));
else
TextBlock->SetText(FText::FromString(TEXT("Start")));
}
}
}
}
// ShowMainMenu와 비슷
void AMyPlayerController::ShowHUD()
{
// HUD, MainMenu 인스턴스 제거 로직
// HUD 생성
if (HUDWidgetClass)
{
HUDWidgetInstance = CreateWidget<UUserWidget>(this, HUDWidgetClass);
if (HUDWidgetInstance)
{
HUDWidgetInstance->AddToViewport();
// 인게임이므로, 마우스 안 보이게
bShowMouseCursor = false;
// Input이 게임에만 영향주도록
SetInputMode(FInputModeGameOnly());
}
}
}
void AMyPlayerController::StartGame()
{
// 게임을 재시작 시키려면, GameInstance에 있는 데이터들을 처음으로 초기화해주면 됨
if (UMyGameInstance* MyGameInstance = Cast<UMyGameInstance>(
UGameplayStatics::GetGameInstance(this)))
{
MyGameInstance->TotalScore = 0;
MyGameInstance->CurrentLevelIndex = 0;
}
// 그리고 게임 시작해주기(레벨 열기)
UGameplayStatics::OpenLevel(GetWorld(), FName("BasicLevel"));
}
RemoveFromViewport : 옛날 방식RemoveFromParent : 부모 위젯이 있으면 부모로부터 제거. 부모 없이 그냥 뷰포트에 있는경우 뷰포트에서 제거
// cpp
void AMyGameState::StartLevel()
{
// ... //
// 레벨 시작했으니 HUD 인스턴스 있으면 표시
if (APlayerController* PlayerController = GetWorld()->GetFirstPlayerController())
{
if (AMyPlayerController* MyPlayerController =
Cast<AMyPlayerController>(PlayerController))
{
MyPlayerController->ShowHUD();
// 게임 일시정지 해제
MyPlayerController->SetPause(false);
}
}
// ... //
}
void AMyGameState::OnGameOver()
{
// 게임 종료 시, 메인 메뉴 UI 띄우기
if (APlayerController* PlayerController = GetWorld()->GetFirstPlayerController())
{
if (AMyPlayerController* MyPlayerController =
Cast<AMyPlayerController>(PlayerController))
{
// 게임 일시정지
MyPlayerController->SetPause(true);
MyPlayerController->ShowMainMenu(true);
}
}
}

기존 메인 메뉴 UI를 수정하여 게임 오버 시, 사진과 같이 게임 오버와 점수 뜨도록 구현
Game Over!의 경우, Render Opacity 애니메이션을 통해 효과를 추가



void AMyPlayerController::ShowMainMenu(bool bIsRestart)
{
// TextBlock 찾아서 Total Score 연동해주는 로직
UTextBlock* TotalScoreTextBlock = Cast<UTextBlock>(MainMenuWidgetInstance
->GetWidgetFromName(FName(TEXT("TotalScoreText"))));
if (TotalScoreTextBlock)
{
UMyGameInstance* MyGameInstance = GetWorld()
->GetGameInstance<UMyGameInstance>();
if (MyGameInstance)
{
TotalScoreTextBlock->SetText(FText::FromString(FString::Printf(
TEXT("Total Score : %d"), MyGameInstance->TotalScore)));
}
}
// 재시작 시, OnGameOver 함수 호출
if (bIsRestart)
{
// 이름으로 함수 찾기
UFunction* OnGameOver = MainMenuWidgetInstance
->FindFunction(TEXT("OnGameOver"));
if (OnGameOver)
{
MainMenuWidgetInstance->ProcessEvent(OnGameOver, nullptr);
}
}
}
ProcessEvent : 이벤트 실행. 함수의 매개인자 없을경우 nullptr 넣어줌체력 UI도 마찬가지로, 위젯 블루프린트(WBP_HP)를 새로 만들고, 텍스트만 추가
캐릭터 클래스에 위젯 컴포넌트 추가하여 만든 위젯을 사용
위젯을 3D World에 배치할 수 있게 해주는 도구
2D로만 보이던 위젯을 객체에 붙여, 3D월드에 붙여주는 컴포넌트
// MyCharacter.h
class UWidgetComponent;
UCLASS()
class SPARTPROJECT_API AMyCharacter : public ACharacter
{
// ... //
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI")
UWidgetComponent* OverHeadWidget;
void UpdateOverHeadHP();
};
// MyCharacter.cpp
#include "Components/WidgetComponent.h"
AMyCharacter::AMyCharacter()
{
OverHeadWidget= CreateDefaultSubobject<UWidgetComponent>(TEXT("OverHeadWidget"));
// 스켈레탈 메시 컴포넌트에 부착
OverHeadWidget->SetupAttachment(GetMesh());
OverHeadWidget->SetWidgetSpace(EWidgetSpace::Screen);
}
void AMyCharacter::UpdateOverHeadHP()
{
if (!OverHeadWidget) return;
UUserWidget* OverHeadWidgetInstance = OverHeadWidget->GetUserWidgetObject();
if (OverHeadWidgetInstance)
{
UTextBlock* OverHeadText = Cast<UTextBlock>(OverHeadWidgetInstance
->GetWidgetFromName(FName(TEXT("OverHeadHP"))));
if (OverHeadText)
{
OverHeadText->SetText(FText::FromString(
FString::Printf(TEXT("HP : %.0f / %.0f"), Health, MaxHealth)));
}
}
}
GetMesh : 부모 클래스(캐릭터)에 있는 함수로, 스켈레탈 메시 컴포넌트를 가져옴SetWidgetSpace : 스크린 모드, 월드 모드 중에 선택EWidgetSpace::Screen : 스크린 모드EWidgetSpace::World : 월드 모드GetUserWidgetObject : 위젯 컴포넌트에 연결된 '사용자 정의 UserWidget 객체' 반환. 내가 생성한 UI 클래스 인스턴스를 얻을 때 사용
GetWidget : 내부 UI 구조에서 좀 더 낮은 레벨의 위젯 포인터를 반환UpdateOverHeadHP를 BeginPlay, AddHealth, TakeDamage 함수에 추가하여 호출

OverHeadWidget컴포넌트 디테일 창에서 Widget Class를 설정.
위젯 위치를 확인하기 위해, Space를 World로 바꾸고 위치 수정 후, 다시 Screen으로
