게임 흐름에 맞추어 UI 구현






저장하고 새 레벨을 만든다.





// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "SpartaPlayerController.generated.h"
class UInputMappingContext;
class UInputAction;
/**
*
*/
UCLASS()
class SPARTAPROJECT_API ASpartaPlayerController : public APlayerController
{
GENERATED_BODY()
public:
ASpartaPlayerController();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="input")
UInputMappingContext* InputMappingContext;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "input")
UInputAction* MoveAction;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "input")
UInputAction* JumpAction;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "input")
UInputAction* LookAction;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "input")
UInputAction* SprintAction;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HUD")
TSubclassOf<UUserWidget> HUDWidgetClass;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "HUD")
UUserWidget* HUDWidgetInstance; // HUD 갱신용 인스턴스
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Menu")
TSubclassOf<UUserWidget> MainMenuWidgetClass;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Menu")
UUserWidget* MainMenuWidgetInstance; //
UFUNCTION(BlueprintPure, Category = "HUD")
UUserWidget* GetHUDWidget() const;
UFUNCTION(BlueprintCallable, Category = "HUD")
void ShowGameHUD(); // HUD를 불러오는 함수
UFUNCTION(BlueprintCallable, Category = "Menu")
void ShowMainMenu(bool bIsRestart); // MainMenu를 불러오는 함수. 시작인지 재시작인지를 인자로 받음.
UFUNCTION(BlueprintCallable, Category = "Menu")
void StartGame(); // 게임 시작 또는 재시작을 실행하는 함수
protected:
virtual void BeginPlay() override;
};
#include "SpartaPlayerController.h"
#include "SpartaGameState.h"
#include "SpartaGameInstance.h"
#include "EnhancedInputSubsystems.h"
#include "Blueprint/UserWidget.h"
#include "Components/TextBlock.h"
#include "Kismet/GameplayStatics.h"
ASpartaPlayerController::ASpartaPlayerController()
: InputMappingContext(nullptr),
MoveAction(nullptr),
JumpAction(nullptr),
LookAction(nullptr),
SprintAction(nullptr),
HUDWidgetClass(nullptr),
HUDWidgetInstance(nullptr),
MainMenuWidgetClass(nullptr),
MainMenuWidgetInstance(nullptr)
{
}
void ASpartaPlayerController::BeginPlay()
{
Super::BeginPlay();
if (ULocalPlayer* LocalPlayer = GetLocalPlayer())
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem =
LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
{
if (InputMappingContext)
{
Subsystem->AddMappingContext(InputMappingContext, 0);
}
}
}
// MainMenu부터 실행하게 변경되어 삭제
//if (HUDWidgetClass)
//{
// HUDWidgetInstance = CreateWidget<UUserWidget>(this, HUDWidgetClass);
// if (HUDWidgetInstance)
// {
// HUDWidgetInstance->AddToViewport();
// }
//}
ASpartaGameState* SpartaGameState = GetWorld() ? GetWorld()->GetGameState<ASpartaGameState>() : nullptr;
if (SpartaGameState)
{
SpartaGameState->UpdateHUD();
}
// 현재 맵이 메인 메뉴라면 메뉴 UI를 띄운다.
FString CurrentMapName = GetWorld()->GetMapName();
if (CurrentMapName.Contains("MenuLevel"))
{
ShowMainMenu(false);
}
}
UUserWidget* ASpartaPlayerController::GetHUDWidget() const
{
return HUDWidgetInstance;
}
void ASpartaPlayerController::ShowGameHUD()
{
// 이미 띄워져 있는 위젯들이 있으면 삭제
if (HUDWidgetInstance)
{
HUDWidgetInstance->RemoveFromParent();
HUDWidgetInstance = nullptr;
}
if (MainMenuWidgetInstance)
{
MainMenuWidgetInstance->RemoveFromParent();
MainMenuWidgetInstance = nullptr;
}
if (HUDWidgetClass)
{
HUDWidgetInstance = CreateWidget<UUserWidget>(this, HUDWidgetClass);
if (HUDWidgetInstance)
{
HUDWidgetInstance->AddToViewport(); // 메뉴를 뷰포트에 띄우기
bShowMouseCursor = false; // 마우스 커서 보이기
SetInputMode(FInputModeGameOnly()); // UI 인풋만 받기
}
// SpartaGameState에서 UpdateHUD
ASpartaGameState* SpartaGameState = GetWorld() ? GetWorld()->GetGameState<ASpartaGameState>() : nullptr; // 안전 처리 코드
if (SpartaGameState)
{
SpartaGameState->UpdateHUD();
}
}
}
void ASpartaPlayerController::ShowMainMenu(bool bIsRestart)
{
// 이미 띄워져 있는 위젯들이 있으면 삭제
if (HUDWidgetInstance)
{
HUDWidgetInstance->RemoveFromParent();
HUDWidgetInstance = nullptr;
}
if (MainMenuWidgetInstance)
{
MainMenuWidgetInstance->RemoveFromParent();
MainMenuWidgetInstance = nullptr;
}
if (MainMenuWidgetClass)
{
MainMenuWidgetInstance = CreateWidget<UUserWidget>(this, MainMenuWidgetClass);
if (MainMenuWidgetInstance)
{
MainMenuWidgetInstance->AddToViewport(); // 메뉴를 뷰포트에 띄우기
bShowMouseCursor = true; // 마우스 커서 보이기
SetInputMode(FInputModeUIOnly()); // UI 인풋만 받기
}
if (UTextBlock* ButtonText = Cast<UTextBlock>(MainMenuWidgetInstance->GetWidgetFromName(TEXT("StartButtonText"))))
{
// 시작과 재시작 구분
if (bIsRestart)
{
ButtonText->SetText(FText::FromString(TEXT("Restart")));
}
else
{
ButtonText->SetText(FText::FromString(TEXT("Start")));
}
}
}
}
void ASpartaPlayerController::StartGame()
{
// 시작시 인스턴스 초기화
if (USpartaGameInstance* SpartaGameInstance = Cast<USpartaGameInstance>(UGameplayStatics::GetGameInstance(this)))
{
SpartaGameInstance->CurrentLevelIndex = 0;
SpartaGameInstance->TotalScore = 0;
}
UGameplayStatics::OpenLevel(GetWorld(), FName("BasicLevel")); // 월드에 BasicLevel 오픈
}
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameState.h"
#include "SpartaGameState.generated.h"
/**
*
*/
UCLASS()
class SPARTAPROJECT_API ASpartaGameState : public AGameState
{
GENERATED_BODY()
public:
ASpartaGameState();
virtual void BeginPlay() override;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Score")
int32 Score; // 점수
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Coin")
int32 SpawnedCoinCount;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Coin")
int32 CollectedCoinCount;
UFUNCTION(BlueprintPure, Category = "Score")
int32 GetScore() const;
UFUNCTION(BlueprintCallable, Category = "Scores")
void AddScore(int32 Amount);
void OnLevelTimeUp();
void EndLevel();
UFUNCTION(BlueprintCallable, Category = "Scores")
void OnGameOver();
// 레벨 타이머
FTimerHandle LevelTimerHandle;
FTimerHandle HUDUpdateTimerHandle;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Level")
float LevelDuration;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Level")
int32 CurrentLevelIndex;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Level")
int32 MaxLevels;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Level")
TArray<FName> LevelMapNames;
void StartLevel();
void OnCoinCollected();
void UpdateHUD();
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "SpartaGameState.h"
#include "SpawnVolume.h"
#include "CoinItem.h"
#include "SpartaGameInstance.h"
#include "SpartaPlayerController.h"
#include "Kismet/GameplayStatics.h"
#include "Components/TextBlock.h"
#include "Blueprint/UserWidget.h"
ASpartaGameState::ASpartaGameState()
{
// 변수 초기화
Score = 0;
SpawnedCoinCount = 0;
CollectedCoinCount = 0;
LevelDuration = 30.0;
CurrentLevelIndex = 0;
MaxLevels = 3;
}
void ASpartaGameState::BeginPlay()
{
Super::BeginPlay();
// UpdateHUD();
StartLevel(); // 레벨 시작
GetWorldTimerManager().SetTimer(
HUDUpdateTimerHandle,
this,
&ASpartaGameState::UpdateHUD,
0.1f,
true
);
}
int32 ASpartaGameState::GetScore() const
{
return Score;
}
void ASpartaGameState::AddScore(int32 Amount)
{
if (UGameInstance* GameInstance = GetGameInstance())
{
USpartaGameInstance* SpartaGameInstance = Cast<USpartaGameInstance>(GameInstance);
if (SpartaGameInstance)
{
SpartaGameInstance->AddToScore(Amount);
}
}
Score += Amount; // 점수 획득
UpdateHUD();
UE_LOG(LogTemp, Warning, TEXT("Score: %d"), Score);
}
void ASpartaGameState::OnLevelTimeUp()
{
EndLevel();
}
void ASpartaGameState::EndLevel()
{
GetWorldTimerManager().ClearTimer(LevelTimerHandle);
CurrentLevelIndex++;
if (UGameInstance* GameInstance = GetGameInstance())
{
USpartaGameInstance* SpartaGameInstance = Cast<USpartaGameInstance>(GameInstance);
if (SpartaGameInstance)
{
AddScore(Score);
SpartaGameInstance->CurrentLevelIndex = CurrentLevelIndex;
}
}
if (CurrentLevelIndex >= MaxLevels)
{
OnGameOver();
return;
}
if (LevelMapNames.IsValidIndex(CurrentLevelIndex))
{
UGameplayStatics::OpenLevel(GetWorld(), LevelMapNames[CurrentLevelIndex]); // 다음 레벨 열기 / 다음 레벨이 열리면 GameState도 새로 생성됨
}
else
{
OnGameOver();
}
}
void ASpartaGameState::OnGameOver()
{
// UpdateHUD();
// UE_LOG(LogTemp, Warning, TEXT("Game Over!!"));
// MainMenu UI 열기
if (APlayerController* PlayerController = GetWorld()->GetFirstPlayerController())
{
if (ASpartaPlayerController* SpartaPlayerController = Cast<ASpartaPlayerController>(PlayerController))
{
SpartaPlayerController->ShowMainMenu(true); // 레벨이 오픈될 때마다 UI는 HUD로 전환
}
}
}
void ASpartaGameState::StartLevel()
{
// -- 규칙 지정 --
// UI - 게임 시작 버튼
// 레벨에서 40개의 아이템이 소환이 된다.
// 코인을 다 얻으면 시간제한 상관없이 다음 레벨로 넘어간다.
// 한 레벨당 30초의 시간이 주어지며, 타임오버가 되어도 바로 다음 레벨로 넘어갈 수 있다.
// 체력이 0이 되면 바로 게임오버가 된다.
// UI - 게임 종료 이후 재시작을 할 수 있다.
// 레벨이 시작될 때 동작
// HUD
if (APlayerController* PlayerController = GetWorld()->GetFirstPlayerController())
{
if (ASpartaPlayerController* SpartaPlayerController = Cast<ASpartaPlayerController>(PlayerController))
{
SpartaPlayerController->ShowGameHUD(); // 레벨이 오픈될 때마다 UI는 HUD로 전환
}
}
if (UGameInstance* GameInstance = GetGameInstance())
{
USpartaGameInstance* SpartaGameInstance = Cast<USpartaGameInstance>(GameInstance);
if (SpartaGameInstance)
{
CurrentLevelIndex = SpartaGameInstance->CurrentLevelIndex;
}
}
SpawnedCoinCount = 0; // 스폰된 코인 카운트
CollectedCoinCount = 0; // 수집된 코인 카운트
TArray<AActor*> FoundVolumes; // SpawnVolume 찾기
UGameplayStatics::GetAllActorsOfClass(GetWorld(), ASpawnVolume::StaticClass(), FoundVolumes); // 현재 월드에 있는 해당 액터를 모두 가져옴
const int32 ItemToSpawn = 40;
for (int32 i = 0; i < ItemToSpawn; i++)
{
if (FoundVolumes.Num() > 0)
{
ASpawnVolume* SpawnVolume = Cast<ASpawnVolume>(FoundVolumes[0]);
if (SpawnVolume)
{
AActor* SpawnedActor = SpawnVolume->SpawnRandomItem();
if (SpawnedActor && SpawnedActor->IsA(ACoinItem::StaticClass())) // IsA는 해당 클래스인지 아닌지 체크하며 하위 클래스까지 확인을 해준다.
{
SpawnedCoinCount++;
}
}
}
}
// UpdateHUD();
GetWorldTimerManager().SetTimer(
LevelTimerHandle,
this,
&ASpartaGameState::OnLevelTimeUp,
LevelDuration,
false
);
// UE_LOG(LogTemp, Warning, TEXT("Level %d Start! Spawned %d coin."), CurrentLevelIndex + 1, SpawnedCoinCount);
}
void ASpartaGameState::OnCoinCollected()
{
CollectedCoinCount++;
UE_LOG(LogTemp, Warning, TEXT("Coin Collected: %d / %d"), CollectedCoinCount, SpawnedCoinCount); // 코인 몇 개를 취득했는지 표시
if (SpawnedCoinCount > 0 && CollectedCoinCount >= SpawnedCoinCount)
{
EndLevel();
}
}
void ASpartaGameState::UpdateHUD()
{
if (APlayerController* PlayerController = GetWorld()->GetFirstPlayerController())
{
if (ASpartaPlayerController* SpartaPlayerController = Cast<ASpartaPlayerController>(PlayerController))
{
if (UUserWidget* HUDWidget = SpartaPlayerController->GetHUDWidget())
{
if (UTextBlock* TimeText = Cast<UTextBlock>(HUDWidget->GetWidgetFromName(TEXT("Time"))))
{
float RemainingTime = GetWorldTimerManager().GetTimerRemaining(LevelTimerHandle); // 레벨의 남은 시간 출력
TimeText->SetText(FText::FromString(FString::Printf(TEXT("Time: %.1f"), RemainingTime))); // 위젯의 텍스트를 세팅
}
if (UTextBlock* ScoreText = Cast<UTextBlock>(HUDWidget->GetWidgetFromName(TEXT("Score"))))
{
if (UGameInstance* GameInstance = GetGameInstance())
{
USpartaGameInstance* SpartaGameInstance = Cast<USpartaGameInstance>(GameInstance);
if (SpartaGameInstance)
{
ScoreText->SetText(FText::FromString(FString::Printf(TEXT("Score: %d"), SpartaGameInstance->TotalScore)));
}
}
}
if (UTextBlock* LevelText = Cast<UTextBlock>(HUDWidget->GetWidgetFromName(TEXT("Level"))))
{
LevelText->SetText(FText::FromString(FString::Printf(TEXT("Level: %d"), CurrentLevelIndex + 1))); // 위젯의 텍스트를 세팅
}
}
}
}
}
코드 작성 후 BP_SpartaPlayerController에서 MainMenu를 설정해준다.

START 버튼에 함수 바인딩



창모드로 실행 시 창 크기 강제 지정
- 프로젝트 폴더의 Config 폴더에서 DefaultGameUserSettings.ini 파일 열기
- 아래 내용을 복사해서 붙여넣고 저장.
[/Script/Engine.GameUserSettings]
ResolutionSizeX=1920
ResolutionSizeY=1080
LastUserConfirmedResolutionSizeX=1920
LastUserConfirmedResolutionSizeY=1080
WindowPosX=-1
WindowPosY=-1
FullscreenMode=2