C++ 팀 과제 TIL(2)

정완훈·2025년 2월 20일

CP_MainMenu.h

#pragma once

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

UCLASS()
class CYBERPUNK_API UCP_MainMenu : public UUserWidget
{
    GENERATED_BODY()

public:
    UCP_MainMenu(const FObjectInitializer& ObjectInitializer);

protected:
    virtual void NativeConstruct() override;

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

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

private:
    UFUNCTION()
    void OnStartButtonClicked();

    UFUNCTION()
    void OnExitButtonClicked();

    // 게임 재개 또는 종료를 위한 메뉴 표시 함수
    UFUNCTION()
    void ShowPauseMenu();

    // 게임 재개 함수
    UFUNCTION()
    void OnResumeButtonClicked();

    // 게임 종료 함수
    UFUNCTION()
    void OnExitFromPauseButtonClicked();

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

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

    UPROPERTY()
    UUserWidget* PauseMenuWidget; // Pause 메뉴 위젯
};

기억해야 할 것

  • UCLASS() 매크로: 이 클래스가 언리얼 엔진의 UObject 시스템에 등록된다는 것을 알림.
  • GENERATED_BODY() 매크로: UCLASS()와 함께 사용되며, 언리얼 엔진이 필요한 코드를 자동으로 생성해줌.
  • UPROPERTY() 매크로: 언리얼 엔진 에디터에서 변수를 편집할 수 있도록 해줌.
  • meta = (BindWidget): UMG 위젯을 C++ 코드와 연결해줌.
  • UFUNCTION() 매크로: 언리얼 엔진의 UObject 시스템에 함수를 등록해줌.
  • AddDynamic() 함수: 위젯의 이벤트를 C++ 함수에 연결해줌.

  • 헤더 파일에는 클래스 선언과 필요한 멤버 변수, 함수 선언만 넣는 것이 좋음.
  • 구현은 cpp 파일에서 하는 것이 일반적.

주의사항

  • #pragma once는 헤더 파일이 컴파일러에 의해 단 한 번만 포함되도록 하는 지시어임.
  • 언리얼 엔진의 클래스 이름은 U로 시작하는 것이 일반적임.

CP_MainMenu.cpp

#include "CP_MainMenu.h"
#include "Kismet/GameplayStatics.h"
#include "Blueprint/WidgetTree.h" // 위젯 트리 사용을 위한 include
#include "EnhancedInputSubsystems.h" // UInputSubsystem 사용을 위한 include
#include "CP_PlayerHUD.h"
#include "Kismet/KismetSystemLibrary.h"

UCP_MainMenu::UCP_MainMenu(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
}

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

    if (StartButton)
    {
        StartButton->OnClicked.AddDynamic(this, &UCP_MainMenu::OnStartButtonClicked);
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("StartButton 위젯이 바인딩되지 않았습니다."));
    }

    if (ExitButton)
    {
        ExitButton->OnClicked.AddDynamic(this, &UCP_MainMenu::OnExitButtonClicked);
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("ExitButton 위젯이 바인딩되지 않았습니다."));
    }
}

void UCP_MainMenu::OnStartButtonClicked()
{
    UGameplayStatics::OpenLevel(GetWorld(), TEXT("MenuMap")); // "YourLevelName"을 실제 레벨 이름으로 변경

    // 게임 시작 후 ESC 키 입력 활성화
    if (APlayerController* PlayerController = GetWorld()->GetFirstPlayerController())
    {
        if (UEnhancedInputLocalPlayerSubsystem* InputSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
        {
            // SetInputActionMappingByName 함수가 없으므로, 적절한 함수로 대체해야 합니다.
            // InputSubsystem->SetInputActionMappingByName("Pause", EInputEvent::IE_Pressed); // "Pause" 액션 매핑
        }
    }

    // 게임 시작 시 HUD 위젯 표시 (CP_PlayerHUD)
    if (APlayerController* PlayerController = GetWorld()->GetFirstPlayerController())
    {
        TSubclassOf<UCP_PlayerHUD> HUDClass; // CP_PlayerHUD 클래스
        if (HUDClass)
        {
            UCP_PlayerHUD* PlayerHUD = CreateWidget<UCP_PlayerHUD>(PlayerController, HUDClass);
            if (PlayerHUD)
            {
                PlayerHUD->AddToViewport();
            }
        }
    }

    UE_LOG(LogTemp, Warning, TEXT("Start Button Clicked"));
}

void UCP_MainMenu::OnExitButtonClicked()
{
    UKismetSystemLibrary::QuitGame(GetWorld(), GetWorld()->GetFirstPlayerController(), EQuitPreference::Quit, true);
    UE_LOG(LogTemp, Warning, TEXT("Exit Button Clicked"));
}

void UCP_MainMenu::ShowPauseMenu()
{
    if (PauseMenuWidget)
    {
        PauseMenuWidget->AddToViewport();
        if (ResumeButton)
        {
            ResumeButton->OnClicked.AddDynamic(this, &UCP_MainMenu::OnResumeButtonClicked);
        }
        if (ExitFromPauseButton)
        {
            ExitFromPauseButton->OnClicked.AddDynamic(this, &UCP_MainMenu::OnExitFromPauseButtonClicked);
        }
    }
    else
    {
        // PauseMenuWidget이 없으면 생성
        PauseMenuWidget = CreateWidget<UUserWidget>(GetWorld()->GetFirstPlayerController(), LoadObject<UClass>(nullptr, TEXT("/Game/UI/WBP_PauseMenu.WBP_PauseMenu"))); // 실제 경로와 이름으로 변경
        if (PauseMenuWidget)
        {
            ShowPauseMenu(); // 다시 호출하여 Pause 메뉴 표시
        }
    }
}

void UCP_MainMenu::OnResumeButtonClicked()
{
    if (PauseMenuWidget)
    {
        PauseMenuWidget->RemoveFromParent(); // RemoveFromViewport 대신 RemoveFromParent 사용
    }
    // 게임 재개 로직 (Unpause 등)
    UE_LOG(LogTemp, Warning, TEXT("Resume Button Clicked"));
}

void UCP_MainMenu::OnExitFromPauseButtonClicked()
{
    UKismetSystemLibrary::QuitGame(GetWorld(), GetWorld()->GetFirstPlayerController(), EQuitPreference::Quit, true);
    UE_LOG(LogTemp, Warning, TEXT("Exit from Pause Button Clicked"));
}

기억해야 할 것

  • #include 문: 필요한 헤더 파일을 포함시켜서 해당 기능들을 사용할 수 있도록 함.
  • Super::NativeConstruct(): 부모 클래스의 NativeConstruct() 함수를 호출해야 함.
  • UE_LOG(): 언리얼 엔진의 로그 시스템을 사용하여 메시지를 출력함.
  • UGameplayStatics::OpenLevel(): 레벨을 여는 함수.
  • UKismetSystemLibrary::QuitGame(): 게임을 종료하는 함수.
  • CreateWidget(): 위젯을 생성하는 함수.
  • AddToViewport(): 위젯을 화면에 표시하는 함수.
  • RemoveFromParent(): 위젯을 부모 위젯에서 제거하는 함수.

  • cpp 파일에는 헤더 파일에서 선언된 함수들의 구현과 추가적인 함수들을 넣음.
  • NativeConstruct() 함수는 위젯이 생성될 때 호출되는 함수임.
  • OnButtonClicked() 함수는 버튼이 클릭되었을 때 호출되는 함수임.

주의사항

  • LoadObject<UClass>() 함수를 사용할 때, 정확한 위젯 블루프린트 경로를 지정해야 함.
  • RemoveFromViewport() 대신 RemoveFromParent()를 사용하는 이유는, Pause 메뉴가 다른 위젯의 자식으로 추가될 수 있기 때문임.

추가 팁

  • SetGamePaused() 함수를 사용하여 게임을 일시 정지/재개할 수 있음.
  • GetWorld()->GetFirstPlayerController()를 사용하여 플레이어 컨트롤러를 얻을 수 있음.
  • TSubclassOf<UCP_PlayerHUD> HUDClass 부분을 실제 CP_PlayerHUD 클래스로 변경해야 함.
  • /Game/UI/WBP_PauseMenu.WBP_PauseMenu 부분을 실제 Pause 메뉴 위젯의 경로와 이름으로 변경해야 함.
  • "MenuMap" 부분을 실제 레벨 이름으로 변경해야 함.

전체적인 주의사항

  • 언리얼 엔진 프로젝트에서는 C++ 코드와 블루프린트를 함께 사용할 수 있음.
  • C++ 코드를 변경하면 반드시 컴파일을 해야 함.
  • 언리얼 엔진 에디터에서 위젯을 생성하고, C++ 코드와 바인딩해야 함.
  • Pause 메뉴 기능을 사용하려면, "Pause"라는 이름의 액션 매핑을 생성하고, ESC 키를 할당해야 함.

0개의 댓글