보키_기록용
[GameplayAbilitySystem] 시작하기 본문
System Setup
- 플러그인 편집창에서 Gameplay Abilities 활성
- (프로젝트이름).build.cs 파일에 PublicDependencyModuleNames에 "GameplayAbilities", "GameplayTags"및 "GameplayTasks"를 추가한다.
PublicDependencyModuleNames.AddRange(new string[] { "GameplayAbilities", "GameplayTags", "GameplayTasks",
"Core", "CoreUObject", "Engine", "InputCore" });
기본 용어
- Attribute Set
- FGameplayAttributeData 구조체에 정의된다.
- current/max health (현재/최대 생명력), mana (마나), attack (공격) 및 defense (방어) buffs (버프), movement speed (이동 속도), 대미지 산출 공식에 사용되는 temporary damage (임시 대미지) 어트리뷰트를 정의
- 영구적인 변경으로만 수정되는 Base value (기본 값)과 임시 버프/디버프로 수정되는 Current value (현재 값)을 저장
- Gameplay Effect
- Attribute를 변경하기 위한 방편
- Attribute의 기본 값에 대한 직접적인 변경 ex ) 피해받은 액터의 생명력 줄이기
- 버프, 디버프 ex ) 몇 초 동안 이동속도 증폭
- 시간에 따라 적용되는 지속 변화 ex ) 초당 마나 재생
Attribute Set 생성
1. 매크로 생성
#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)
Get, Set 함수를 자동으로 만든다.
2. 어트리뷰트 프로퍼티 정의 (예시로 Health)
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_Health, Category = "Attributes")
FGameplayAttributeData Health;
ATTRIBUTE_ACCESSORS(UGASTestAttributeSet, Health);
UFUNCTION()
virtual void OnRep_Health(const FGameplayAttributeData& OldValue);
리플리케이션하기때문에 GetLifetimeReplicatedProps에서 등록해줘야 한다. (.cpp)
void UGASTestAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION_NOTIFY(ULyraHealthSet, Health, COND_None, REPNOTIFY_Always);
}
void UGASTestAttributeSet::OnRep_Health(const FGameplayAttributeData& OldValue)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UGASTestAttributeSet, Health, OldValue);
}
Effect 및 Attribute 관련 함수
1. PreAttributeChange / PreAttributeBaseChange
어트리뷰트 수정 직전 호출 함수. 어트리뷰트의 값에 대한 규칙을 적용
ex ) Health 값은 0과 MaxHealth 사이어야 한다.
void UGASTestAttributeSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
{
Super::PreAttributeChange(Attribute, NewValue);
if (Attribute == GetMaxHealthAttribute())
{
AdjestAttributeForMaxChange(Health, MaxHealth, NewValue, GetHealthAttribute());
}
}
void UGASTestAttributeSet::AdjestAttributeForMaxChange(const FGameplayAttributeData& AffectedAttribute,
const FGameplayAttributeData& MaxAttribute, float NewMaxValue,
const FGameplayAttribute& AffectedAttributeProperty) const
{
UAbilitySystemComponent* AbilityComponent = GetOwningAbilitySystemComponent();
const float CurrentMaxValue = MaxAttribute.GetCurrentValue();
if (!FMath::IsNearlyEqual(CurrentMaxValue, NewMaxValue) && AbilityComponent)
{
const float CurrentValue = AffectedAttribute.GetCurrentValue();
const float NewDelta = (CurrentMaxValue > 0.f) ? (CurrentValue * NewMaxValue / CurrentMaxValue) - CurrentValue : NewMaxValue;
AbilityComponent->ApplyModToAttributeUnsafe(AffectedAttributeProperty, EGameplayModOp::Additive, NewDelta);
}
}
2. PostGameplayEffectExecute
어트리뷰트 값 수정 직후. 보통 어트리뷰트 최종 값에 대한 클램핑(범위 제한)이나 새 값에 대한 게임 내 반응 포함
ex ) Health 어트리뷰트가 0으로 떨어지면 사망효과 발동
void UGASTestAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
Super::PostGameplayEffectExecute(Data);
FGameplayEffectContextHandle Context = Data.EffectSpec.GetContext();
UAbilitySystemComponent* Source = Context.GetOriginalInstigatorAbilitySystemComponent();
const FGameplayTagContainer& SourceTags = *Data.EffectSpec.CapturedSourceTags.GetAggregatedTags();
float DeltaValue { 0.f };
// Clamp되기 전 Attribute 최종값
if (Data.EvaluatedData.ModifierOp == EGameplayModOp::Type::Additive)
{
DeltaValue = Data.EvaluatedData.Magnitude;
}
AGASTestCharacter* TargetCharacter { nullptr };
//Attribute 값 주인 찾기
if (Data.Target.AbilityActorInfo.IsValid() && Data.Target.AbilityActorInfo->AvatarActor.IsValid())
{
AActor* TargetActor { nullptr };
TargetActor = Data.Target.AbilityActorInfo->AvatarActor.Get();
TargetCharacter = Cast<AGASTestCharacter>(TargetActor);
}
//Clamp해서 적용. Health값이 변경 됐을 때의 액션들 불러오기
if (Data.EvaluatedData.Attribute == GetHealthAttribute())
{
SetHealth(FMath::Clamp(GetHealth(), 0.f, GetMaxHealth()));
if (TargetCharacter)
{
TargetCharacter->HandleHealthChanged(DeltaValue, SourceTags);
}
}
}
Ability Input Binding
1. Enum 정의하기
UENUM(BlueprintType)
enum class EGASTestAbilityInputID : uint8
{
None,
Confirm,
Cancel,
Punch //Ability01
};
2. 편집>프로젝트 세팅>입력에서 같은 이름으로 바인딩. Enum의 이름과 일치해야 한다.
3. GameplayAbility 파생 클래스 만들기
UCLASS()
class GAMEPLAYABILITYTEST_API UGASTestGameplayAbility : public UGameplayAbility
{
GENERATED_BODY()
public:
UGASTestGameplayAbility();
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Ability")
EGASTestAbilityInputID AbilityInputID { EGASTestAbilityInputID::None };
};
4. 캐릭터의 SetupPlayerInputComponent에 바인딩한다.
void AGASTestCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
if (AbilitySystemComponent && InputComponent)
{
const FGameplayAbilityInputBinds Binds(
"Confirm",
"Cancel",
"EGASTestAbilityInputID",
static_cast<int32>(EGASTestAbilityInputID::Confirm),
static_cast<int32>(EGASTestAbilityInputID::Cancel));
AbilitySystemComponent->BindAbilityActivationToInputComponent(InputComponent, Binds);
}
}
5. AddStartGameplayAbilities 함수를 만들고 Ability가 추가될 때 AbilityInputID를 바인딩한다.
void AGASTestCharacter::AddStartGameplayAbilities()
{
check(AbilitySystemComponent);
if (GetLocalRole() == ROLE_Authority && !bAbilityInitialized)
{
for (TSubclassOf<UGASTestGameplayAbility>& StartupAbility : GameplayAbilities)
{
AbilitySystemComponent->GiveAbility(FGameplayAbilitySpec(
StartupAbility, 1,
static_cast<int32>(StartupAbility.GetDefaultObject()->AbilityInputID),
this));
}
bAbilityInitialized = true;
}
}
6. PossessdBy에서 AbilityActor 초기화하고 AddStartGameplayAbilities 호출
void AGASTestCharacter::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);
if (AbilitySystemComponent)
{
AbilitySystemComponent->InitAbilityActorInfo(this, this);
AddStartGameplayAbilities(); // Ability 추가
}
}
참고
언리얼 포 게임플레이 어빌리티 시스템 소개 (11)-능력과 바인딩 입력 (programming.vip)
Unreal four Gameplay Ability System introduction (11)-Ability and binding input
After writing ten articles, I found that I haven't talked about the Ability that GAS has been using. Here is a brief introduction to skills. The relationship and interaction between the server and the client are not involved here, because I am not familiar
programming.vip
(21) [UE5 - C++] Gameplay Ability System in Unreal Engine 5 - Part 01 - The Code - YouTube
ARPG 의 어트리뷰트와 이펙트 | 언리얼 엔진 문서 (unrealengine.com)
ARPG 의 어트리뷰트와 이펙트
ARPG 에서 어트리뷰트와 이펙트를 어떻게 사용했는지 살펴봅니다.
docs.unrealengine.com
게임플레이 어트리뷰트 및 게임플레이 이펙트 | 언리얼 엔진 5.0 문서 (unrealengine.com)
게임플레이 어트리뷰트 및 게임플레이 이펙트
Gameplay Ability System 안의 Attribute 및 Effect 에 대한 개요입니다.
docs.unrealengine.com
'언리얼 > Framework' 카테고리의 다른 글
[GameplayAbilitySystem] DOREPLIFETIME_CONDITION_NOTIFY에 대해 (Prediction) (0) | 2022.09.01 |
---|---|
[GameplayAbilitySystem] 정리 (0) | 2022.08.30 |
[GameplayAbilitySystem] Ability C++로 만들기 (0) | 2022.08.30 |
[GameplayAbilitySystem] Ability 써서 체력 닳게 하기 (2) (0) | 2022.08.26 |
[GameplayAbilitySystem] Ability 써서 체력 닳게 하기 (1) (0) | 2022.08.26 |