[UNSEEN 리팩토링] PC -> HUD로 UI 생성 위치 이동

suyoung·2024년 7월 16일

UE5

목록 보기
2/12

UNSEEN 기간 동안 교육 강의를 보면서 개발을 진행하다 보니, 강의대로 UI 생성을 HUD 대신 PlayerController를 사용했었다.
UNSEEN 멘토링 어느날, 멘토님께서 왜 UI 생성을 PlayerController에서 해? 라고 물어보았고, 그 말에 나는 대답하지못했다. (다른 방법이 있는지 몰랐...)
개발이 완료된 지금 시점에서, PlayerController에 있는 모든 UI 로직을 정리하고, HUD로 옮겨본다.

AHUD Class란 무엇인가?

AHUD 클래스는 언리얼 엔진의 Head-Up Display 시스템을 담당하는 기본 클래스입니다. HUD는 게임에서 플레이어에게 정보를 시각적으로 제공하는데 사용되며, 일반적으로 화면 상단에 게임 상태, 점수, HP등을 표현한다.

AHUD 클래스의 주요 역할

  1. 게임 상태 정보 표시
  2. 사용자 입력 처리

AHUD 클래스 사용 방법

AHUD 클래스는 Owner - PlayerController 이다.

AHUD 내에서 PlayerState, Pawn 접근을 위해서는 아래 GetPlayerState,GetPawn 함수를 사용한다.

//PlayerState
AQLPlayerState* PS = PlayerOwner->GetPlayerState<AQLPlayerState>();
//Player Pawn
AQLCharacterPlayer *LocalPlayer = PlayerOwner->GetPawn<AQLCharacterPlayer>();

그리고, Pawn이나 PlayerState, PlayerController에서는 PlayerController - GetHUD 함수에서부터 클래스를 가져올 수 있다.

LocalHUD = Cast<AQLHUD>(PlayerController->GetHUD());
if (LocalHUD)
{
	LocalHUD->CreateHUD();
}

이번 벨로그는 제 프로젝트에 AHUD 클래스를 적용하는 걸 목표로 합니다.

  1. GameMode에 먼저 설정합니다.
  2. HUD 클래스에 미리 정의한 'CreateHUD'(사용자 정의 함수)를 Pawn에서 생성해준다.
    시점 : OnRep_PlayerController(클라이언트) , PossessedBy(서버) [ 리슨서버 기준 ]
void AQLCharacterPlayer::OnRep_Controller()
{

	Super::OnRep_Controller();

	AQLPlayerController* PlayerController = Cast<AQLPlayerController>(GetController());

	LocalHUD = Cast<AQLHUD>(PlayerController->GetHUD());
	if (LocalHUD && LocalHUD->HUDNum() == 0) 
    //Respawn에 의해 여러번 호출되는 경향이 있음. 한번만 호출하도록 한다.
	{
		LocalHUD->CreateHUD();
	}
}
  1. CreateHUD 함수에서는 모든 UserWidget를 생성해서, 화면에 띄어준다.

    위 클래스 모두 생성해서 HUDs 로 관리 (TMap 자료구조)

    AHUD로 잘 연동된 모습을 볼 수 있다.

  2. 입력에 따른 HUD 띄우기 => PlayerController로 몇 가지 이동

  • 상호작용 UI
    - WB_Story
    - WB_Menu
    - WB_Inventory
    - Win/Loose
    AHUD 와 PlayerController 내에서 상호작용 UI를 모두 띄우기 때문에 Z-Order값을 조정해주었음.
    Z-Order이란? Z-order는 주로 UI 요소와 2D 요소의 겹침 순서를 정의하는 데 사용됩니다. 초기값은 0으로, 값이 커지면 커질 수록 이전 숫자를 가진 이미지보다 위에 위치한다는 특징을 가지고 있습니다.
    또한, HUD 클래스에 위치한 위젯은 모두 Visibility를 조절하였다.
    Visibility를 조절하면서, 실제 상호작용 안 할 위젯은 조정할 수 있다.

  • Visible : 보이고 클릭 가능

  • Collapsed : 보이지 않고, layout에 공간을 차지 하지 않으며, 클릭 불가능

  • Hidden : 보이지 않고, layout에 공간을 차지하나 클릭은 불가능

  • Not Hit-Testable ( Self & All Children ) : 보이지만 클릭 불가능, 자식 + 나 모두 포함

  • Not Hit-Testable ( Self Only ) : 보이지만 나 자신만 클릭 불가능

  1. 사용자 Input 상호작용 연결

  • 현재 문제점 :
    - Attribute set 값이 연동되지 않는다.
    - 예상 : PlayerState를 중심으로, Attribute Set 과 AHUD 사이의 옵저버 패턴을 유지한다.

    상태 값 - PlayerState - UI
//일부 발췌 중간자 역할을 담당
void AQLPlayerState::OnChangedStamina(const FOnAttributeChangeData& Data)
{
//OnChangedStamina 함수도, 델리게이트로 연결되어 있는 함수이다.

    float CurrentStamina = Data.NewValue;

	//클라인지 서버인지 구별할 필요없음, 어차피 Bound되어 있다면, 아래 구문을 수행하기 때문이다.
    if (UpdateStaminaDelegate.IsBound())
    {
        UpdateStaminaDelegate.Execute(CurrentStamina, GetMaxStamina());
    }
}

//값이 변경되어 전달옴, 그리고 UI를 변경
void AQLHUD::ChangedStaminaPercentage(float Stamina, float MaxStamina)
{
	UQLUserWidget* Widget = Cast<UQLUserWidget>(HUDs[EHUDType::HUD]);

	if (Widget)
	{
		Widget->ChangedStaminaPercentage(Stamina, MaxStamina);
	}
}

AHUD로 옮기는 작업은 모두 끝났습니다! 다음에는 UI 최적화에 대해 공부하고, 현재 사용 중인 UI를 최적화 해보도록 하겠습니다!

profile
게임 클라이언트 프로그래머

0개의 댓글