언리얼 엔진 5/개발 일지

[UE5] 블루아카이브 TPS게임 개발일지 (38) - 탄약 UI 구현 1

ciel45 2024. 1. 14. 00:01

슬슬 탄약 시스템과 재장전 시스템을 만들 때가 된 것 같다.

 

장전 로직의 핵심을 정리해보자면 다음과 같다.

 

  • 총을 발사할 때마다 탄약 개수가 1씩 줄어든다.
  • 현재 탄약이 0이면 발사하지 못한다.
  • 장전을 통해 현재 탄약을 최대 탄약으로 채우고, 전체 탄약에서 채운 양을 제외한다.

 

실은 이러한 기본적인 것들은 이미 Gun 클래스에 구현해두었다.

void AGun::Fire(FVector& StartPoint, FVector& Direction)
{
	CurrentAmmo = FMath::Clamp(CurrentAmmo-1, 0, MaxAmmo); // 현재탄약 -1
}
/*
* MaxAmmo: 탄창에 들어가는 최대 탄약 개수
* CurrentAmmo: 현재 탄창에 들어있는 탄약 개수
* ReloadingAmmo: 전체 탄약 개수에서 빠져서 탄창으로 들어갈 탄약의 개수
*/
void AGun::Reload()
{
	if (MaxAmmo == CurrentAmmo)
		return;

	UE_LOG(LogTemp,Warning, TEXT("RELOAD"));

	int32 ReloadingAmmo = MaxAmmo - CurrentAmmo;

	TotalAmmo -= ReloadingAmmo;

	CurrentAmmo = MaxAmmo;
}

 

 

변수들은 모두 갖춰졌으므로, 먼저 이걸 화면 우측 하단에 뿌려주는 것부터 시작하였다.

 

탄약 개수 정보는 게임 전반에 걸쳐 보여줘야 하므로, 게임 전반에 쓰이는 UI 클래스에 포함시키는 것이 좋을 것 같다.

따라서 게임 전반에 쓰이는 KannaTPSOverlay 클래스를 만들고, 그 일부로 탄약 정보를 넣어줄 것이다.

 

먼저 UserWidget을 부모로하는 블루프린트 WBP_KannaTPSOverlay를 만들었다.

UserWidget을 부모 클래스로

탄약 정보를 나타내는 UI를 대강 만들었다.

 

* 탄약 정보는 화면 우측 하단에 나타날 것이므로 추후 화면 비율의 가변성을 고려한다면 앵커를 우측 하단으로 설정해주어야 한다.

 

TotalAmmoText와 CurrentAmmoText가 있는데, 이 두 텍스트의 값은 C++ 코드로 넣어줄 것이다.

그러려면 이 블루프린트가 C++ 클래스를 상속받도록 해야한다.

 

UserWidget을 상속받는 클래스 KannaTPSOverlay를 만들고, 헤더 파일을 다음과 같이 만들어주었다.

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

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

class UTextBlock;

UCLASS()
class KANNATPS_API UKannaTPSOverlay : public UUserWidget
{
	GENERATED_BODY()

public:
	void SetCurrentAmmoText(int32 amount);

	void SetTotalAmmoText(int32 amount);

	void HideAmmoText();

	void ShowAmmoText();
	
private:
	// meta = BindWidget은 블루프린트에서의 변수와 연동시키겠다는 의미이다.
	UPROPERTY(meta = (BindWidget))
	UTextBlock* CurrentAmmoText;

	UPROPERTY(meta = (BindWidget))
	UTextBlock* TotalAmmoText;

	UPROPERTY(meta = (BindWidget))
	UTextBlock* AmmoTextSlash;
};

위젯 블루프린트에서의 Text는 C++에서는 UTextBlock이다. 따라서 변수 타입도 UTextBlock 포인터로 해줘야한다.

 

이미 만들어져 있는 위젯 블루프린트와 연동시키는게 목적이므로, 변수들에 UPROPERTY(meta = (BindWidget))을 사용할 것이다.

이렇게 함으로써 블루프린트의 부모클래스를 이 C++클래스로 바꾸면 자동으로 프로퍼티들이 바인딩이 된다.

 

주의할 점은, 변수의 이름을 블루프린트와 C++의 것을 똑같이 일치시켜야한다.

그래야 바인딩이 제대로 된다.

 

게임에 사용할 오버레이가 만들어졌는데, 이것을 크로스헤어 때처럼 KannaCharacter의 BeginPlay에서 Add to Viewport를통해 표시해줄 수도 있지만, 게임 전반에 사용되는 것이므로 더 적절한 방법을 사용할 것이다.

 

그것은 HUD에 포함시키는 것이다.

 

프로젝트의 GameMode 블루프린트를 보면, HUD 기본 클래스를 설정할 수 있는 칸이 있다.

원래 기본값으로는 그냥 HUD클래스가 들어가있는데, 이미지는 새로 만든 것을 넣어준 버전이다.

 

새로운 HUD클래스를 사용하기 위해, 우선 C++에서 KannaTPSHUD 클래스를 새로 만들었다.

이 클래스의 멤버로써 방금 만든 KannaTPSOverlay를 사용할 수 있어야한다.

 

우선 헤더파일이다.

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "KannaTPSHUD.generated.h"

class UKannaTPSOverlay;

UCLASS()
class KANNATPS_API AKannaTPSHUD : public AHUD
{
	GENERATED_BODY()

public:
	FORCEINLINE UKannaTPSOverlay* GetKannaTPSOverlay() const {return KannaTPSOverlay;}

protected:
	virtual void BeginPlay() override;
	
private:
	UPROPERTY(EditDefaultsOnly)
	TSubclassOf<UKannaTPSOverlay> KannaTPSOverlayClass;

	UPROPERTY()
	UKannaTPSOverlay* KannaTPSOverlay;
};

프라이빗 섹션의 TSubclassOf<UKannaTPSOverlay> KannaTPSOverlayClass; 에 주목해보자면,

우선 TSubClassOf는 말그대로 특정 타입의 서브클래스라는 의미이다.

그러므로 여기서 KannaTPSOverlayClass는 UKannaTPSOverlay의 서브클래스라면 무엇이든지 담을 수 있다.

 

그말인 즉슨, UKannaTPSOverlay를 상속받은 블루프린트도 여기에 넣어줄 수 있다.

사실 그것을 위해 해당 변수를 만든 것이고, 블루프린트 에디터 창에서 집어넣을 수 있도록 UPROPERTY(EditDefaultsOnly) 지정자를 사용한 것이다.

 

이제 이 C++파일을 상속받은 블루프린트 BP_KannaTPSHUD를 만들고나면, 다음과 같이 KannaTPSOverlayClass에 클래스를 집어넣을 수 있다. 여기에 아까 만든 BP_KannaTPSOverlay를 집어넣으면 된다.

 

이렇게 하여 KannaTPSHUD 클래스가 WBP_KannaTPSOverlay 클래스를 알 수 있게 되었다.

이제 KannaTPSHUD가 WBP_KannaTPSOverlay를 생성하고, 뷰포트에 추가할 수 있다.

이는 BeginPlay에서 수행할 것이다.

 

// Fill out your copyright notice in the Description page of Project Settings.


#include "HUD/KannaTPSHUD.h"
#include "HUD/KannaTPSOverlay.h"

void AKannaTPSHUD::BeginPlay()
{
	Super::BeginPlay();

	UWorld* World = GetWorld();

	if (World)
	{
		APlayerController* Controller = World->GetFirstPlayerController();
		if (Controller && KannaTPSOverlayClass)
		{
			KannaTPSOverlay = CreateWidget<UKannaTPSOverlay>(Controller, KannaTPSOverlayClass);
			KannaTPSOverlay->AddToViewport();
		}
	}

}

 

CreateWidget 함수를 통해 위젯을 만들고, AddToViewport 함수를 통해 화면에 뿌려주면 된다.

CreateWidget 함수는 첫번째 인자로 APlayerController 포인터를 받는다. 이를 위해서는 World->GetFirstPlayerController()로 컨트롤러를 가져와서 써주면 된다.

싱글플레이 게임이므로 FirstPlayerController는 당연히 현재 플레이어의 컨트롤러일 것이기 때문이다.

 

게임모드의 HUD에 BP_KannaTPSHUD가 박혀있는 것을 한번 더 확인해주고, 게임을 실행하면 오버레이가 뜨게된다.

 

현재 진행을 좀 더 해둔 상태라 탄약 숫자가 쓰여져있는데, 원래 여기까지 했을 때는 0 / 0으로 뜨는 것이 맞다.

 

어쨌든 확실한 것은, 이제 KannaTPSHUD가 KannaTPSOverlay를 만들어서 화면에 뿌려준다는 것이다.

 

다음 포스팅에서는 실제로 UI에 탄약의 개수를 담는 과정을 다룰 것이다.