[UE5] 어트리뷰트 메뉴

kkado·2024년 9월 5일
0

UE5

목록 보기
60/63
post-thumbnail

여러 게임플레이 어트리뷰트를 확인할 수 있는 상태창을 만들어보자.

Widget -> Widget Controller -> Data 방향의 의존성을 가지고 있는 구조는 이전 글에 자세히 나와있다.

Widget Blueprint

여러 개의 위젯 블루프린트를 쌓아서 만드는 편이 모듈화 측면에서 효율적일 것이다. 먼저 숫자를 표시하는 위젯을 만들고, 그 위젯을 포함해서 어트리뷰트의 이름과 함께 하나의 줄을 이루는 Text Value Row를 만든다.

Attribute Menu WidgetBlueprint


특성 포인트를 할당할 수 있는 버튼이 있는 버전도 함께 만든다.

마찬가지로 이 버튼 또한 별도의 위젯이다.

그리고 이것들을 예쁘게 조합해서 하나의 어트리뷰트 메뉴 창을 만든다.

창 오픈 버튼

버튼을 먼저 만들어주고

이 버튼에다가 AttributeMenu 위젯을 만들고 AddtoViewport 하는 커스텀 이벤트 AttributeMenuClicked 를 만들어 붙인다.

아래쪽 시퀀스 노드는 AttributeMenu의 X 버튼을 눌렀을 때 창이 꺼지면서 다시 버튼을 활성화하는 부분이다.

Data Asset

게임플레이 태그를 키로 하여 찾을 수 있는 데이터 에셋을 만든다.
예를 들어 UI상에 어떻게 표시될지를 DisplayName 이라는 변수로 만들 수도 있고, 마우스를 올렸을 때 어떤 태그인지를 알려주는 도움말로 사용될 Comment 등의 변수 등을 하나의 구조체로 묶을 수 있다.

먼저 하나의 구조체를 만들어 주고, 멤버들을 넣는다.

USTRUCT(BlueprintType)
struct FAuraAttributeInfo
{
	GENERATED_BODY()

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
	FGameplayTag AttributeTag = FGameplayTag();

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
	FText DisplayText = FText();

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
	FText AttributeDesc = FText();
	
	UPROPERTY(BlueprintReadOnly)
	float AttributeValue = 0.f;
};

태그로 Info를 찾을 수 있는 함수와 함께 데이터 자료구조를 가지고 있는 클래스를 만든다.

UCLASS()
class AURA_API UAttributeInfo : public UDataAsset
{
	GENERATED_BODY()

public:
	FAuraAttributeInfo FindAttributeInfoByTag(const FGameplayTag& AttributeTag, bool bLogNotFound = false) const;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
	TArray<FAuraAttributeInfo> AttributeInfo;
};

이제 miscellaneous -> Data Asset을 통해 데이터 에셋을 만들어주고 태그 정보들을 기입한다.

WidgetController에서 Widget 방향으로 브로드캐스트 할 때, 이 데이터 에셋에서 정보들을 찾아 전달해주는 용도로 사용할 것이다.

Native GameplayTag

이전에는 GameplayTag를 config -> DefaultGameplayTags.ini 파일을 이용해서 만들었지만, C++상에서 직접적으로 태그를 정의하고 사용할 수 있는 Native한 방식을 사용해보자.

게임 상에서 static하게 존재하는 싱글톤으로 구현할 것이며, 따라서 전역에서 자유롭게 태그에 접근할 수 있도록 한다.

FAuraGameplayTags 라는 이름의 구조체를 만들었으며, static 멤버를 private 섹션에, 이 객체에 접근할 수 있는 Getter 함수를 public으로 만든다.

struct FAuraGameplayTags
{
public:
 static const FAuraGameplayTags& Get() {return GameplayTags;}
 static void InitializeNativeGameplayTags();

 FGameplayTag Attributes_Primary_Strength;
 FGameplayTag Attributes_Primary_Intelligence;
 FGameplayTag Attributes_Primary_Resilience;
 FGameplayTag Attributes_Primary_Vigor;
 
private:
 // Singleton instance
 static FAuraGameplayTags GameplayTags;
};
#include "AuraGameplayTags.h"
#include "GameplayTagsManager.h"

// 정적(static) 멤버 변수를 선언하고, 동시에 그것을 인스턴스화
FAuraGameplayTags FAuraGameplayTags::GameplayTags;

void FAuraGameplayTags::InitializeNativeGameplayTags()
{
	/*
	 * Primary Attributes
	 */
	GameplayTags.Attributes_Primary_Strength = UGameplayTagsManager::Get().AddNativeGameplayTag(
		FName("Attributes.Primary.Strength"),
		FString("Increases physical damage")
		);

	GameplayTags.Attributes_Primary_Intelligence = UGameplayTagsManager::Get().AddNativeGameplayTag(
		FName("Attributes.Primary.Intelligence"),
		FString("Increases magical damage")
		);

	GameplayTags.Attributes_Primary_Resilience = UGameplayTagsManager::Get().AddNativeGameplayTag(
		FName("Attributes.Primary.Resilience"),
		FString("Increases Armor and Armor Penetration")
		);

	GameplayTags.Attributes_Primary_Vigor = UGameplayTagsManager::Get().AddNativeGameplayTag(
		FName("Attributes.Primary.Vigor"),
		FString("Increases Health")
		);
}

Widget Controller

OverlayWidgetController와 마찬가지로, 거의 유사하게 동작하는 AttributeMenuWidgetController 클래스를 만든다. 방금 만든 AttributeInfo ObjectPtr 멤버와 델리게이트 선언, 이를 브로드캐스트하는 함수를 만든다.

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAttributeInfoSignature, const FAuraAttributeInfo&, Info);

UCLASS(BlueprintType, Blueprintable)
class AURA_API UAttributeMenuWidgetController : public UAuraWidgetController
{
	GENERATED_BODY()
	
public:
	virtual void BroadcastInitialValues() override;

	UPROPERTY(BlueprintAssignable, Category = "GAS|Attributes")
	FAttributeInfoSignature AttributeInfoDelegate;

protected:
	UPROPERTY(EditDefaultsOnly)
	TObjectPtr<UAttributeInfo> AttributeInfo;
};

...

void UAttributeMenuWidgetController::BroadcastInitialValues()
{
	const UAuraAttributeSet* AS = Cast<UAuraAttributeSet>(AttributeSet);
	check(AttributeInfo);
	FAuraAttributeInfo Info = AttributeInfo->FindAttributeInfoByTag(FAuraGameplayTags::Get().Attributes_Primary_Strength);
	Info.AttributeValue = AS->GetStrength();
	AttributeInfoDelegate.Broadcast(Info);
}

우선 Strength 태그에 대한 초기값 설정만을 하기로 하고 BroadcastInitValue 함수를 만든다. AttributeInfo에서 태그를 찾아서 데이터 에셋을 찾은 후에, 이 값을 그대로 AttributeInfoDelegate로 브로드캐스트한다.

이벤트가 발생할 때 Label Text를 갱신하는 식으로 사용

GetAttributeMenuWidgetController 는 WidgetController를 쉽게 연결하기 위해 만든 blueprint pure 함수이다.

UAttributeMenuWidgetController* UAuraAbilitySystemLibrary::GetAttributeMenuWidgetController(const UObject* WorldContextObject)
{
	if (APlayerController* PC = UGameplayStatics::GetPlayerController(WorldContextObject, 0))
	{
		if (AAuraHUD* AuraHUD = Cast<AAuraHUD>(PC->GetHUD()))
		{
			AAuraPlayerState* PS = PC->GetPlayerState<AAuraPlayerState>();
			UAbilitySystemComponent* ASC = PS->GetAbilitySystemComponent();
			UAttributeSet* AS = PS->GetAttributeSet();
			const FWidgetControllerParams WidgetControllerParams(PC, PS, ASC, AS);

			return AuraHUD->GetAttributeMenuWidgetController(WidgetControllerParams);
		}
	}
	return nullptr;
}

이제 어트리뷰트 창이 열리고 난 뒤에 위젯 컨트롤러를 연결한 후, BroadcastInitialValues() 함수를 통해 Strength 태그에 대한 정보가 브로드캐스트되었고, 이 브로드캐스트를 통해 UI에 반영된다.

각 row마다 어떤 태그의 변화를 받을지를 아직 명시하지 않았기 때문에 모든 row가 strength 태그에 따라 바뀌고 있는 것을 볼 수 있다.


어트리뷰트 태그 부착

이제 각각의 row 위젯이 특정한 태그에만 반응하여 그 값을 변경하도록 만들면 된다.

row 위젯에 GameplayTag 변수를 추가하고, 블루프린트에 노출한다.

그리고 attribute menu 위젯에서 이 태그들을 모두 다르게 설정해준다.

void UAttributeMenuWidgetController::BroadcastAttributeInfo(const FGameplayTag& AttributeTag,
	const FGameplayAttribute& Attribute) const
{
	FAuraAttributeInfo Info = AttributeInfo->FindAttributeInfoByTag(AttributeTag);
	Info.AttributeValue = Attribute.GetNumericValue(AttributeSet);
	AttributeInfoDelegate.Broadcast(Info);
}

마지막으로 row 위젯의 AttributeInfoDelegate에서 Matches Tag 옵션을 추가함으로써, 각 row가 가지고 있는 태그와 일치하는 태그가 파라미터일 때만 값을 갱신하도록 수정한다.

브로드캐스트하는 파라미터의 경우 AttributeInfo GameAsset에서 Tag를 기반으로 찾은 결과를 담아 전달한다.

그러면 각 row가 서로 다른 태그에만 반응하여 보여주고 있는 것을 볼 수 있다.

profile
베이비 게임 개발자

0개의 댓글