[CH3-04] PauseMenu 구현, IMC, 연결

김여울·2025년 8월 6일

내일배움캠프

목록 보기
55/139

🧩 메뉴 구성

1. Resume 버튼 = Resume_BTN

  • 게임 일시정지 해제 → SetPause(false)
  • 위젯 제거 → PauseMenuWidgetInstance->RemoveFromParent();
  • 인풋 모드 복구 → SetInputMode(FInputModeGameOnly())
  • 마우스 커서 숨기기

2. BackToMainMenu 버튼 = Return_BTN

  • OpenLevel("MainMenuLevel") 호출

3. PauseMenuWidget - PlayerController에 연결

PauseMenu C++ 클래스 만들기

C++ 클래스를 먼저 만들고 블루프린트로 만들어야 하는데 블루프린트 먼저 만들었다
그래서 C++ 클래스 만들어준다.

  1. File → New C++ Class 클릭

  2. 부모 클래스UserWidget 선택

  3. 클래스 이름은 PauseMenuWidget으로 입력

// PauseMenuWidget.h
#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "Components/Button.h"
#include "PauseMenuWidget.generated.h"

/**
 * @class UPauseMenuWidget
 * @brief 게임의 일시 정지 메뉴를 관리하는 위젯 클래스입니다.
 * by Team4 (yeoul)
 */

UCLASS()
class PPP_API UPauseMenuWidget : public UUserWidget
{
	GENERATED_BODY()

protected:
    virtual void NativeConstruct() override;

    UPROPERTY(meta = (BindWidget))
    UButton* Resume_BTN;

    UPROPERTY(meta = (BindWidget))
    UButton* Return_BTN;

    UFUNCTION()
    void OnResumeClicked();

    UFUNCTION()
    void OnReturnClicked();
};


// PauseMenuWidget.cpp
#include "PauseMenuWidget.h"
#include "Components/Button.h"
#include "Kismet/GameplayStatics.h"
#include "../Characters/PppPlayerController.h"

void UPauseMenuWidget::NativeConstruct()
{
    // 부모 함수 먼저 호출
    // 이 위젯이 만들어질 때 버튼을 눌렀을 때 호출할 이벤트 연결
    Super::NativeConstruct();

    if (Resume_BTN)
    {
        Resume_BTN->OnClicked.AddDynamic(this, &UPauseMenuWidget::OnResumeClicked);
    }

    if (Return_BTN)
    {
        Return_BTN->OnClicked.AddDynamic(this, &UPauseMenuWidget::OnReturnClicked);
    }
}

void UPauseMenuWidget::OnResumeClicked()
{
    UE_LOG(LogTemp, Log, TEXT("Resume Clicked"));

    if (APppPlayerController* PC = Cast<APppPlayerController>(UGameplayStatics::GetPlayerController(this, 0)))
    {
        PC->SetPause(false); // 게임 재개
        PC->bShowMouseCursor = false;
        PC->SetInputMode(FInputModeGameOnly());

        this->RemoveFromParent(); // 정지 UI 닫기
    }
}


void UPauseMenuWidget::OnReturnClicked()
{

    if (APppPlayerController* PC = Cast<APppPlayerController>(UGameplayStatics::GetPlayerController(this, 0)))
    {
        PC->ShowMainMenu(true); // MainMenu로 복귀
    }
    UE_LOG(LogTemp, Log, TEXT("Return Clicked"));
}

부모 클래스 수동으로 변경하는 방법

만들어뒀던 블루프린트 위젯의 부모를 C++ 클래스로 설정해준다.

방법 1: 우클릭으로 부모 클래스 변경하기

  1. 콘텐츠 브라우저에서 MainMenuWidget_BP 우클릭

  2. Asset ActionsCreate Blueprint class based on this ← 이거 아님!!

  3. 아니면 그냥 우클릭 하고 “Edit Blueprint Options” 또는 “Reparent Blueprint” 선택

💡 "Reparent Blueprint" 메뉴 안 보이면 에디터 상단 메뉴바
Window > Developer Tools > Class Viewer 켜고 거기서 직접 설정 가능


방법 2: Class Settings → Details 패널에서 Parent Class 직접 수정

  1. MainMenuWidget_BP 열기

  2. 상단 메뉴에서 Class Settings 클릭

  3. 우측 Details 패널 아래로 내리고 “Parent Class”에 직접 UMainMenuWidget 입력

Input Action, IMC, PlayerController 연결하기

  1. Input Action :
    PauseMenuAction → P 키에 매핑됨

  2. IMC :
    IMC_PauseMenu → PauseMenuAction 포함
    ❗나중에 캐릭터의 IMC로 이동❗

  3. Input 등록 위치 :
    PlayerController의 BeginPlay()SetupInputComponent()

  4. UI 표시 :
    PauseMenuWidgetClass로 CreateWidget + AddToViewport

💡 Input Mode 전환 (UIOnly <-> GameOnly) 꼭 해줘야 메뉴 상태가 잘 전환됨

// PlayerController.h
public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI")
        TSubclassOf<UUserWidget> PauseMenuWidgetClass;
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "UI")
        UUserWidget* PauseMenuWidgetInstance;

    UFUNCTION(BlueprintCallable, Category = "UI")
    void ShowPauseMenu();

private:
    UFUNCTION()
    void HandlePauseKey();
    
// PlayerController.cpp
void APppPlayerController::BeginPlay()
{
    Super::BeginPlay();

    EnableInput(this);  // ESC나 메뉴 입력용
    
    if (ULocalPlayer* LocalPlayer = GetLocalPlayer())
    {
        if (UEnhancedInputLocalPlayerSubsystem* SubSystem = LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
        {
            if (PauseMenuIMC)
            {
                SubSystem->AddMappingContext(PauseMenuIMC, 0);
                UE_LOG(LogTemp, Warning, TEXT("PauseMenuIMC 등록 완료"));
            }
            if (PauseMenuIMC)
            {
                SubSystem->AddMappingContext(PauseMenuIMC, 0);
                UE_LOG(LogTemp, Warning, TEXT("PauseMenuIMC 등록 완료: %s"), *PauseMenuIMC->GetName());
            }
            else
            {
                UE_LOG(LogTemp, Error, TEXT("PauseMenuIMC가 NULL입니다!"));
            }
        }
    }

void APppPlayerController::ShowPauseMenu()
{
    if (PauseMenuWidgetInstance)
    {
        PauseMenuWidgetInstance->RemoveFromParent();
        PauseMenuWidgetInstance = nullptr;
    }

    if (PauseMenuWidgetClass)
    {
        PauseMenuWidgetInstance = CreateWidget<UUserWidget>(this, PauseMenuWidgetClass);
        if (PauseMenuWidgetInstance)
        {
            PauseMenuWidgetInstance->AddToViewport();
            PauseMenuWidgetInstance->SetIsFocusable(false);

            FInputModeGameAndUI InputMode;
            InputMode.SetWidgetToFocus(PauseMenuWidgetInstance->TakeWidget());
            InputMode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);

            SetInputMode(InputMode);
            bShowMouseCursor = true;
        }
    }

    if (!IsPaused())
    {
        SetPause(true);
    }
}

void APppPlayerController::SetupInputComponent()
{
    Super::SetupInputComponent();

    if (UEnhancedInputComponent* EnhancedInput = Cast<UEnhancedInputComponent>(InputComponent))
    {
        if (PauseMenuAction)
        {
            EnhancedInput->BindAction(PauseMenuAction, ETriggerEvent::Started, this, &APppPlayerController::HandlePauseKey);
        }
    }
}

void APppPlayerController::HandlePauseKey()
{
    ShowPauseMenu();
}

ESC 키 반응

EnhancedInputComponent → PauseMenuAction → HandlePauseKey()로 처리

ESC 키로 입력하면 에디터 실행창이 꺼져서 P 키로 변경했음

0개의 댓글