Home [UE5 TPS 제작기] 16. C++로 UMG 만드는 방법
Post
Cancel

[UE5 TPS 제작기] 16. C++로 UMG 만드는 방법

C++를 이용하여 UMG를 어떻게 만들 수 있는지를 다루고 있습니다.
설명은 현재 만들고 있는 TPS 게임의 총알 개수를 표기하는 TEXT UI를 구현하는 과정입니다.

글에서 UI란 Button, Text와 같은 위젯을 의미합니다.

1. Build.cs 수정

UE4 공식 문서 - User Interface With UMG

C++에서 UMG를 사용하기 위해서는 **.Build.cs 파일을 수정해야 한다.

위의 공식 문서에 잘 나와있듯이

  • 공개 모듈 목록: UMG 추가
  • 비공개 모듈 목록: Slate 및 SlateCore
    추가가 필요하다.
1
2
3
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UMG" });

PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });

2. C++ 클래스 만들기

UserWidget을 상속받는 C++ 클래스를 생성해준다.
저의 경우 HUD를 만들 것이기 때문에 MyHUD 라는 이름으로 생성해주었다.

클래스를 생성하면 아래와 같은 코드를 볼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "MyHUD.generated.h"

/**
 * 
 */
class UTextBlock;
UCLASS()
class TPS_PROTOTYPE_API UMyHUD : public UUserWidget
{
	GENERATED_BODY()
};

3. C++ 멤버 변수에 Blueprint 위젯 UI 바인딩하기

보통 Widget은 Blueprint를 이용해서 시각적으로 각종 UI들을 배치한다.

제가 Unity 개발자이다 보니 이렇게 생각하는 거일수도…

그리고 배치한 UI에 특정 값을 넣거나 상호 작용에 대한 피드백 처리를 해줘야 할 때가 있는데 처리를 위해선 Blueprint를 사용하거나 C++를 사용해야 한다.

이번 글에서는 C++를 이용한 방법을 살펴볼 것이다.

블루프린트로 만드는 방법은 이전에 포스팅한 [UE4] UMG를 이용한 체력바(HP Bar) 만들기 에서 확인할 수 있습니다.

BindWidget 메타 속성을 이용한 변수 생성

이번 글에서는 설명과 함께 총알 개수를 표현하는 Widget을 만들 예정이기에
UMG Widget에서 텍스트 박스에 해당하는 C++ 클래스인 TextBlock 클래스 변수를 만들어 줄 것이다.

Widget Blueprint에서 텍스트 박스를 생성해보면 TextBlock 클래스로 만들었다는 것을 알 수 있다.
1

1
2
3
4
// AmmoCount 라는 텍스트 박스 변수 생성
UPROPERTY(BlueprintReadOnly,Category ="TEXT", meta = (BindWidget, AllowPrivateAccess = "true"))
UTextBlock* AmmoCount;
};

생성한 변수의 UPROPERTY()를 보면 meta = (BindWidget) 를 볼 수 있는데
BindWidgetC++에서 블루프린트로 생성한 UI(Ex.텍스트 박스)를 제어할 수 있게 해주는 메타 속성이다.

BindWidget을 제대로 사용하기 위해서는 메타 속성 등록 뿐만 아니라 아래와 같은 처리가 추가로 필요하다.

  1. 현재 작업 중인 C++ 클래스를 부모로 하는 위젯 블루프린트 생성
  2. BindWidget 메타 속성을 가진 변수와 동일한 유형동일한 이름을 가진 UI 생성 2

BindWidget 메타 속성 적용과 위 두 가지 처리를 해줘야 C++를 통해서 UI 컨트롤이 가능해진다.

위젯 블루프린트 생성 시 주의점

한 가지 주의해야 할 점은 위젯 블루프린트를 생성할 때 C++ 기반 블루프린트 클래스 생성으로 만들면 안된다.

UserWidget을 상속받는 클래스로 블루프린트 클래스를 생성해보면 아래와 같이 UI를 배치할 수 있는 창이 생성되지 않는다.
3

실제로 파일 확장자명(?)을 보면 위젯 블루프린트가 아님을 알 수 있다.
4

그러므로 위젯 블루프린트를 생성하기 위해서는

  1. 위젯 블루프린트 생성
  2. 부모는 직접 구현한 C++ 클래스로 선택
    으로 생성해야 한다.

5
6

4. C++을 이용하여 텍스트 처리하기

공부하면서 구현해본 것을 정리한 내용입니다.

위에서 선언한 AmmoCount 변수를 기반으로 탄약 개수의 변화를 UI에 표시할 것이다.
이를 위해 클래스에 Init 메소드와 SetAmmoCountText 메소드를 추가하자.

MyHUD.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "MyHUD.generated.h"

/**
 * 
 */
class UTextBlock;
UCLASS()
class TPS_PROTOTYPE_API UMyHUD : public UUserWidget
{
	GENERATED_BODY()

	UPROPERTY(BlueprintReadOnly,Category ="TEXT", meta = (BindWidget, AllowPrivateAccess = "true"))
	UTextBlock* AmmoCount;

protected:
	/** C++ 생성자와 동일한 기능을 하는 메소드 */
	virtual void NativeConstruct() override;
public:
	void Init(int remainAmmoCount, int maxAmmoCount) const;
	void SetAmmoCountText(int remain, int max) const;
};

MyHUD.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Fill out your copyright notice in the Description page of Project Settings.


#include "MyHUD.h"
#include "Components/TextBlock.h"

void UMyHUD::NativeConstruct()
{
	Super::NativeConstruct();
}

void UMyHUD::Init(int remainAmmoCount, int maxAmmoCount) const
{
	SetAmmoCountText(remainAmmoCount, maxAmmoCount);
}

void UMyHUD::SetAmmoCountText(int remain, int max) const
{
	FString string = FString::Printf(TEXT("%d/%d"), remain, max);
	AmmoCount->SetText(FText::FromString(string));
}

Widget 생성하기

MyHUD 위젯은 캐릭터 클래스를 통해서 생성할 것이기 때문에 캐릭터 클래스 헤더 파일에 아래 두개의 변수를 선언해 주었다.

1
2
3
4
UPROPERTY(EditAnywhere, Category = "Widget", meta = (AllowPrivateAccess = "true"))
TSubclassOf<UMyHUD> MyHUDWidgetClass;
UPROPERTY(VisibleAnywhere, Category = "Widget", meta = (AllowPrivateAccess = "true"))
UMyHUD* MyHUD;

MyHUDWidgetClass는 어떤 위젯을 사용할 것인지에 대한 변수이고
MyHUDMyHUDWidgetClass를 통해 생성한 위젯을 뜻한다.

그리고 Widget 생성을 담당할 CreateHUD()메소드를 추가로 선언해주었다.

1
2
private:
	void CreateHUD();

그리고 아래와 같이 Widget을 생성하고 Init해주었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void APlayerCharacter::CreateHUD()
{
	if (MyHUDWidgetClass)
	{
		MyHUD = Cast<UMyHUD>(CreateWidget(GetWorld(), MyHUDWidgetClass));

		if (MyHUD)
		{
			MyHUD->AddToViewport();
			MyHUD->SetVisibility(ESlateVisibility::HitTestInvisible);

			int AmmoRemainCount = EquipWeapon ? EquipWeapon->GetAmmoRemainCount() : 0;
			int AmmoMaxCount = EquipWeapon ? EquipWeapon->GetAmmoMaxCount() : 0;

			MyHUD->Init(AmmoRemainCount, AmmoMaxCount);
		}
	}
}

탄약 변화에 따른 처리를 위해 Delegate 생성하기

지금 이 글에서는 탄약 변화량을 UI에 표기할 것이기 때문에 탄약을 관리하는 Weapon 클래스에 델리게이트를 선언하여 처리하였다.

1
2
3
4
5
6
7
8
DECLARE_MULTICAST_DELEGATE(FDele_Multi);

UCLASS(Abstract)
class TPS_PROTOTYPE_API AWeapon : public AActor
{
public:
	FDele_Multi AmmoChangedDelegate;
}

추가로 탄약 변화량을 관리하는 메소드인 DecreaseAmmoCount() ResetAmmoCount()를 추가하였다.
그리고 변화에 맞춰서 델리게이트가 호출될 수 있도록 처리하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void AWeapon::DecreaseAmmoCount()
{
	--AmmoRemainCount;

	if (AmmoChangedDelegate.IsBound())
		AmmoChangedDelegate.Broadcast();
}

void AWeapon::ResetAmmoCount()
{
	AmmoRemainCount = AmmoMaxCount;

	if (AmmoChangedDelegate.IsBound())
		AmmoChangedDelegate.Broadcast();
}

Delegate에 바인딩하기

위젯 생성과 무기 관리를 캐릭터 클래스에서 하기 때문에 Delegate 바인딩 또한 캐릭터 클래스에서 처리할 것이고 바인딩 처리를 위해 BindingAmmoChangedDelegate() 메소드를 추가 및 BeginPlay()에서 호출해 주었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void APlayerCharacter::BindingAmmoChangedDelegate() const
{
	if (EquipWeapon)
	{
		EquipWeapon->AmmoChangedDelegate.AddLambda([&]()
			{
				if (MyHUD)
					MyHUD->SetAmmoCountText(EquipWeapon->GetAmmoRemainCount(), EquipWeapon->GetAmmoMaxCount());
			});
	}
}

// Called when the game starts or when spawned
void APlayerCharacter::BeginPlay()
{
	Super::BeginPlay();

	AttachWeapon(Weapon);
	CreateHUD();
	BindingAmmoChangedDelegate();
}

5. 결과

총알 변화에 따라 우측 하단에 남은 탄약 개수 텍스트가 변화하는 것을 볼 수 있다.


참고 문헌

benui - Connect C++ to UMG Blueprints with BindWidget

This post is licensed under CC BY 4.0 by the author.

[UE5 TPS 제작기] 15. 장전(Reload) 시스템 만들기

[C# 프로그래머스] 미로 탈출