-
[Unreal Engine] GamePlay Ability System #2 Attribute Sets2024년 05월 12일
- 유니얼
-
작성자
-
2024.05.12.:29
728x90Unreal Engine의 Gameplay Ability System(GAS)은 개발자가 게임 내에서 캐릭터의 능력, 상태 변경, 상호작용 등을 효율적으로 관리할 수 있게 하는 강력한 프레임워크입니다. 이 시스템의 핵심 요소 중 하나는 "속성(Attribute)"입니다. 이번 포스트에서는 GAS에서 속성이 어떻게 정의되고 사용되는지에 대해 자세히 살펴보겠습니다.
참고 링크:
속성(Attribute)의 정의
속성은 FGameplayAttributeData 구조체를 사용하여 정의되며, 이는 게임 내에서 캐릭터의 다양한 수치 정보를 나타냅니다. 이러한 수치들은 캐릭터의 남은 생명력, 비히클의 최고 속도, 아이템 사용 가능 횟수 등을 포함할 수 있으며, 각각의 속성은 AttributeSet 내에 저장되어 관리됩니다.
속성은 기본적으로 ‘현재(current) 값’과 ‘기본(base) 값’ 두 가지를 가집니다. 기본 값은 보다 장기적으로 고정되어 있으며, 현재 값은 게임플레이 이펙트에 따라 일시적으로 변경될 수 있습니다. 예를 들어, 점프 높이 속성이 기본적으로 100.0이지만, 특정 게임플레이 이펙트로 인해 70%만큼 저하되었다면 현재 값은 70.0이 됩니다.
어트리뷰트 세트(Attribute Set)의 정의 및 구성
어트리뷰트 세트는 하나 이상의 게임플레이 어트리뷰트(Gameplay Attributes)로 구성됩니다. 게임플레이 어트리뷰트는 FGameplayAttributeData UProperties로 표현되며, 이들은 캐릭터의 생명력, 이동 속도, 마나 등과 같은 게임플레이 관련 수치를 저장하고 관리하는 역할을 합니다.
속성의 생성 및 관리
속성을 생성하고 관리하기 위해서는 먼저 AttributeSet을 정의해야 합니다. AttributeSet은 하나 이상의 게임플레이 속성을 포함하며, 어빌리티 시스템 컴포넌트에 등록되어 게임플레이 속성과 시스템의 다른 부분 간의 상호작용을 관리합니다.
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "AttributeSet.h" #include "AbilitySystemComponent.h" #include "CharacterAttributeSetBase.generated.h" UCLASS() class SAMPLEPROJECT_API UCharacterAttributeSetBase : public UAttributeSet { GENERATED_BODY() public: UPROPERTY(BlueprintReadOnly, Category = "Level") FGameplayAttributeData Level; UPROPERTY(BlueprintReadOnly, Category = "Health") FGameplayAttributeData Health; UPROPERTY(BlueprintReadOnly, Category = "Health") FGameplayAttributeData MaxHealth; UPROPERTY(BlueprintReadOnly, Category= "Mana") FGameplayAttributeData Mana; UPROPERTY(BlueprintReadOnly, Category= "Mana") FGameplayAttributeData MaxMana; UPROPERTY(BlueprintReadOnly, Category="Damage") FGameplayAttributeData Damage; };
게임플레이 어트리뷰트 상호작용 매크로
어트리뷰트와 효율적으로 상호작용하기 위해 Unreal Engine은 다음과 같은 매크로를 제공합니다. 이 매크로들은 어트리뷰트 세트의 헤더 파일에 추가하여 사용할 수 있으며, 각 어트리뷰트에 대한 getter와 setter 함수를 자동으로 생성해 줍니다.
매크로(파라미터) 생성된 함수의 시그니처 행동/사용 GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UMyAttributeSet,AttributeName) static FGameplayAttribute GetHealth() 스태틱 함수이며, 엔진의 리플렉션 시스템으로부터 FGameplayAttribute 구조체를 반환합니다. GAMEPLAYATTRIBUTE_VALUE_GETTER(AttributeName) float GetAttribute() const 게임플레이 어트리뷰트의 현재 값을 반환합니다. GAMEPLAYATTRIBUTE_VALUE_SETTER(AttributeName) void SetAttribute(float NewVal) 게임플레이 어트리뷰트의 값을 NewVal 로 설정합니다. GAMEPLAYATTRIBUTE_VALUE_INITTER(AttributeName) void InitAttribute(float NewVal) 게임플레이 어트리뷰트의 값을 NewVal 로 초기화합니다. // Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "AttributeSet.h" #include "AbilitySystemComponent.h" #include "CharacterAttributeSetBase.generated.h" // ATTRIBUTE_ACCESSORS 매크로: 주어진 속성에 대한 접근자 함수들을 자동으로 생성합니다. // ClassName은 클래스 이름, PropertyName은 속성 이름을 인자로 받습니다. // 이 매크로는 속성의 getter, setter 및 초기화 함수를 정의합니다. #define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \ GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \ GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \ GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \ GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName) /** * */ UCLASS() class SAMPLEPROJECT_API UCharacterAttributeSetBase : public UAttributeSet { GENERATED_BODY() public: UPROPERTY(BlueprintReadOnly, Category = "Level") FGameplayAttributeData Level; ATTRIBUTE_ACCESSORS(UCharacterAttributeSetBase, Level) UPROPERTY(BlueprintReadOnly, Category = "Health") FGameplayAttributeData Health; ATTRIBUTE_ACCESSORS(UCharacterAttributeSetBase, Health) UPROPERTY(BlueprintReadOnly, Category = "Health") FGameplayAttributeData MaxHealth; ATTRIBUTE_ACCESSORS(UCharacterAttributeSetBase, MaxHealth) UPROPERTY(BlueprintReadOnly, Category= "Mana") FGameplayAttributeData Mana; ATTRIBUTE_ACCESSORS(UCharacterAttributeSetBase, Mana) UPROPERTY(BlueprintReadOnly, Category= "Mana") FGameplayAttributeData MaxMana; ATTRIBUTE_ACCESSORS(UCharacterAttributeSetBase, MaxMana) UPROPERTY(BlueprintReadOnly, Category="Damage") FGameplayAttributeData Damage; ATTRIBUTE_ACCESSORS(UCharacterAttributeSetBase, Damage); }
어트리뷰트 세트의 리플리케이션 설정
어트리뷰트 세트 내에서 개별 게임플레이 어트리뷰트의 리플리케이션을 설정하는 것은 멀티플레이어 게임에서 각 플레이어의 게임 상태를 동기화하는 데 필수적입니다. 예를 들어, 캐릭터의 'Health' 어트리뷰트를 리플리케이트하려면 다음 단계를 따라야 합니다:
1. 어트리뷰트 프로퍼티 정의
UPROPERTY 매크로를 사용하여 어트리뷰트를 정의하고, ReplicatedUsing 지정자를 추가하여 리플리케이션 시 호출될 콜백 함수를 지정합니다.
UCLASS() class SAMPLEPROJECT_API UCharacterAttributeSetBase : public UAttributeSet { GENERATED_BODY() public: // Level 속성: 캐릭터의 현재 건강 수치를 나타내며, 변경 시 OnRep_Health 함수를 호출합니다. UPROPERTY(BlueprintReadOnly, Category = "Level", ReplicatedUsing = OnRep_Level) FGameplayAttributeData Level; ATTRIBUTE_ACCESSORS(UCharacterAttributeSetBase, Level) // Health 속성: 캐릭터의 현재 건강 수치를 나타내며, 변경 시 OnRep_Health 함수를 호출합니다. UPROPERTY(BlueprintReadOnly, Category = "Health", ReplicatedUsing = OnRep_Health) FGameplayAttributeData Health; ATTRIBUTE_ACCESSORS(UCharacterAttributeSetBase, Health) // MaxHealth 속성: 캐릭터의 최대 건강 수치를 나타내며, 변경 시 OnRep_MaxHealth 함수를 호출합니다. UPROPERTY(BlueprintReadOnly, Category = "Health", ReplicatedUsing = OnRep_MaxHealth) FGameplayAttributeData MaxHealth; ATTRIBUTE_ACCESSORS(UCharacterAttributeSetBase, MaxHealth) // Mana 속성: 캐릭터의 현재 마나 수치를 나타내며, 변경 시 OnRep_Mana 함수를 호출합니다. UPROPERTY(BlueprintReadOnly, Category= "Mana", ReplicatedUsing= OnRep_Mana) FGameplayAttributeData Mana; ATTRIBUTE_ACCESSORS(UCharacterAttributeSetBase, Mana) // MaxMana 속성: 캐릭터의 최대 마나 수치를 나타내며, 변경 시 OnRep_MaxMana 함수를 호출합니다. UPROPERTY(BlueprintReadOnly, Category= "Mana", ReplicatedUsing= OnRep_MaxMana) FGameplayAttributeData MaxMana; ATTRIBUTE_ACCESSORS(UCharacterAttributeSetBase, MaxMana) // Damage 속성: 캐릭터가 입히는 피해량을 나타냅니다. 이 값은 능력 시스템을 통해 변경될 수 있습니다. UPROPERTY(BlueprintReadOnly, Category="Damage") FGameplayAttributeData Damage; ATTRIBUTE_ACCESSORS(UCharacterAttributeSetBase, Damage); }
2. 리플리케이션 콜백 함수 선언
네트워크를 통해 새로운 'Attribute' 값이 도착할 때 호출될 콜백 함수를 선언합니다
// Health 속성 변경 시 호출되는 함수, 변경 전 건강 수치를 인자로 받습니다. UFUNCTION() virtual void OnRep_Level(const FGameplayAttributeData& OldLevel); // Health 속성 변경 시 호출되는 함수, 변경 전 건강 수치를 인자로 받습니다. UFUNCTION() virtual void OnRep_Health(const FGameplayAttributeData& OldHealth); // MaxHealth 속성 변경 시 호출되는 함수, 변경 전 최대 건강 수치를 인자로 받습니다. UFUNCTION() virtual void OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth); // Mana 속성 변경 시 호출되는 함수, 변경 전 마나 수치를 인자로 받습니다. UFUNCTION() virtual void OnRep_Mana(const FGameplayAttributeData& OldMana); // MaxMana 속성 변경 시 호출되는 함수, 변경 전 최대 마나 수치를 인자로 받습니다. UFUNCTION() virtual void OnRep_MaxMana(const FGameplayAttributeData& OldMaxMana); // 네트워크 복제를 위한 속성들을 설정하는 함수. virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override; };
3. 콜백 함수 구현
어트리뷰트 세트의 소스 파일에서 콜백 함수를 정의하고, GAMEPLAYATTRIBUTE_REPNOTIFY 매크로를 사용하여 기본적인 리플리케이션 행동을 구현합니다.
#include "Character/Abilities/AttributeSets/CharacterAttributeSetBase.h" #include "Net/UnrealNetwork.h" void UCharacterAttributeSetBase::OnRep_Level(const FGameplayAttributeData& OldLevel) { // Health 속성의 변경을 처리하고, 필요한 UI 업데이트나 게임 로직을 트리거할 수 있습니다. GAMEPLAYATTRIBUTE_REPNOTIFY(UCharacterAttributeSetBase, Level, OldLevel); } // Health 속성이 네트워크를 통해 변경되었을 때 호출되는 함수입니다. void UCharacterAttributeSetBase::OnRep_Health(const FGameplayAttributeData& OldHealth) { // Health 속성의 변경을 처리하고, 필요한 UI 업데이트나 게임 로직을 트리거할 수 있습니다. GAMEPLAYATTRIBUTE_REPNOTIFY(UCharacterAttributeSetBase, Health, OldHealth); } // MaxHealth 속성이 네트워크를 통해 변경되었을 때 호출되는 함수입니다. void UCharacterAttributeSetBase::OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth) { // MaxHealth 속성의 변경을 처리합니다. GAMEPLAYATTRIBUTE_REPNOTIFY(UCharacterAttributeSetBase, MaxHealth, OldMaxHealth); } // Mana 속성이 네트워크를 통해 변경되었을 때 호출되는 함수입니다. void UCharacterAttributeSetBase::OnRep_Mana(const FGameplayAttributeData& OldMana) { // Mana 속성의 변경을 처리합니다. GAMEPLAYATTRIBUTE_REPNOTIFY(UCharacterAttributeSetBase, Mana, OldMana); } // MaxMana 속성이 네트워크를 통해 변경되었을 때 호출되는 함수입니다. void UCharacterAttributeSetBase::OnRep_MaxMana(const FGameplayAttributeData& OldMaxMana) { // MaxMana 속성의 변경을 처리합니다. GAMEPLAYATTRIBUTE_REPNOTIFY(UCharacterAttributeSetBase, MaxMana, OldMaxMana); }
4. 리플리케이션 프로퍼티 등록
GetLifetimeReplicatedProps 함수를 오버라이드하여 어트리뷰트의 리플리케이션을 설정합니다. 이 함수는 어떤 프로퍼티가 네트워크를 통해 복제될지 결정합니다.
// 네트워크 동기화를 위해 속성 복제를 설정하는 함수입니다. void UCharacterAttributeSetBase::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const { // 부모 클래스의 동일 함수를 호출하여 기본 속성 복제 설정을 상속받습니다. Super::GetLifetimeReplicatedProps(OutLifetimeProps); // 각 속성의 네트워크 복제를 설정합니다. 이 속성들은 항상 네트워크에 복제되며, 값이 변경되면 통지가 발생합니다. DOREPLIFETIME_CONDITION_NOTIFY(UCharacterAttributeSetBase, Level, COND_None, REPNOTIFY_Always); DOREPLIFETIME_CONDITION_NOTIFY(UCharacterAttributeSetBase, Health, COND_None, REPNOTIFY_Always); DOREPLIFETIME_CONDITION_NOTIFY(UCharacterAttributeSetBase, MaxHealth, COND_None, REPNOTIFY_Always); DOREPLIFETIME_CONDITION_NOTIFY(UCharacterAttributeSetBase, Mana, COND_None, REPNOTIFY_Always); DOREPLIFETIME_CONDITION_NOTIFY(UCharacterAttributeSetBase, MaxMana, COND_None, REPNOTIFY_Always); }
전체코드
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "AttributeSet.h" #include "AbilitySystemComponent.h" #include "CharacterAttributeSetBase.generated.h" // ATTRIBUTE_ACCESSORS 매크로: 주어진 속성에 대한 접근자 함수들을 자동으로 생성합니다. // ClassName은 클래스 이름, PropertyName은 속성 이름을 인자로 받습니다. // 이 매크로는 속성의 getter, setter 및 초기화 함수를 정의합니다. #define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \ GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \ GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \ GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \ GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName) /** * */ UCLASS() class SAMPLEPROJECT_API UCharacterAttributeSetBase : public UAttributeSet { GENERATED_BODY() public: // Level 속성: 캐릭터의 현재 건강 수치를 나타내며, 변경 시 OnRep_Health 함수를 호출합니다. UPROPERTY(BlueprintReadOnly, Category = "Level", ReplicatedUsing = OnRep_Level) FGameplayAttributeData Level; ATTRIBUTE_ACCESSORS(UCharacterAttributeSetBase, Level) // Health 속성: 캐릭터의 현재 건강 수치를 나타내며, 변경 시 OnRep_Health 함수를 호출합니다. UPROPERTY(BlueprintReadOnly, Category = "Health", ReplicatedUsing = OnRep_Health) FGameplayAttributeData Health; ATTRIBUTE_ACCESSORS(UCharacterAttributeSetBase, Health) // MaxHealth 속성: 캐릭터의 최대 건강 수치를 나타내며, 변경 시 OnRep_MaxHealth 함수를 호출합니다. UPROPERTY(BlueprintReadOnly, Category = "Health", ReplicatedUsing = OnRep_MaxHealth) FGameplayAttributeData MaxHealth; ATTRIBUTE_ACCESSORS(UCharacterAttributeSetBase, MaxHealth) // Mana 속성: 캐릭터의 현재 마나 수치를 나타내며, 변경 시 OnRep_Mana 함수를 호출합니다. UPROPERTY(BlueprintReadOnly, Category= "Mana", ReplicatedUsing= OnRep_Mana) FGameplayAttributeData Mana; ATTRIBUTE_ACCESSORS(UCharacterAttributeSetBase, Mana) // MaxMana 속성: 캐릭터의 최대 마나 수치를 나타내며, 변경 시 OnRep_MaxMana 함수를 호출합니다. UPROPERTY(BlueprintReadOnly, Category= "Mana", ReplicatedUsing= OnRep_MaxMana) FGameplayAttributeData MaxMana; ATTRIBUTE_ACCESSORS(UCharacterAttributeSetBase, MaxMana) // Damage 속성: 캐릭터가 입히는 피해량을 나타냅니다. 이 값은 능력 시스템을 통해 변경될 수 있습니다. UPROPERTY(BlueprintReadOnly, Category="Damage") FGameplayAttributeData Damage; ATTRIBUTE_ACCESSORS(UCharacterAttributeSetBase, Damage); // Health 속성 변경 시 호출되는 함수, 변경 전 건강 수치를 인자로 받습니다. UFUNCTION() virtual void OnRep_Level(const FGameplayAttributeData& OldLevel); // Health 속성 변경 시 호출되는 함수, 변경 전 건강 수치를 인자로 받습니다. UFUNCTION() virtual void OnRep_Health(const FGameplayAttributeData& OldHealth); // MaxHealth 속성 변경 시 호출되는 함수, 변경 전 최대 건강 수치를 인자로 받습니다. UFUNCTION() virtual void OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth); // Mana 속성 변경 시 호출되는 함수, 변경 전 마나 수치를 인자로 받습니다. UFUNCTION() virtual void OnRep_Mana(const FGameplayAttributeData& OldMana); // MaxMana 속성 변경 시 호출되는 함수, 변경 전 최대 마나 수치를 인자로 받습니다. UFUNCTION() virtual void OnRep_MaxMana(const FGameplayAttributeData& OldMaxMana); // 네트워크 복제를 위한 속성들을 설정하는 함수. virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override; };
// Fill out your copyright notice in the Description page of Project Settings. #include "Character/Abilities/AttributeSets/CharacterAttributeSetBase.h" #include "Net/UnrealNetwork.h" void UCharacterAttributeSetBase::OnRep_Level(const FGameplayAttributeData& OldLevel) { // Health 속성의 변경을 처리하고, 필요한 UI 업데이트나 게임 로직을 트리거할 수 있습니다. GAMEPLAYATTRIBUTE_REPNOTIFY(UCharacterAttributeSetBase, Level, OldLevel); } // Health 속성이 네트워크를 통해 변경되었을 때 호출되는 함수입니다. void UCharacterAttributeSetBase::OnRep_Health(const FGameplayAttributeData& OldHealth) { // Health 속성의 변경을 처리하고, 필요한 UI 업데이트나 게임 로직을 트리거할 수 있습니다. GAMEPLAYATTRIBUTE_REPNOTIFY(UCharacterAttributeSetBase, Health, OldHealth); } // MaxHealth 속성이 네트워크를 통해 변경되었을 때 호출되는 함수입니다. void UCharacterAttributeSetBase::OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth) { // MaxHealth 속성의 변경을 처리합니다. GAMEPLAYATTRIBUTE_REPNOTIFY(UCharacterAttributeSetBase, MaxHealth, OldMaxHealth); } // Mana 속성이 네트워크를 통해 변경되었을 때 호출되는 함수입니다. void UCharacterAttributeSetBase::OnRep_Mana(const FGameplayAttributeData& OldMana) { // Mana 속성의 변경을 처리합니다. GAMEPLAYATTRIBUTE_REPNOTIFY(UCharacterAttributeSetBase, Mana, OldMana); } // MaxMana 속성이 네트워크를 통해 변경되었을 때 호출되는 함수입니다. void UCharacterAttributeSetBase::OnRep_MaxMana(const FGameplayAttributeData& OldMaxMana) { // MaxMana 속성의 변경을 처리합니다. GAMEPLAYATTRIBUTE_REPNOTIFY(UCharacterAttributeSetBase, MaxMana, OldMaxMana); } // 네트워크 동기화를 위해 속성 복제를 설정하는 함수입니다. void UCharacterAttributeSetBase::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const { // 부모 클래스의 동일 함수를 호출하여 기본 속성 복제 설정을 상속받습니다. Super::GetLifetimeReplicatedProps(OutLifetimeProps); // 각 속성의 네트워크 복제를 설정합니다. 이 속성들은 항상 네트워크에 복제되며, 값이 변경되면 통지가 발생합니다. DOREPLIFETIME_CONDITION_NOTIFY(UCharacterAttributeSetBase, Level, COND_None, REPNOTIFY_Always); DOREPLIFETIME_CONDITION_NOTIFY(UCharacterAttributeSetBase, Health, COND_None, REPNOTIFY_Always); DOREPLIFETIME_CONDITION_NOTIFY(UCharacterAttributeSetBase, MaxHealth, COND_None, REPNOTIFY_Always); DOREPLIFETIME_CONDITION_NOTIFY(UCharacterAttributeSetBase, Mana, COND_None, REPNOTIFY_Always); DOREPLIFETIME_CONDITION_NOTIFY(UCharacterAttributeSetBase, MaxMana, COND_None, REPNOTIFY_Always); }
결론
Unreal Engine의 Gameplay Ability System에서 속성을 효과적으로 관리하는 것은 게임의 다이내믹을 조절하고 플레이어에게 몰입감 있는 경험을 제공하는 데 필수적입니다. 속성 시스템을 통해 개발자는 캐릭터의 다양한 상태와 능력을 정밀하게 조절하며, 게임의 다양한 상황에 맞게 적응시킬 수 있습니다.
반응형다음글이전글이전 글이 없습니다.댓글