언리얼 엔진의 게임플레이 어빌리티 시스템용 게임플레이 어트리뷰트 및 게임플레이 이펙트 |
게임플레이 어빌리티 시스템 내 어트리뷰트 및 이펙트의 개요입니다.
dev.epicgames.com
https://github.com/tranek/GASDocumentation?tab=readme-ov-file#concepts-ge
GitHub - tranek/GASDocumentation: My understanding of Unreal Engine 5's GameplayAbilitySystem plugin with a simple multiplayer s
My understanding of Unreal Engine 5's GameplayAbilitySystem plugin with a simple multiplayer sample project. - tranek/GASDocumentation
github.com
- GAS에서 Attribute를 변경하는 방식
- UGameEffect를 상속받아 구현
- BlueprintNative로 작업하지 않는 대신, 변수를 통해 환경설정을 한다.
- GameplayEffects(이하 GE)는 Instance를 생성하지 않는다.
- Ability나 ASC가 GE를 등록하기 원하면, GE는 ClassDefaultObjects로부터 GameplayEffectSpec를 생성한다.
- 성공적으로 생성된 GameplayEffectSpec은 FActiveGameplayEffect에 추가되어
ASC의 ActiveGameplayEffects에 저장된다.
Apply
- GE는 GameplayAbilities와 ASC에서 제공하는 수 많은 ApplyGameplayEffectTo가 포함된 함수로 등록한다.
- 또한 Duration, Infinite GE가 ASC에 등록될 때 Delegate를 받을 수 있다.
-
더보기
/** Delegate for when an effect is applied */ DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnGameplayEffectAppliedDelegate, UAbilitySystemComponent*, const FGameplayEffectSpec&, FActiveGameplayEffectHandle); /** Called on server whenever a GE is applied to self. This includes instant and duration based GEs. */ FOnGameplayEffectAppliedDelegate OnGameplayEffectAppliedDelegateToSelf; /** Called on server whenever a GE is applied to someone else. This includes instant and duration based GEs. */ FOnGameplayEffectAppliedDelegate OnGameplayEffectAppliedDelegateToTarget; /** Called on both client and server whenever a duration based GE is added (E.g., instant GEs do not trigger this). */ FOnGameplayEffectAppliedDelegate OnActiveGameplayEffectAddedDelegateToSelf; /** Called on server whenever a periodic GE executes on self */ FOnGameplayEffectAppliedDelegate OnPeriodicGameplayEffectExecuteDelegateOnSelf; /** Called on server whenever a periodic GE executes on target */ FOnGameplayEffectAppliedDelegate OnPeriodicGameplayEffectExecuteDelegateOnTarget; /** This ASC has successfully applied a GE to something (potentially itself) */ void OnGameplayEffectAppliedToTarget(UAbilitySystemComponent* Target, const FGameplayEffectSpec& SpecApplied, FActiveGameplayEffectHandle ActiveHandle); void OnGameplayEffectAppliedToSelf(UAbilitySystemComponent* Source, const FGameplayEffectSpec& SpecApplied, FActiveGameplayEffectHandle ActiveHandle); void OnPeriodicGameplayEffectExecuteOnTarget(UAbilitySystemComponent* Target, const FGameplayEffectSpec& SpecExecuted, FActiveGameplayEffectHandle ActiveHandle); void OnPeriodicGameplayEffectExecuteOnSelf(UAbilitySystemComponent* Source, const FGameplayEffectSpec& SpecExecuted, FActiveGameplayEffectHandle ActiveHandle);
-
- 위 함수들은 LocalRole에 따라 호출 여부가 다르다.
- Authority: 언제나 항상 호출
- Autonomous: 대상 GE의 Replication Mode가 Full이거나 Mixed인 경우에만 호출 됨
- Simulated: 대상 GE의 Replication Mode가 Full일 때에만 호출 됨
Remove
- GE는 GameplayAbilities와 ASC에서 RemoveActiveGameplayEffect가 포함된 함수들에 의해 제거된다.
- 또한 Duration, Infinite Ge는 제거될 때 Delegate를 받을 수 있다.
-
더보기
/** Contains all of the gameplay effects that are currently active on this component */ UPROPERTY(Replicated) FActiveGameplayEffectsContainer ActiveGameplayEffects; DECLARE_MULTICAST_DELEGATE_OneParam(FOnGivenActiveGameplayEffectRemoved, const FActiveGameplayEffect&); DECLARE_MULTICAST_DELEGATE_OneParam(FOnActiveGameplayEffectRemoved_Info, const FGameplayEffectRemovalInfo&); /** Called when any gameplay effects are removed */ FOnGivenActiveGameplayEffectRemoved& OnAnyGameplayEffectRemovedDelegate(); /** Returns delegate structure that allows binding to several gameplay effect changes */ FOnActiveGameplayEffectRemoved_Info* OnGameplayEffectRemoved_InfoDelegate(FActiveGameplayEffectHandle Handle);
-
- 위 함수들은 LocalRole에 따라 호출 여부가 다르다.
- Authority: 언제나 항상 호출
- Autonomous: 대상 GE의 Replication Mode가 Full이거나 Mixed인 경우에만 호출 됨
- Simulated: 대상 GE의 Replication Mode가 Full일 때에만 호출 됨
Duration
더보기
/** Gameplay effect duration policies */
UENUM()
enum class EGameplayEffectDurationType : uint8
{
/** This effect applies instantly */
Instant,
/** This effect lasts forever */
Infinite,
/** The duration of this effect will be specified by a magnitude */
HasDuration
};
Instant
- Attribute의 BaseValue를 즉시 영구적으로 바꾼다.
- GameplayTags 사용이 불가하다.
- GameplayCue, GameplayTags에서 Execute를 호출
Infinite
- Attribute의 CurrentValue를 일시적으로 바꾼다.
- GE가 삭제될 때 GameplayTags도 삭제된다.
- ASC에서 스스로 만료되지 않기 때문에 반드시 명시적으로 삭제되어야 한다.
- GameplayCue에서 Add, Remove를 호출.
- GE가 발생하는 시점과 효과가 발동하는 시점을 다르게 등록할 수 있음
- Audio, Visual Effect 타이밍 구성에도 유용함
HasDuration
- Attribute의 CurrentValue를 일시적으로 바꾼다.
- GE가 만료되거나 삭제될 때 GameplayTags도 삭제된다.
- Duration은 UGameplayEffect BP로 지정된다.
- GameplayCue에서 Add, Remove를 호출.
- GE가 발생하는 시점과 효과가 발동하는 시점을 다르게 등록할 수 있음
- Audio, Visual Effect 타이밍 구성에도 유용함
Period
더보기
/** Period in seconds. 0.0 for non-periodic effects */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Duration|Period", meta=(EditCondition="DurationPolicy != EGameplayEffectDurationType::Instant", EditConditionHides))
FScalableFloat Period;
- Duration 타입이 HasDuration, Infinite인 경우에만 적용
- GE의 Modifiers나 Executions를 등록된 수치의 seconds만큼 대기 했다가 실행
- 이 때 Delay를 갖고 발생하는 Effects들은 다음 조건을 만족하는 경우 Instant GE로 취급한다.
- Attribute의 BaseValue를 변경하는 경우
- GameplayCue를 실행하는 경우
- 도트딜을 작업할 때 유용함
Modifiers
더보기
/**
* FGameplayModifierInfo
* Tells us "Who/What we" modify
* Does not tell us how exactly
*/
USTRUCT(BlueprintType)
struct GAMEPLAYABILITIES_API FGameplayModifierInfo
{
GENERATED_USTRUCT_BODY()
/** The Attribute we modify or the GE we modify modifies. */
UPROPERTY(EditDefaultsOnly, Category=GameplayModifier, meta=(FilterMetaTag="HideFromModifiers"))
FGameplayAttribute Attribute;
/** The numeric operation of this modifier: Override, Add, Multiply, etc */
UPROPERTY(EditDefaultsOnly, Category=GameplayModifier)
TEnumAsByte<EGameplayModOp::Type> ModifierOp = EGameplayModOp::Additive;
/** Magnitude of the modifier */
UPROPERTY(EditDefaultsOnly, Category=GameplayModifier)
FGameplayEffectModifierMagnitude ModifierMagnitude;
/** Evaluation channel settings of the modifier */
UPROPERTY(EditDefaultsOnly, Category=GameplayModifier)
FGameplayModEvaluationChannelSettings EvaluationChannelSettings;
UPROPERTY(EditDefaultsOnly, Category=GameplayModifier)
FGameplayTagRequirements SourceTags;
UPROPERTY(EditDefaultsOnly, Category=GameplayModifier)
FGameplayTagRequirements TargetTags;
/** Equality/Inequality operators */
bool operator==(const FGameplayModifierInfo& Other) const;
bool operator!=(const FGameplayModifierInfo& Other) const;
};
/** Array of modifiers that will affect the target of this effect */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category=GameplayEffect, meta=(TitleProperty=Attribute))
TArray<FGameplayModifierInfo> Modifiers;
- GameplayEffect가 Attribute와 상호작용 하는 방식을 결정
- Attribute 자체에 대한 수학적 Interaction뿐 아니라, GameplayTag 정보도 포함된다.
- 각 Modifier는 특정 연산으로 하나의 Attribute만 수정할 책임이 있다.
- Add
- Modifier의 특정 Attribute에 결과 값을 더한다.
- 음수를 사용하여 뺄셈을 구현한다.
- Multiply
- Modifier의 특정 Attribute에 결과 값을 곱한다.
- Divide
- Modifier의 특정 Attribute에 결과 값을 나눈다.
- Override
- Modifier의 특정 Attribute에 결과 값을 덮어씌운다.
- Add
- Attribute의 CurrentValue는 BaseValue에 가해지는 모든 Modifier의 종합 결과이다.
- Modifier가 어떻게 집계될지에 대한 공식은 다음 함수에 표현되어 있다.
-
더보기
float FAggregatorModChannel::EvaluateWithBase(float InlineBaseValue, const FAggregatorEvaluateParameters& Parameters) const { for (const FAggregatorMod& Mod : Mods[EGameplayModOp::Override]) { if (Mod.Qualifies()) { return Mod.EvaluatedMagnitude; } } float Additive = SumMods(Mods[EGameplayModOp::Additive], GameplayEffectUtilities::GetModifierBiasByModifierOp(EGameplayModOp::Additive), Parameters); float Multiplicitive = SumMods(Mods[EGameplayModOp::Multiplicitive], GameplayEffectUtilities::GetModifierBiasByModifierOp(EGameplayModOp::Multiplicitive), Parameters); float Division = SumMods(Mods[EGameplayModOp::Division], GameplayEffectUtilities::GetModifierBiasByModifierOp(EGameplayModOp::Division), Parameters); if (FMath::IsNearlyZero(Division)) { ABILITY_LOG(Warning, TEXT("Division summation was 0.0f in FAggregatorModChannel.")); Division = 1.f; } return ((InlineBaseValue + Additive) * Multiplicitive) / Division; }
- 위 연산을 마치면 가장 마지막에 Override Modifier가 동작한다.
-
Modifier Type
Scalable Float
- 변수를 Row로 갖고 Level을 Column으로 갖는 Data Table을 가르킬 수 있는 구조체
- Ability의 현재 Level이나 GameplayEffectSpec에서 override 된 다른 Level을 가진 Row의 Value를 자동으로 읽는다.
- 이 값은 계수에 의해 변동될 수 있다.
- 만약 특정 DataTable이나 Row가 없다면 값이 1이 되어 계수 값을 가진 하나의 변수로 사용 가능하다.
Attribute Based
- Source(GameplayEffectSpec을 만든 주체)나 Target(GameplayEffecSpec을 받을 주체)의 Attribute의 Current/Base Value를 기반으로 사용
- 위 값을 기반으로 계수나 계수 전후의 추가값을 사용하여 수정
- Snapshooting이 세팅되면 GameplayEffectSpec이 생성될 때 Attribute이 캡쳐 된다.
- Snapshooting이 설정되지 않으면 GameplayEffeectSpec이 등록 될 때 Attribute가 캡쳐된다.
Custom Calculation Class
- 복잡한 Modifier에 대해 가장 유연함을 자랑한다.
- ModifierMagnitudeCalculation class를 사용하고 결과에 계수, 계수 전후 추가값으로 추가 보정이 가능하다.
Set By Caller
- Runtime 상에서 Ability나 GameplayEffectSpec에서 GameplayEffectSpec 만들면서 GE 외에서 값에 영향을 미치는 경우에 사용
- 예를 들어 버튼을 Hold한 만큼 데미지가 증가하는 경우 이 타입을 사용한다.
- 이 타입은 사실 GameplayEffectSpec에 있는 TMap<FGameplayTag, float>
- Modifier는 그저 제공된 GameplayTag와 관련된 SetByCaller 값을 읽고 연산할 뿐이다.
- 때문에 Modifier에서 사용하는 SetByCallers는 GameplayTag 버전만 사용 가능하고, FName 버전은 불가하다.
- Modifier가 SetByCaller 타입으로 선언 되었으나 GameplayEffectSpec에 적절한 GameTag가 없는 경우,
게임은 Runtime Error를 생성하며 0을 반환한다.- 이 케이스는 주로 Divide 연산에서 자주 발생한다.
Multiply/Divide Modifiers
- 기본적으로 Multiply/Divide Modifier는 Attribute의 BaseValue에 연산이 적용되기 전에 함께 추가된다.
-
더보기
/** * Helper function to sum all of the mods in the specified array, using the specified modifier bias and evaluation parameters * * @param InMods Mods to sum * @param Bias Bias to apply to modifier magnitudes * @param Parameters Evaluation parameters * * @return Summed value of mods */ float FAggregatorModChannel::SumMods(const TArray<FAggregatorMod>& InMods, float Bias, const FAggregatorEvaluateParameters& Parameters) { float Sum = Bias; for (const FAggregatorMod& Mod : InMods) { if (Mod.Qualifies()) { Sum += (Mod.EvaluatedMagnitude - Bias); } } return Sum; } /** * Evaluates the channel's mods with the specified base value and evaluation parameters * * @param InlineBaseValue Base value to use for the evaluation * @param Parameters Additional evaluation parameters to use * * @return Evaluated value based upon the channel's mods */ float FAggregatorModChannel::EvaluateWithBase(float InlineBaseValue, const FAggregatorEvaluateParameters& Parameters) const { for (const FAggregatorMod& Mod : Mods[EGameplayModOp::Override]) { if (Mod.Qualifies()) { return Mod.EvaluatedMagnitude; } } float Additive = SumMods(Mods[EGameplayModOp::Additive], GameplayEffectUtilities::GetModifierBiasByModifierOp(EGameplayModOp::Additive), Parameters); float Multiplicitive = SumMods(Mods[EGameplayModOp::Multiplicitive], GameplayEffectUtilities::GetModifierBiasByModifierOp(EGameplayModOp::Multiplicitive), Parameters); float Division = SumMods(Mods[EGameplayModOp::Division], GameplayEffectUtilities::GetModifierBiasByModifierOp(EGameplayModOp::Division), Parameters); if (FMath::IsNearlyZero(Division)) { ABILITY_LOG(Warning, TEXT("Division summation was 0.0f in FAggregatorModChannel.")); Division = 1.f; } return ((InlineBaseValue + Additive) * Multiplicitive) / Division; }
-
- Multiply/Divide Modifier는 둘 다 1의 Bias Value를 가지고 있다.
- 1 + (Mod1.Magnitude - 1) + (Mod2.Magnitude - 1) + ...
- 이러한 식은 예상치 못한 결과를 야기한다.
- BaseValue에 Multiply/Divide가 수행되기 전에 모든 Modifier를 더해준다.
- 예를 들어 1.5 Multiply Modifier가 2개가 있을 때 사람들의 예상 값은 1.5 * 1.5 = 2.25이다.
- 하지만 실제 연산은 1 + 0.5 + 0.5 = 2가 된다.
- 설계된대로 사용할 수 있는 값에 대한 기록되지 않는 규칙이 존재한다.
- 첫번째 Modifier 값이 1보다 작고 그 외의 값들이 모두 1 이상 2 미만인 경우
- 첫번째 Modifier 값이 2보다 큰 경우
- 이를 해결하기 위해 FAggregatorModeChannel::EvaluateWithBase를 다음과 같이 수정하는 것을 권장한다.
-
더보기
float FAggregatorModChannel::MultiplyMods(const TArray<FAggregatorMod>& InMods, const FAggregatorEvaluateParameters& Parameters) { float Multiplier = 1.0f; for (const FAggregatorMod& Mod : InMods) { if (Mod.Qualifies()) { Multiplier *= Mod.EvaluatedMagnitude; } } return Multiplier; }
-
- BaseValue에 Multiply/Divide가 수행되기 전에 모든 Modifier를 더해준다.
Gameplay Tags on Modifiers
- Modifier에는 SourceTag와 TargetTag를 지정할 수 있다.
- 이 둘은 GE의 Application Tag Requirements와 동일하게 동작한다.
- 때문에 Tag는 Effect가 적용될 때에만 고려된다.
- Attribute Based Modifier는 SourceTagFilter와 TargeTagFilter를 지정할 수 있다.
- 이는 Modifier의 Source의 규모를 결정할 때,
Filter에서 제공하는 Tag를 모두 가지고 있는 Modifier를 연산에서 배제한다.
- 이는 Modifier의 Source의 규모를 결정할 때,
Stacking
- Default GE는 이전에 생성된 GameplayEffectSpec Instance를 고려하지 않고 새로운 Instance를 등록할 것이다.
- GE는 새로운 Instance를 추가하는 대신, 기존 Instance의 Stack Count를 바꾸는 방식으로 쌓을 수 있다.
- 이 기능은 Infinite, Duration GE에서만 동작한다.
- Stacking은 또한 만료, Duration 갱신, Period 초기화 기능도 제공한다.
Aggregate By Source
- Target의 Source ASC가 각각 1개의 Instance를 가지는 경우
- 각 Source가 동일한 수의 Stack Count를 가질 수 있다.
Aggregate By Target
- Source 무관하게 Target 당 1개의 Instance를 가진다.
- Souce는 공유된 Stack Count를 관리한다.
Tags
- GE는 여러 개의 GameplayTagContainers를 가질 수 있다.
- Designer는 Added/Removed GameplayTagContainer를 수정할 수 있고,
그 결과는 Compile을 거쳐 Combined GameplayTagContainer에 나타난다.
- Designer는 Added/Removed GameplayTagContainer를 수정할 수 있고,
- Added Tag는 이 부모 GE에 없는 것고 이 GE에서 추가된 Tag를 지칭한다.
- Removed Tag는 부모 GE가 가지고 있지만 이 GE가 가지고 있지 않는 Tag를 지칭한다.
Gameplay Effect Asset Tags
- GE가 가지고 있는 Tag
- 아무런 기능적 목적이 없고, GE를 표현하기 위해서만 사용된다.
Granted Tags
- GE에 있지만 GE가 ASC에 등록될 때 전달되기도 하는 Tag
- GE가 제거될 때 ASC에서도 제거된다.
- Duration, Infinite GE에서만 동작한다.
Ongoing Tag Requirements
- 한번만 등록되며 GE가 활성화 되었는지 여부를 알 수 있다.
- GE가 등록되어 있더라도 이 Tag를 비활성화 할 수 있다.
- 만약 GE가 Ongoing Tag Requirements가 실패해 비활성화 되었지만 Requirements가 그 뒤에 도착하면,
GE가 Tag를 다시 활성화 하고 Modifier를 재등록한다. - Duration, Infinite GE에서만 동작한다.
Application Tag Requirements
- GE가 Target에 적용될 수 있는지를 확인하는 Tag
- 이 Requirements가 맞지 않으면 GE가 적용되지 않는다.
Remove Gameplay Effects with Tags
- Asset Tag나 Granted Tag에서 이 Tag에 등록된 Tag는 GE가 적용될 때 Target으로부터 제거된다.
Immunity
- 다른 GE의 동작을 효과적으로 막기 위해 GE는 GameplayTag를 이용해 Immunity를 부여할 수 있다.
- Application Tag Requirements와 같이 다른 수단을 통햐 Immunity를 효과적으로 부여할 수 있기도 하지만,
이 System을 사용하면 Immunity로 GE가 차단될 때 delegate를 받을 수 있습니다.-
더보기
/** Notifies when GameplayEffectSpec is blocked by an ActiveGameplayEffect due to immunity */ DECLARE_MULTICAST_DELEGATE_TwoParams(FImmunityBlockGE, const FGameplayEffectSpec& /*BlockedSpec*/, const FActiveGameplayEffect* /*ImmunityGameplayEffect*/); /** Immunity notification support */ FImmunityBlockGE OnImmunityBlockGameplayEffectDelegate;
-
GarantedApplicationImmunityTags
- 5.3 버전에서 Deprecated 됨
- UImmunityGameplayEffectComponent를 대신 사용
GrantedApplicationImmunityQuery
- 5.3 버전에서 Deprecated 됨
- UImmunityGameplayEffectComponent를 대신 사용
GameplayEffectSpec
- GE의 인스턴스화로 취급할 수 있다.(이하 GESpec)
- 자신이 가르키는 GE의 Reference 만들어진 Level, 만든 주체에 대한 정보를 내포하고 있다.
- 이 정보들은 GE와 다르게 Runtime상에서 적용되기 전까지 자유롭게 생성/수정이 가능하다.
- GE가 적용 될 때 GESpec이 GE로부터 생성되고 실질적으로 GESpec이 Target에 적용된다.
- GESpec은 UASC::MakeOutgoingSpec()함수를 통해 GE로부터 생성된다.
-
더보기
/** Get an outgoing GameplayEffectSpec that is ready to be applied to other things. */ UFUNCTION(BlueprintCallable, Category = GameplayEffects) virtual FGameplayEffectSpecHandle MakeOutgoingSpec(TSubclassOf<UGameplayEffect> GameplayEffectClass, float Level, FGameplayEffectContextHandle Context) const;
-
- GESpec은 그 즉시 적용될 필요가 없다.
- 보통은 투사체에 GESpec을 전달하고 충돌 발생 시 연산에 사용하곤 한다.
- GESpec이 성공적으로 적용되면 FActiveGameplayEffect를 반환한다.
- GESpec에는 아래 내용들이 제공된다.
- GESpec을 만드는데 사용된 GE 클래스
- GESpec의 Level
- 보통 GESpec을 만드는데 사용한 Ability와 같지만 다를수도 있음
- Duration
- Default는 GE의 Duration이지만 값이 다를 수도 있다.
- Period
- Default는 GE의 Period이지만 다를 수도 있다.
- 현재 GESpec의 Stack Count
- 상한은 GE의 설정값을 따른다.
- GameplayEffectContextHandle
- 이를 통해 누가 이 GeSpec을 만들었는지 인지할 수 있다.
- GESpec이 생성 되었을 때 Snapshotting으로 인해 Capture되는 Attribute 들
- DynamicGrantedTag
- GESpec이 부여 될 때 GameplayTag에 GE가 추가로 부여해야 하는 GameplayEffectSpec
- DynamicAssetTag
- GESpec이 GE와 비교해 추가로 가져야 하는 Tag들
- SetByCaller
SetByCallers
- GESpec으로 하여금 GameplayTag나 FName과 관련된 float 값을 전달할 수 있도록 한다.
- 이들은 GESpec에 TMap 형태로 저장된다.
- 이 값들은 GE의 Modifier에 사용되거나, 일반적인 의미의 Ferring Floats로 사용될 수 있다.
Modifiers에서 사용
- GE class보다 앞서 정의되어야만 한다.
- GameTag 버전만 사용 가능하다.
- GE class에 하나가 정의 되었는데 GESpec이 대응하는 Tag-float pair가 없는 경우,
GESpec에서 Runtime Error가 발생하며 0을 반환한다. - Divide Operation에서 문제를 야기할 수 있다.
그 외에서 사용
- 어느 class보다도 앞서 정의 될 필요가 없다.
- GESpec에 존재하지 않는 SetByCaller를 읽더라도 사용자로 하여금 Default 값과 warning을 반환할 수 있다.
관련 함수
- 보통 Ability 안에서 생성된 숫자 데이터들은 SetByCallers을 통해
GameplayEffectExecutionCalculations나 ModifierMagnitudeCalculations로 전달하는 것이 일반적이다. - SetByCaller를 BP에서 읽을 수 있으려면 BPLibrary에 Custom Node를 만들어 줘야 한다.
- GESpec은 SetByCaller 값과 관련된 아래 함수들을 제공하고 있다.
-
더보기
/** Sets the magnitude of a SetByCaller modifier */ void SetSetByCallerMagnitude(FName DataName, float Magnitude); /** Sets the magnitude of a SetByCaller modifier */ void SetSetByCallerMagnitude(FGameplayTag DataTag, float Magnitude); /** Returns the magnitude of a SetByCaller modifier. Will return 0.f and Warn if the magnitude has not been set. */ float GetSetByCallerMagnitude(FName DataName, bool WarnIfNotFound = true, float DefaultIfNotFound = 0.f) const; /** Returns the magnitude of a SetByCaller modifier. Will return 0.f and Warn if the magnitude has not been set. */ float GetSetByCallerMagnitude(FGameplayTag DataTag, bool WarnIfNotFound = true, float DefaultIfNotFound = 0.f) const;
- 이 중 FName을 쓰는 것보다 GameplayTag를 사용하는 것을 권장한다.
-
GameplayEffectContext
- GEContext는 GESpec의 Instigator와 TargetData를 가지고 있다.
- 이는 Subclass를 통해 임시 데이터를 다른 곳으로 전달하는 좋은 구조이다.
- ModifierMagnitudeCalculations, GameplayEffectExecutionCalculations, AS, GameplayCues 등
사용법
- FGameplayEffectContext를 상속
- GetScriptStruct(), Duplicate(), NetSerialize() 함수들을 Override
- 상위 Class(FGameplayEffectContext)처럼 하위 Class의 TStructOpsTypeTraits를 구현
- AbilitySystemGlobals::AllocGameplayEffectContext() 함수에서 새로 만든 Class를 반환하도록 override .
ModifierMagnitudeCalculation
- GE에서 Modifier와 같이 매우 강력한 Class(이하 MMC)
- 기능은 GameplayEffectExecution Calculations와 비슷하나 덜 강력하고, Predict 가능하다.
- 유일한 목적은 CalculateBaseMagnitude_Implementation() 함수를 통해 float value를 반환하는 것이다.
- BP와 C++ 모두에게서 상속 받아 override 할 수 있다.
- Instant, Duration, Infinite, Periodic GE 모두에게서 사용할 수 있다.
- MMC의 강점은 GESpec으로부터 GameplayTag나 SetByCaller를 읽을 수 있는 권한을 통해
Source의 Attribute나 GE의 Target을 캡쳐 할 수 있다는 점이다. - 또한 Attribute는 스냅샷이 될 수도 있다.
- 스냅샷이 되는 Attribute는 GESpec이 생성될 때 캡쳐가 된다.
- 스냅샷이 되지 않는 Attribute는 Infinite, Duration GE로 인해 자동으로 갱신되어 GESpec이 적용될 때 캡쳐된다.
- Attribute를 캡쳐하는 것은 ASC에 존재한 mod로부터 CurrentValue를 재계산 한다.
- 이 재계산은 AbilitySet의 PreAttributeChange() 함수를 호출하지 않기 때문에
그 과정 속에서 Clamping이 이루어져야 한다.
- 이 재계산은 AbilitySet의 PreAttributeChange() 함수를 호출하지 않기 때문에
- 만약 Duration, Infinite GE에서 Modifier를 다시 계산해야 한다면, SetActiveGameplayEffectLevel 함수를 사용한다.
-
더보기
void SetActiveGameplayEffectLevel(FActiveGameplayEffectHandle ActiveHandle, int32 NewLevel);
-
- MMC의 결과값은 계수와 계수 전후 추가 값으로 인해 GE의 Modifier에서 수정될 수 있다.
GameplayEffectExecutionCalculation
- GE로 하여금 ASC에 변화를 가할 수 있는 가장 강력한 방법(이하 ExeCalc)
- MMC와 같이 Attribute를 캡쳐하고 선택적으로 스냅샷 할 수 있다.
- MMC와 다르게 1개 이상의 Attribute를 바꿀 수 있고, 무엇보다 Programmer가 원하는 것을 추가할 수 있다.
- 단점으로는 Predict 할 수 없고 반드시 C++로만 작업이 되어야 한다는 점이다.
- Instant, Periodic GE에서만 사용할 수 있다.
- 보통 "Execute"가 붙은 것들은 대부분 Instant, Periodic GE와 관련되어 있다.
- 스냅샷은 GE가 생성될 때 Attribute를 캡쳐한다.
- 스냅샷을 하지 않는 것들은 GE가 적용 될 때 Attribute를 캡쳐한다.
- Attribute를 캡쳐하는 것은 ASC의 mod에 있는 Attribute의 CurrentValue를 재계산한다.
- 이 재계산은 AbilitySet의 PreAttributeChange 함수를 호출하지 않는다.
- 때문에 이 과정에서 Clamping이 반드시 이루어져야 한다.
- Attribute 캡쳐를 설정하기 위해 우리는 Epic의 ActionRPG Sample Project에서 설정한 패턴을 따른다.
- 이는 Attribute를 저장하고 캡처 방식을 정의하는 구조체를 정의하고, 생성자에서 그 복사본을 생성한다.
- 또한 각 ExecCalc당 하나씩 있다.
- 각 구조체는 같은 namespace를 공유하려면 유니크한 이름이 필요하다.
- 같은 이름의 구조체를 사용하는 것은 Attribute Capture에 있어 모호함을 발생한다.
- Local Predicted, Server Only, Server Initiated GameplayAbility에서 ExecCalc는 서버에서만 호출된다.
- Source와 Target의 여러 Attribute로부터 복잡한 연산을 통해 Damage를 계산하는 것이
가장 대표적인 ExecCalc의 예시이다.
ExeCalc에 Data 보내기
- Attribute Capture에 더불어 ExeCalc에 정보를 전달하는 몇 가지 방법이 있다.
SetByCaller
- GESpec에 설정된 SetByCaller는 ExeCalc가 직접 읽을 수 있디ㅏ.
-
더보기
const FGameplayEffectSpec& Spec = ExecutionParams.GetOwningSpec(); float Damage = FMath::Max<float>(Spec.GetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Damage")), false, -1.0f), 0.0f);
Attribute Calculation Modifier 데이터 백업
- 만약 값을 GE에 하드코딩 하고 싶다면,
백업 데이터로 캡쳐한 Attribute 중 하나를 CalculationModifier를 통해 전달할 수 있다.
Temporary Variable Calculation Modifier 데이터 백업
- 만약 값을 GE에 하드코딩 하고 싶다면,
C++에서 호출되는 Temporary Variable이나 Transient Aggregator를 CalculationModifier를 통해 전달할 수 있다. - Temporary Variable은 GameplayTag와 연관이 있다.
Gameplay Effect Context
- GESpec의 Custom GameplayEffectContext를 통해 ExecutionCalculation으로 데이터를 보낼 수 있다.
CustomApplicationRequirement
- Designer로 하여금 GE의 적용여부를 통제할 수 있는 고급 기능을 제공한다.(이하 CAR)
- 단순히 GameplayTag 검사보다 GE에 대해 더 복잡한 조건을 설정할 수 있게 한다.
- C++에서는 CanApplyGameplayEffect_Implementation(), BP에서는 CanApplyGameplayEffect를 override 할 수 있다.
- 또한 CAR는 좀 더 확장된 기능을 작업할 수 있다.
- GE Instance가 이미 Target에 있는지 여부 확인
- 새 Instance를 등록하지 않고 기존 Instance의 Duration을 변경
Executions
더보기
/**
* Struct representing the definition of a custom execution for a gameplay effect.
* Custom executions run special logic from an outside class each time the gameplay effect executes.
*/
USTRUCT(BlueprintType)
struct GAMEPLAYABILITIES_API FGameplayEffectExecutionDefinition
{
GENERATED_USTRUCT_BODY()
/**
* Gathers and populates the specified array with the capture definitions that the execution would like in order
* to perform its custom calculation. Up to the individual execution calculation to handle if some of them are missing
* or not.
*
* @param OutCaptureDefs [OUT] Capture definitions requested by the execution
*/
void GetAttributeCaptureDefinitions(OUT TArray<FGameplayEffectAttributeCaptureDefinition>& OutCaptureDefs) const;
/** Custom execution calculation class to run when the gameplay effect executes */
UPROPERTY(EditDefaultsOnly, Category=Execution)
TSubclassOf<UGameplayEffectExecutionCalculation> CalculationClass;
/** These tags are passed into the execution as is, and may be used to do conditional logic */
UPROPERTY(EditDefaultsOnly, Category = Execution)
FGameplayTagContainer PassedInTags;
/** Modifiers that are applied "in place" during the execution calculation */
UPROPERTY(EditDefaultsOnly, Category = Execution)
TArray<FGameplayEffectExecutionScopedModifierInfo> CalculationModifiers;
/** Other Gameplay Effects that will be applied to the target of this execution if the execution is successful. Note if no execution class is selected, these will always apply. */
UPROPERTY(EditDefaultsOnly, Category = Execution)
TArray<FConditionalGameplayEffect> ConditionalGameplayEffects;
};
/** Array of executions that will affect the target of this effect */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = GameplayEffect)
TArray<FGameplayEffectExecutionDefinition> Executions;
- Modifier에서 지원하는 정보 이상의 것이 필요할 때에는 사용
- UGameplayEffectExecutionCalculation을 사용하면 GE의 Custom Behaviour를 정의
- Modifier로 커버할 수 없는 복잡한 식을 정의할 때 유용
Application Requirements
- GE를 적용하는 조건을 처리하는 부분
- 5.3 버전 기준으로 Deprecated
- UCustomCanApplyGameplayEffectComponent를 대신 사용
Granted Abilities
- GE를 통해 부여하는 Tag 혹은 Attribute
- 5.3 버전 기준으로 Deprecated
- GetBlockedAbilityTags 함수를 통해 UTargetTagsGameplayEffectComponent를 대신 사용
Overflow Effects
/** Effects to apply when a stacking effect "overflows" its stack count through another attempted application. Added whether the overflow application succeeds or not. */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Stacking|Overflow", meta = (EditConditionHides, EditCondition = "StackingType != EGameplayEffectStackingType::None"))
TArray<TSubclassOf<UGameplayEffect>> OverflowEffects;
- 이미 적용된 GE가 다시 적용되는 Overflow 상황을 처리하는 정책
- 이미 적용되어 있는 GE가 포화상태가 되면 새로운 GE가 적용되는 것을 지칭
- System상 다양한 Behaviour를 제공
- 한계치를 넘을 때까지 중첩
- 새로 적용될 때마다 늘어나는 Stack 수를 최대 한도까지 유지
- 시간이 지나명 GE의 시간이 리셋 또는 추가
- 단순한 GE의 여러 Instance에 개별 Timer를 독립적으로 적용
Gameplay Cue Display
더보기
/**
* FGameplayEffectCue
* This is a cosmetic cue that can be tied to a UGameplayEffect.
* This is essentially a GameplayTag + a Min/Max level range that is used to map the level of a GameplayEffect to a normalized value used by the GameplayCue system.
*/
USTRUCT(BlueprintType)
struct FGameplayEffectCue
{
GENERATED_USTRUCT_BODY()
FGameplayEffectCue()
: MinLevel(0.f)
, MaxLevel(0.f)
{
}
FGameplayEffectCue(const FGameplayTag& InTag, float InMinLevel, float InMaxLevel)
: MinLevel(InMinLevel)
, MaxLevel(InMaxLevel)
{
GameplayCueTags.AddTag(InTag);
}
/** The attribute to use as the source for cue magnitude. If none use level */
UPROPERTY(EditDefaultsOnly, Category = GameplayCue)
FGameplayAttribute MagnitudeAttribute;
/** The minimum level that this Cue supports */
UPROPERTY(EditDefaultsOnly, Category = GameplayCue)
float MinLevel;
/** The maximum level that this Cue supports */
UPROPERTY(EditDefaultsOnly, Category = GameplayCue)
float MaxLevel;
/** Tags passed to the gameplay cue handler when this cue is activated */
UPROPERTY(EditDefaultsOnly, Category = GameplayCue, meta = (Categories="GameplayCue"))
FGameplayTagContainer GameplayCueTags;
float NormalizeLevel(float InLevel)
{
float Range = MaxLevel - MinLevel;
if (Range <= KINDA_SMALL_NUMBER)
{
return 1.f;
}
return FMath::Clamp((InLevel - MinLevel) / Range, 0.f, 1.0f);
}
};
/** Cues to trigger non-simulated reactions in response to this GameplayEffect such as sounds, particle effects, etc */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "GameplayCues")
TArray<FGameplayEffectCue> GameplayCues;
- Particle이나 Sound 같은 Decoration Effect를 관리하는 효율적인 Network 방식
- GAS로 제어할 수 있다.
- GA나 GE로 트리거 할 수 있다.
- 모든 Gameplay Cue에는 GameplayCue로 시작하는 Gameplay Tag를 붙여야 한다.
- Native C++ 혹은 BP로 Override할 수 있고 4개 함수로 작동한다.
-
더보기
/** Called when a GameplayCue is executed, this is used for instant effects or periodic ticks */ UFUNCTION(BlueprintNativeEvent, Category = "GameplayCueNotify") bool OnExecute(AActor* MyTarget, const FGameplayCueParameters& Parameters); /** Called when a GameplayCue with duration is first activated, this will only be called if the client witnessed the activation */ UFUNCTION(BlueprintNativeEvent, Category = "GameplayCueNotify") bool OnActive(AActor* MyTarget, const FGameplayCueParameters& Parameters); /** Called when a GameplayCue with duration is first seen as active, even if it wasn't actually just applied (Join in progress, etc) */ UFUNCTION(BlueprintNativeEvent, Category = "GameplayCueNotify") bool WhileActive(AActor* MyTarget, const FGameplayCueParameters& Parameters); /** Called when a GameplayCue with duration is removed */ UFUNCTION(BlueprintNativeEvent, Category = "GameplayCueNotify") bool OnRemove(AActor* MyTarget, const FGameplayCueParameters& Parameters);
-
GA 와의 Interaction
- GE가 GA와 상호작용할 때에 AS에서 제공하는 함수를 이용한다.
- 하지만 이는 기본적인 기능만 제공하고,
Attribute의 값 변경에 대한 추가 작업이나 범위 지정을 하려면 AS의 함수를 Override 해야 한다.
- 하지만 이는 기본적인 기능만 제공하고,
- 더보기
/** * Called just before modifying the value of an attribute. AttributeSet can make additional modifications here. Return true to continue, or false to throw out the modification. * Note this is only called during an 'execute'. E.g., a modification to the 'base value' of an attribute. It is not called during an application of a GameplayEffect, such as a 5 ssecond +10 movement speed buff. */ virtual bool PreGameplayEffectExecute(struct FGameplayEffectModCallbackData &Data) { return true; } /** * Called just after a GameplayEffect is executed to modify the base value of an attribute. No more changes can be made. * Note this is only called during an 'execute'. E.g., a modification to the 'base value' of an attribute. It is not called during an application of a GameplayEffect, such as a 5 ssecond +10 movement speed buff. */ virtual void PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData &Data) { } /** * An "On Aggregator Change" type of event could go here, and that could be called when active gameplay effects are added or removed to an attribute aggregator. * It is difficult to give all the information in these cases though - aggregators can change for many reasons: being added, being removed, being modified, having a modifier change, immunity, stacking rules, etc. */ /** * Called just before any modification happens to an attribute. This is lower level than PreAttributeModify/PostAttribute modify. * There is no additional context provided here since anything can trigger this. Executed effects, duration based effects, effects being removed, immunity being applied, stacking rules changing, etc. * This function is meant to enforce things like "Health = Clamp(Health, 0, MaxHealth)" and NOT things like "trigger this extra thing if damage is applied, etc". * * NewValue is a mutable reference so you are able to clamp the newly applied value as well. */ virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) { } /** Called just after any modification happens to an attribute. */ virtual void PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue) { } /** * This is called just before any modification happens to an attribute's base value when an attribute aggregator exists. * This function should enforce clamping (presuming you wish to clamp the base value along with the final value in PreAttributeChange) * This function should NOT invoke gameplay related events or callbacks. Do those in PreAttributeChange() which will be called prior to the * final value of the attribute actually changing. */ virtual void PreAttributeBaseChange(const FGameplayAttribute& Attribute, float& NewValue) const { } /** Called just after any modification happens to an attribute's base value when an attribute aggregator exists. */ virtual void PostAttributeBaseChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue) const { }
'UE5 > GAS' 카테고리의 다른 글
[GAS] Gameplay Ability (0) | 2024.05.17 |
---|---|
[GAS] Ability Task (0) | 2024.05.16 |
[GAS] Attribute Set (1) | 2024.05.14 |
[GAS] Gameplay Attribute (0) | 2024.05.13 |
[GAS] Ability System Component (0) | 2024.05.13 |