https://dev.epicgames.com/documentation/ko-kr/unreal-engine/gameplay-ability-tasks-in-unreal-engine?application_version=5.3

 

언리얼 엔진의 게임플레이 어빌리티 태스크 | 언리얼 엔진 5.4 문서 | Epic Developer Community

어빌리티 태스크 클래스의 개요입니다.

dev.epicgames.com

https://github.com/tranek/GASDocumentation?tab=readme-ov-file#concepts-at

 

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

  • 일반적인 Gameplay Task Class의 특수한 형태로 GA와 함께 작동하도록 되어있다.
  • GAS를 사용하는 게임에는 보통 고유한 Gameplay 기능을 구현하는 다양한 Custom Ability Task가 포함되어 있다.
  • 이러한 Task는 GA 실행 중 비동기로 수행하고, Native C++에서 Delegate를 호출하거나
    BP에서 하나 이상의 출력 실행 핀을 이동하여 실행 흐름에 영향을 미칠 수 있다.
    • 이를 통해 Ability가 여러 Frame에서 실행되기도 하고, 동일 Frame에 여러 개의 다른 기능을 수행할 수 있다.
  • 대부분의 Ability Task에는 즉시 발동하는 실행 핀이 있다.
    • 때문에 TAsk 시작 후에도 BP가 계속 실행 된다.
  • 이 외에 Delay 후에 발동되거나, 발생하지 않을 수도 있는 조건부 이벤트에 뒤따라 발동되는 Task별 핀이 있기도 하다.
  • Ability Task를 종료시킬 수 있는 방법은 2가지이다.
    • EndTask 함수를 호출하여 자체적으로 종료
    • 해당 Task를 실행한 Gameplay Ability가 끝날 때 자동 종료하도록 대기
  • 이는 Phantom Ability Task가 실행되어 CPU 및 메모리 누수가 나는 것을 방지합니다.
  • Ability Task는 Network 여부와 상관 없이 모두 사용하도록 설계되었지만 ,직접 자체적으로 업데이트 하지 않는다.
    • 일반적으로 Ability Task는 Replicate되는 GA에서 생성된다.
    • 하지만 실행 흐름을 결정하기 위해 Input이나 Network Variable 등 Replicate 된 정보를 사용한다.
    • 때문에 간접적으로 동기화 상태를 유지한다.

Definition

  • GA는 오직 한 Frame에서 동작하고 이 이상의 유연성을 제공하지 않는다.
  • 여러 Frame에서 동작 하거나 시간이 지난 후 특정 시점에 Delegate를 받으려면 Gmaplay Task를 써야 한다.
  • AbilityTask는 일반적인 Gameplay Task의 특수한 형태로 다음 기능들을 제공한다.
    • RootMotionSource로 움직이는 Character에 대한 Task
    • Animation Montage를 재생시키는 Task
    • Attribute 변화에 대한 반응 Task
    • GE 변화에 대한 반응 Task
    • Player Input에 대한 반응 Task
  • UAbilityTask 생성자는 동시에 실행될 수 있는 AbilityTask를 게임 전체에서 최대 1000개로 제한한다.
    • 이는 캐릭터가 수 백개가 나오는 RTS 게임 설계에서 유념해둬야 한다.

Custom Ability Task

  • Custom Ability Task 생성은 매우 빈번할 것이다.
  • AbilityTask는 다음으로 구성되어 있다.
    • 새 AbilityTask Instance를 생성하는 static function
    • AbilityTask가 동작을 완수 했을 때 발생하는 delegate
    • Activate() 함수
      • 주요 작업을 시행하고 외부 delegate에 bind
    • OnDestroy()
      • 외부 delegate를 포함한 모든 clean up
    • 외부 Delegate에 Bind 되는 Callback 함수
    • 멤버 변수와 다른 내부 함수
  • AbilityTask는 오직 하나의 Output Delegate를 선언할 수 있다.
    • 모든 Output Delegate 는 파라미터가 있든 없든 이 타입이어야 한다.
    • 파라미터를 사용 안 하면 Default Value가 전달된다.
  • Ability Task는 오직 이를 소지한 GameplayAbility의 서버/클라이언트에서만 동작한다.
    • 하지만 AbilityTask::InitSimulatedTask 함수를 override하여 그 안에서 bSimulatedTask 플래그와
      다른 Replicate 되는 멤버변수를 설정하여 Simulated Client에서 동작하도록 할 수 있다.
    • 이는 모든 Movement AbilityTask를 replicate 하고 싶지 않지만 simulate는 하고 싶을 때 유용하다.
  • 모든 RootMotionSource AbilityTask는 이것을 사용한다.
  • 또한 AbilityTask::TickTask를 override하여 bTickTask 플래그를 활성화 하면 Tick을 사용할 수 있다.
    • 이는 프레임에 따라 값이 부드럽게 바뀌는 경우에 유용하다.

Using Ability Task

C++

더보기
UGDAT_PlayMontageAndWaitForEvent* Task = UGDAT_PlayMontageAndWaitForEvent::PlayMontageAndWaitForEvent(this, NAME_None, MontageToPlay, FGameplayTagContainer(), 1.0f, NAME_None, false, 1.0f);
Task->OnBlendOut.AddDynamic(this, &UGDGA_FireGun::OnCompleted);
Task->OnCompleted.AddDynamic(this, &UGDGA_FireGun::OnCompleted);
Task->OnInterrupted.AddDynamic(this, &UGDGA_FireGun::OnCancelled);
Task->OnCancelled.AddDynamic(this, &UGDGA_FireGun::OnCancelled);
Task->EventReceived.AddDynamic(this, &UGDGA_FireGun::EventReceived);
Task->ReadyForActivation();

BP

  • BP에서는 ReadyForActivation()을 호출할 필요가 없다.
    • K2Node_LatentGameplayTaskCall에서 자동으로 호출해준다.
    • 해당 AbilityTask에 있는 경우 BeginSpawningActor(), FinishSpawningActor()도 자동으로 호출해준다.

Root Motion Source Ability Task

  • GAS에서 캐릭터를 이동시키는 AbilityTask는 CharacterMovementComponent에
    후킹 된 Root Motion Source를 이용한다.

'UE5 > GAS' 카테고리의 다른 글

[GAS] Gameplay Cue  (0) 2024.05.21
[GAS] Gameplay Ability  (0) 2024.05.17
[GAS] Gameplay Effect  (0) 2024.05.15
[GAS] Attribute Set  (1) 2024.05.14
[GAS] Gameplay Attribute  (0) 2024.05.13

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/gameplay-effects-for-the-gameplay-ability-system-in-unreal-engine?application_version=5.3

 

언리얼 엔진의 게임플레이 어빌리티 시스템용 게임플레이 어트리뷰트 및 게임플레이 이펙트 |

게임플레이 어빌리티 시스템 내 어트리뷰트 및 이펙트의 개요입니다.

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에 결과 값을 덮어씌운다.
  • 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;
        }

Gameplay Tags on Modifiers

  • Modifier에는 SourceTag와 TargetTag를 지정할 수 있다.
    • 이 둘은 GE의 Application Tag Requirements와 동일하게 동작한다.
    • 때문에 Tag는 Effect가 적용될 때에만 고려된다.
  • Attribute Based Modifier는 SourceTagFilter와 TargeTagFilter를 지정할 수 있다.
    • 이는 Modifier의 Source의 규모를 결정할 때,
      Filter에서 제공하는 Tag를 모두 가지고 있는  Modifier를 연산에서 배제한다.

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에 나타난다.
  • 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이 이루어져야 한다.
  • 만약 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

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/gameplay-attributes-and-attribute-sets-for-the-gameplay-ability-system-in-unreal-engine?application_version=5.3

 

언리얼 엔진의 게임플레이 어빌리티 시스템을 위한 게임플레이 어트리뷰트 및 어트리뷰트 세트

게임플레이 어트리뷰트 및 어트리뷰트 세트 사용하기

dev.epicgames.com

https://github.com/tranek/GASDocumentation?tab=readme-ov-file#concepts-as

 

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

Definition

  • 하나 이상의 GA로 AS를 구성하고, ASC에 등록을 한다.
    • Owner Actor의 Constructor에서 AttributeSet을 생성하면 ASC에 자동으로 등록이 된다.

Design

  • ASC가 여러 개의 AS를 가질 수는 있지만, 각 AS는 모두 서로 클래스가 달라야 한다.
  • AS는 무시할만한 Memory Overhead를 야기한다.
    • 그렇기에 얼마나 AS를 많이 사용할지는 개발자의 결정에 달려있다.
  • 또한 AS는 Actor가 어떤 Attribute를 가질지 선택지를 제공하는 의미로 SubClass를 생성할 수 있다.
    • Atribute는 내부에서 AttributeSetClassName.AttributeName으로 일컬어진다.
    • 때문에 AS를 상속 받더라도, 상위 AS의 Attribute는 상위 AS의 이름을 그대로 유지한다.

Subcomponents with Individual Attributes

  • 장비 파괴처럼 복수의 Damagable Component를 소유했을 때, Component의 최대 갯수를 알 수 있다면
    AttributeSet에 각 Component에 대응하는 health Attribute를 만드는 것을 권장한다.
    • Damagable Component에는 가상의 Slot 값을 부여
    • 부여된 Slot값을 통해 GameplayAbilities를 읽거나 Attribute에 접근해 데미지 계산이 가능
    • Character/Pawn은 1개 이하의 health Attribute만 소유하는 것을 권장
    • Attribute는 AttributeSet이 가져야 하는거지, Character/Pawn이 가져야만 하는 것이 아니기 때문.
  • 하지만 다음 조건에서는 위와 같은 방식이 기대한 것만큼 잘 동작하지 않는다.
    • Subcomponent가 Runtime 상 상한이 없는 경우
    • Subcomponent가 Detach되어 다른 유저가 사용할 수 있는 경우
  • 이 때에는 Attribute를 사용하지 말고 고전적인 float 타입의 변수를 Component에 직접 부여하는 것을 권장한다.
  • 자세한 사항은 Item Attribute를 확인하길 바란다.

Add/Remove AttributeSet at Runtime

  • AS은 Runtime 상에 ASC에 추가될 수 있다.
    • 물론 ASC로부터 제거도 가능하지만 이는 매우 위험하다.
    • 만약 client에서 server보다 먼저 AS를 제거하고 Attribute 값 변화가 client로 replicate 된다면, 
      Attribute를 찾을 수 없어 Crash가 난다.
  • 그렇기에 Runtime 상에서 AS를 추가/제거를 할 때에는 대상 ASC에 대해 ForceReplication 함수를 호출해줘야 한다.

Item Attribute

  • 장비류 아이템에 대한 Attribute를 구현하는 방식.
    • 이런 방식은 전부 Item에 직접 값을 저장한다.
  • 이 시스템은 1명 이상의 플레이어가 Lifetime동안 장비를 장착/탈착 할 수 있는 경우에 반드시 제공되어야 한다.

Plain Floats on the Item

  • Attribute 대신 float 변수로 정보를 관리하는 방식
    • Fortnite와 GAS 예시코드가 이런 방식을 채택
  • 총기가 최대 Ammo 수, 현재 Ammo 수, 남은 Ammo 수 등을 저장하고 Client에 Replicate한다.
  • 만약 총기가 남은 Ammo를 공유하며느 그 값을 Character의 Attribute로 가져와 AttributeSet에 저장한다.
  • 다만 총기에서 Attribute를 사용하지 않기 때문에, UGameplayAbility에서 제공하는 일부 함수를 override해야 한다.
    • 총기에서 사용하는 Plain Float에 대한 검증과 계산을 위해
  • 총기를 GameplayAbilitySpec의 SourceObject로 지정하면 접근 권한을 Ability 내에서 처리할 수 있다.
  • 자동사격 과정에서 총기의 Ammo가 Replicate로 인해 덮어씌어지는 Clobbering을 방지하기 위해,
    PreReplication() 함수에서 IsFiring GameplayTag가 있는 동안 Ammo의 Replicate를 비활성화 한다.
    • 더보기
      void AGSWeapon::PreReplication(IRepChangedPropertyTracker& ChangedPropertyTracker)
      {
      	Super::PreReplication(ChangedPropertyTracker);
      
      	DOREPLIFETIME_ACTIVE_OVERRIDE(AGSWeapon, PrimaryClipAmmo, (IsValid(AbilitySystemComponent) && !AbilitySystemComponent->HasMatchingGameplayTag(WeaponIsFiringTag)));
      	DOREPLIFETIME_ACTIVE_OVERRIDE(AGSWeapon, SecondaryClipAmmo, (IsValid(AbilitySystemComponent) && !AbilitySystemComponent->HasMatchingGameplayTag(WeaponIsFiringTag)));
      }
    • 장점
      • AttributeSet을 사용하면서 발생하는 한계를 피할 수 있음
    • 단점
      • 현존하는 GameplayEffects의 흐름을 사용할 수 없음.
      • UGameplayAbility의 핵심 함수를 ammo 관리를 위해 override 해야 함.

AttributeSet on the Item

  • Item에서 AttributeSet을 사용하고 Player의 ASC를 이용하는 방법
  • 하지만 근본적인 한계가 존재한다.
    • Weapon이 가지고 있는 고유한 Attribute를 경우에 따라 Character로 옮겨와야 한다.
    • 만약 Weapon을 Inventory에 넣는다면,
      Weapon의 AttributeSet을 Character가 가진 ASC의 SpawnedAttributes에 옮겨와야 한다.
  • 만약 AttributeSet이 OwnerActor가 아닌 곳에 존재하면 Compile Error가 발생한다.
  • 이를 수정하기 위해 다음 작업이 진행되어야 한다.
    • AttributeSet을 Constructor가 아닌 BeginPlay()에서 Construct
    • IAbilitySystemInterface를 Implement
  • 장점
    • 제공되는 GameplayAbility와 GameplayEffect를 그대로 사용 가능
    • Item에 간단한 세팅으로 기능 구현 가능
  • 단점
    • 각 무기 타입에 맞는 AttributeSet을 생성해야 한다.
      • ASC는 기능적으로 Class당 하나의 AttributeSet을 가질 수 밖에 없다.
      • ASC가 Attribute 변경사항을 적용할 때 SpawnedAttributes에서 조건에 맞는 첫번째 AS만 탐색하기 때문.
      • 나머지 AttributeSet은 항상 무시 됨
    • 위와 같은 이유로 플레이어 당 동일한 무기를 2개 이상 소지할 수 없음
    • AttributeSet을 Runtime상에서 제거하는 것에서 오는 근본적인 리스크
      • 예를 들어 탄속이 있는 무기로 적을 처치 했는데 발사와 처치 사이에 무기가 제거가 되면
        관련 AttributeSet을 찾지 못해 Crash가 발생한다.

ASC on the Item

  • ASC를 통째로 아이템에 박아 넣는 극단적인 방법.
  • 장점
    • GameplayAbility와 GameplayEffect의 모든 기능을 오롯이 사용 가능
    • Attributeset Class를 재사용 할 수 있음
      • ASC에서 1개만 존재하니까
  • 단점
    • 개발 비용이 까마득하고 검증되지 않음

Defining Attributes

  • 더보기
     UCLASS()
     class MYPROJECT_API UMyAttributeSet : public UAttributeSet
     {
         GENERATED_BODY()
     
         public:
         /** 퍼블릭 액세스 가능한 샘플 어트리뷰트 'Health'*/
         UPROPERTY(EditAnywhere, BlueprintReadOnly)
         FGameplayAttributeData Health;
     };

     

    class MyAbilitySystemComponent : public UAbilitySystemComponent
    {
    	....
    	/** 샘플 어트리뷰트 세트. */
    	UPROPERTY()
    	const UMyAttributeSet* AttributeSet;
    	....
    }
     // 이런 코드는 일반적으로 BeginPlay()에 나타나지만,
     // 적절한 어빌리티 시스템 컴포넌트 구하기일 수 있습니다. 다른 액터에 있을 수도 있으므로 GetAbilitySystemComponent를 사용하여 결과가 유효한지 확인하세요.
     AbilitySystemComponent* ASC = GetAbilitySystemComponent();
     // AbilitySystemComponent가 유효한지 확인합니다. 실패를 허용할 수 없다면 if() 조건문을 check() 구문으로 대체합니다.
     if (IsValid(ASC))
     {
         // 어빌리티 시스템 컴포넌트에서 UMyAttributeSet를 구합니다. 필요한 경우 어빌리티 시스템 컴포넌트가 UMyAttributeSet를 생성하고 등록할 것입니다.
         AttributeSet = ASC->GetSet<UMyAttributeSet>();
     
         // 이제 새 UMyAttributeSet에 대한 포인터가 생겨 나중에 사용할 수 있습니다. 초기화 함수가 있는 경우 여기서 호출하면 좋습니다.
     }


    1.  Attribute는 FGameplayAttributeData 타입으로 Attribute를 선언한다.
    2. 위 코드에서는 Attribute Set 안의 값이 코드에서 직접 수정하지 않도록 const로 선언을 정의한다.
    3. AttributeSet은 Actor Construct에서 Instance화 하는 시점에 함수가 유효한 ASC를 반환하는 한,
      이 시점 혹은 BeginPlay 도중에 자동으로 등록된다.
    4. BP를 편집하여 Attribute Set Type을 ASC의 Default Start Data로 추가할 수 있다.
    5. ASC가 없는 GA를 수정하는 Gameplay Effect가 적용된다면, ASC는 일치하는 GA를 자동으로 생성한다.
      1. 하지만 이 Methond는 AS를 생성하거나, 기존 AS에 GA를 추가하지 않는다.

Helper Funcion Macro

  • GAS에서는 GA와 상호작용할 기본 헬퍼 함수를 추가할 수 있다.
    • GA 자체는 protected/private 접근자로 설정하고, 상호작용 함수를 public으로 설정하는 것이 좋다.
  • 사용이 필수 사항은 아니지만, 모범 사례로 간주한다.
  • 더보기
    #define GAMEPLAYATTRIBUTE_REPNOTIFY(ClassName, PropertyName, OldValue) \
    { \
    	static FProperty* ThisProperty = FindFieldChecked<FProperty>(ClassName::StaticClass(), GET_MEMBER_NAME_CHECKED(ClassName, PropertyName)); \
    	GetOwningAbilitySystemComponentChecked()->SetBaseAttributeValueFromReplication(FGameplayAttribute(ThisProperty), PropertyName, OldValue); \
    }
    
    #define GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
    	static FGameplayAttribute Get##PropertyName##Attribute() \
    	{ \
    		static FProperty* Prop = FindFieldChecked<FProperty>(ClassName::StaticClass(), GET_MEMBER_NAME_CHECKED(ClassName, PropertyName)); \
    		return Prop; \
    	}
    
    #define GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
    	FORCEINLINE float Get##PropertyName() const \
    	{ \
    		return PropertyName.GetCurrentValue(); \
    	}
    
    #define GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
    	FORCEINLINE void Set##PropertyName(float NewVal) \
    	{ \
    		UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent(); \
    		if (ensure(AbilityComp)) \
    		{ \
    			AbilityComp->SetNumericAttributeBase(Get##PropertyName##Attribute(), NewVal); \
    		}; \
    	}
    
    #define GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName) \
    	FORCEINLINE void Init##PropertyName(float NewVal) \
    	{ \
    		PropertyName.SetBaseValue(NewVal); \
    		PropertyName.SetCurrentValue(NewVal); \
    	}

Initialize

  • AttributeMetaData라는 GAS TableRow로 초기화 할 수 있다.
    • 이는 외부 파일에서 Import할 수도 있고, Editor에서 수동으로 채워줄 수도 있다.
  •  
    DataTable Asset 생성 할 때 AttributeMetaData를 선택한다.
  • 위와 같이 선언된 AttributeMetaData Table Asset에 추가 Row를 덧붙여 AS를 다수의 GA로 지원할 수 있다.
    • 예를 들어 MyAttributeSet.Health 외에 MyAttributeSet.Attack Row를 추가하면 한 AS에서 제공하는 다수의 GA를 하나의 Table Asset으로 Initialize 할 수 있다.
  • Column에 MinValue, MaxValue가 지원되지만 GA와 AS에 범위 제한 행동이 없으므로 이 항목들은 무효하다.

Gameplay Attribute Access 제어

  • GA에 대한 직접 Access를 지어하는 것은 AS 통해 수행되며, FGameplayAttributeData를 확장하지 않는다.
    • FGameplayAttributeData는 Data에 대한 Access만 저장하고 제공한다.
  • GAS에서 제공하는 Macro를 사용하지 않고, 동일한 Macro에 구현부를 직접 작업해 값의 범위를 제한할 수도 있다.
  • 더보기
    GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UMyAttributeSet, Health);
    float GetHealth() const;
    void SetHealth(float NewVal);
    GAMEPLAYATTRIBUTE_VALUE_INITTER(Health);

     

    float UMyAttributeSet::GetHealth() const
    {
        // Health의 현재 값을 반환하지만, 0 미만의 값은 반환하지 않습니다.
        // 이 값은 Health에 영향을 미치는 모든 모디파이어가 고려된 후의 값입니다.
        return FMath::Max(Health.GetCurrentValue(), 0.0f);
    }
    
    void UMyAttributeSet::SetHealth(float NewVal)
    {
        // 0 미만의 값을 받지 않습니다.
        NewVal = FMath::Max(NewVal, 0.0f);
    
        // 어빌리티 시스템 컴포넌트 인스턴스가 있는지 확인합니다. 항상 인스턴스가 있어야 합니다.
        UAbilitySystemComponent* ASC = GetOwningAbilitySystemComponent();
        if (ensure(ASC))
        {
            // 적절한 함수를 통해 현재 값이 아닌 베이스 값을 설정합니다.
            // 그러면 적용한 모디파이어가 계속해서 적절히 작동합니다.
            ASC->SetNumericAttributeBase(GetHealthAttribute(), NewVal);
        }
    }
    
    AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(AttributeSet->GetHealthAttribute()).AddUObject(this, &AGASAbilityDemoCharacter::OnHealthChangedInternal);

Gameplay Effect와의 Interaction

  • GA의 값을 제어하는 가장 일반적인 방법은 관련 Gameplay Effect를 처리하는 것이다.
  •  
    • PostGameplayEffectExecute는 Attribute의 Base 값을 변경되는 GameplayEffect가 호출되기 전에 호출된다.
      • GameplayEffect가 호출(Execute) 되기 전에만 호출되고, 직접 접근해 수정할 때에는 호출되지 않는다.
      • 또한 BaseValue가 아닌 current 값에 영향을 주는 GameplayEffect에서도 호출되지 않는다.
    • 위 예시에서 PostGameplayEffectExecute 함수는 public 접근자가 지정되어야 한다.
    • 함수 내에서 새로운 Body를 작성하되, Super::PostGameplayEffectExecute가 확실히 호출되어 있어야 한다.
  • 더보기
    class UMyAttributeSet : public AttributeSet
    {
    	.....
    public:
    	void PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData& Data) override;
    	.....
    }

     

    void UMyAttributeSet::PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData& Data)
    {
    	// 잊지 말고 부모 구현을 호출하세요.
    	Super::PostGameplayEffectExecute(Data);
    
    	// 프로퍼티 게터를 사용하여 이 호출이 Health에 영향을 미치는지 확인합니다.
    	if (Data.EvaluatedData.Attribute == GetHealthAttribute())
    	{
    		// 이 게임플레이 이펙트는 Health를 변경합니다. 적용하되 우선 값을 제한합니다.
    		// 이 경우 Health 베이스 값은 음수가 아니어야 합니다.
    		SetHealth(FMath::Max(GetHealth(), 0.0f));
    	}
    }

Replication

  • Multiplayer의 경우, 여타 Property Replicate와 유사하게 AS을 통해 GA Replicate를 할 수 있다.
  • 더보기
    class UMyAttributeSet : public UAttributeSet
    {
    	.....
    
        /** 네트워크를 통해 새 Health 값이 도착할 때 호출됨 */
        UFUNCTION()
        virtual void OnRep_Health(const FGameplayAttributeData& OldHealth);
        
        /** 리플리케이트할 프로퍼티 표시 */
        virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
    
    protected:
        /** 샘플 어트리뷰트 'Health'*/
        UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_Health)
        FGameplayAttributeData Health;
        
        ....
    };

     

    void UMyAttributeSet::OnRep_Health(const FGameplayAttributeData& OldHealth)
    {
    	// 디폴트 게임플레이 어트리뷰트 시스템의 repnotify 행동을 사용합니다.
    	GAMEPLAYATTRIBUTE_REPNOTIFY(UMyAttributeSet, Health, OldHealth);
    }
    
    void UMyAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
    {
    	// 부모 함수를 호출합니다.
             Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    
    	// Health에 대한 리플리케이션을 추가합니다.
    	DOREPLIFETIME_CONDITION_NOTIFY(UMyAttributeSet, Health, COND_None, REPNOTIFY_Always);
    }
    • Replicate 할 Attribute에 PROPERTY에 ReplicatedUsing 지정자를 추가하고, Callback 함수를 선언한다.
    • Source File에서 Callback 함수를 정의한다.
    • Callback 함수 내에는 GAMEPLAYATTRIBUTE_REPNOTIFY 매크로로
      Replicate 되는 Attribute에 대한 RepNotify 행동을 실행한다.
    • 만약 이 AS가 처음 Replicate되는 Attribute라면, GetLifetimeReplicatedProps에 추가한다.

주요 함수

PreAttributeChange

	/**
	 *	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) { }
  • Attribute의 CurrentValue가 바뀌기 전에 호출되는 함수
    • NewValue를 이용해 CurrentValue를 Clamp한 상태로 적용할 때 사용 함.
    • 더보기
      if (Attribute == GetMoveSpeedAttribute())
      {
      	// Cannot slow less than 150 units/s and cannot boost more than 1000 units/s
      	NewValue = FMath::Clamp<float>(NewValue, 150, 1000);
      }
  • 단, Attribute에 직접 접근하여 변경하는 것이 아닌 Macro에서 제공하는 Setter 함수나 GameplayEffects로만 호출된다.
  • 그리고 Epic에서는 이 함수를 Attribute 변경에 대한 Delegate로 사용을 권장하지 않는다.
    • 그저 Attribute 값의 보정(Clamp) 용도로 사용을 권장한다.

PostGameplayEffectExecute

	/**
	 *	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) { }
  • Attribute의 BaseValue가 Instant GameplayEffect로 인해 변경될 때 호출되는 함수
    • GameplayEffect로 인해 Attribute가 변동될 때 좀 더 복잡한 작업을 수행할 수 있다.
  • 함수가 호출된 시점에는 이미 Attribute 값이 변경이 된 후다.
    • 하지만 아직 Client에는 Replicate 되지 않았다.
  • 때문에 여기서 Clamping을 하는 것은 Client에서는 발생하지 않고, 그 결과만 받는다.

OnAttributeAggregatorCreate

	/** Callback for when an FAggregator is created for an attribute in this set. Allows custom setup of FAggregator::EvaluationMetaData */
	virtual void OnAttributeAggregatorCreated(const FGameplayAttribute& Attribute, FAggregator* NewAggregator) const { }
  • Attribute가 set에서 생성되어 Aggregator가 생성될 때.
  • FAggregatorEvaluateMetaData에 대한 Custom Setup을 할 수 있다.
    • Attribute에서 Modifier로 적용하는데 가장 작은 음수 값을 가져올 수 있다.
    • Paragon에서 여러 개의 감속 디버프가 걸릴 때 가장 낮은 비율로 적용할 때 이 기능을 사용
  • 더보기
    void UGSAttributeSetBase::OnAttributeAggregatorCreated(const FGameplayAttribute& Attribute, FAggregator* NewAggregator) const
    {
    	Super::OnAttributeAggregatorCreated(Attribute, NewAggregator);
    
    	if (!NewAggregator)
    	{
    		return;
    	}
    
    	if (Attribute == GetMoveSpeedAttribute())
    	{
    		NewAggregator->EvaluationMetaData = &FAggregatorEvaluateMetaDataLibrary::MostNegativeMod_AllPositiveMods;
    	}
    }

 

'UE5 > GAS' 카테고리의 다른 글

[GAS] Ability Task  (0) 2024.05.16
[GAS] Gameplay Effect  (0) 2024.05.15
[GAS] Gameplay Attribute  (0) 2024.05.13
[GAS] Ability System Component  (0) 2024.05.13
[GAS] Gameplay Ability System 소개  (0) 2024.05.10

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/gameplay-attributes-and-attribute-sets-for-the-gameplay-ability-system-in-unreal-engine?application_version=5.3

 

언리얼 엔진의 게임플레이 어빌리티 시스템을 위한 게임플레이 어트리뷰트 및 어트리뷰트 세트

게임플레이 어트리뷰트 및 어트리뷰트 세트 사용하기

dev.epicgames.com

https://github.com/tranek/GASDocumentation?tab=readme-ov-file#concepts-a

 

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

 

  • FGameplayAttribute를 사용해 Gameplay 관련 소수값을 저장/계산 수정하는 기능
    • 남은 생명력
    • Vehicle의 최고 속력
    • 아이템의 사용 가능 횟수
  • GAS의 Actor는 Gameplay Attribute(이하 GA)는 Attribute Set(이하 AS)에 저장한다.
    • 이는 Attribute를 Actor의 ASC에 등록하고, GA와 시스템의 나머지 부분간의 상호작용 관리에 유리하다.
    • 상호작용에는 범위 제한, 일시적 값 변경을 위한 계산, 영구적 값 변경을 하는 이벤트에 대한 반응이 있다.
  • GA는 Gameplay Effect로만 수정되어야 한다.
    • 그래야 ASC가 변화를 예측할 수 있다.

Gameplay Attribute

  • 생성하려면 우선 Attribute Set을 만들어야 한다.
    • 경우에 따라서는 AS가 없이도 GA가 존재할 수 있다.
    • 보통은 적절한 GA Type을 포함하는 AS가 없는 ASC에 GA가 저장된 경우
    • 하지만 이 케이스는 GA가 ASC와 상호작용하는 그 어떠한 행동도 정의되지 않고 값만 저장한다.
    • 때문에 권장되지 않는 방식이다.
  • 만약 GA를 Editor에 노출하고 싶지 않다면, UPROPERTY 지정자에 Meta=(HideInDetailsView)를 추가하면 된다.
  • GA 값은 CurrentValue와 BaseValue로 구분된다.

CurrentValue

  • BaseValue에 더불어 Gameplay Effects로 발생한 일시적인 변화를 합친 값
  • Duration, Infinite Gameplay Effects에 의해 값이 변경된다.
    • Duration Gameplay Effects: 미리 지정된 시간 동안만 효과가 적용되는 Gameplay Effects
    • Infinite Gameplay Effects: 특정 조건을 만족하기 전까지 계속 효과가 유지되는 Gameplay Effects
  • PreAttributeChange() 함수에서 CurrentValue 값의 Clamping 작업을 적용할 수 있다.

BaseValue

  • Attribute의 영구적인 값
  • Instant 혹은 Periodic Gameplay Effects에 의해 값이 변경된다.
    • Instant Gameplay Effects: 즉시 발동되지만 그 효과가 영구적인 Gameplay Effects
    • Periodic Gameplay Effects: 주기적으로 반복해서 효과가 발동하는 Gameplay Effects
  • 간혹 BaseValue Attribute의 최대값하고 혼동하지만 이는 잘못된 사용법이다.
    • 현재는 Attribute의 최대값은 별도의 Attribute로 분리되어야 한다.
    • FAttributeMetaData에 Min/Max Value Column이 있지만, Epic에서 WIP라고 코멘트 해두었다.
  • PostGameplayEffectExecute()에서 BaseValue 값의 Clamp를 적용할 수 있다.

Meta Attributes

  • Attributes들과 상호작용을 목적으로 일시적인 값을 담고 있는 임시 Attribute를 지칭
    • 예를 들어 damage의 경우, GameplayEffect로 health Attribute에 직접 영향을 주는 대신
      damage라는 Meta Attribute를 임시선언으로 사용할 수 있다.
    • 이 때 Meta Attribute는 health Attribute에 도달하기 전에 여러 변화를 가할 수 있다.
      • GameplayEffectExecutionCalculation 함수를 통해 Buff/Debuff 적용
      • Shield Attribute에 의해 발생하는 damage의 감소
  • Meta Attribute는 Gameplay Effect간의 영속성이 없고 모두에 의해 override될 수 있다.
    • 또한 일반적으로 Replicate 되지 않는다.
  • Meta Attribute는 적용 대상 Attribute의 수치 변화와 그에 따른 상호작용을 논리적으로 분리할 수 있다.
    • Gameplay Effect가 "얼마나 수치를 변경할 것인가?"를 결정한다면,
      Attribute Set은 "수치가 바뀐 뒤 어떻게 반응할 것인가?"를 결정한다.
    • 하지만 상위 Attribute Set에서 제공하는 Attribute를 대상으로 한다 해도,
      하위 Attribute Set의 구성에 따라 실제 변화량과 그 반응은 다를 수 있다.
    • Meta Attribute는 이렇게 다양한 경우에 대해 유연하게 대응할 수 있다.
  • 물론 Meta Attribute는 좋은 패턴이지만 필수적인 것은 아니다.
    • 만약 Attribute에 대해 하나의 ExecutionCalculation만 제공되고 모든 Actor가 그 Attribute Set을 사용한다면,
      관련된 값들을 직접 수정하는 방식으로 작업하는 것이 더 좋을 수 있다.

Responding to Attribute Changes

  • Attribute의 변화를 UI나 다른 Gameplay에 전달하려면,
    UAbilitySystemComponent::GetGameplayAttributeValueChangeDelegate(FGameplayAttribute Attribute)를 쓴다.
  • 더보기
    typedef TMulticastDelegate_OneParam< void, const FOnAttributeChangeData & > FOnGameplayAttributeValueChange
    
    FOnGameplayAttributeValueChange & GetGameplayAttributeValueChangeDelegate
    (
        FGameplayAttribute Attribute
    )

     

    AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(AttributeSetBase->GetHealthAttribute()).AddUObject(this, &AGDPlayerState::HealthChanged);
    
    virtual void HealthChanged(const FOnAttributeChangeData& Data);

     

    • 이 때 Attribute 변수는 Server에서만 값이 채워진다.
  • BP에서는 이 함수가 AsyncTask로 감싸져서 제공된다.
    • 보통 UI_HUD에서 사용되며 해당 Widget의 Destruct Event에서 EndTask()가 호출되기 전까지 죽지 않는다.

Derived Attributes

  • 하나의 Attribute가 복수의 Attribute로부터 값이 정해질 때, Infinite Gameplay Effect를 여러 개의 Modifier Magnitude Calculation(이하 MMC)와 함께 사용한다.
  • Derived Attributes는 해당 Attribute에 의존성을 갖는 Attribute들을 자동으로 갱신해준다.
  • Derived Attribute의 연산자 공식은 Modifier Aggregator와 동일한 방식으로 동작한다.
    • 만약 저해진 순서대로 계산이 되어야 한다면, MMC 내부에서 시행하면 된다.
  • PIE에서 Multi Client로 실행한 경우, Editor Preferences -> Run Under One Process 옵션을 비활성화 해야한다.
    • 안 그러면 Derived Attributes가 각각의 Attribute가 client에서 첫번째로 update되지 않으면 갱신하지 않을 것이다.

예시

  • 다음과 같이 값이 정해지는 TestAttrA가 있다고 가정하자.
    • TestAttrA = (TestAttrA + TestAttrB) * (2 * TestAttrC) .
  • 이 때 TestAttrA에 대한 Derived Attributes 설정은 다음과 같다.
  •  

'UE5 > GAS' 카테고리의 다른 글

[GAS] Ability Task  (0) 2024.05.16
[GAS] Gameplay Effect  (0) 2024.05.15
[GAS] Attribute Set  (1) 2024.05.14
[GAS] Ability System Component  (0) 2024.05.13
[GAS] Gameplay Ability System 소개  (0) 2024.05.10

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/gameplay-ability-system-component-and-gameplay-attributes-in-unreal-engine?application_version=5.3

 

언리얼 엔진의 게임플레이 어빌리티 시스템 컴포넌트 및 게임플레이 어트리뷰트를 살펴봅니다.

게임플레이 어트리뷰트 및 어트리뷰트 세트와 함께 어빌리티 시스템 컴포넌트를 사용하는 방법을 살펴봅니다.

dev.epicgames.com

  • UGameAbilitySystemComponent는 Actor와 GAS 사이의 가교 역할을 한다.
  • GAS와 상호작용해야 하는 Actor는 다음 2가지 조건 중 하나를 반드시 만족해야 한다.
    • Ability System Component(이하 ASC)를 가지고 있거나
    • 다른 Actor의 ASC에 Access 할 수 있거나.
  • ASC는 FGameplayAbilitySpecContainer ActivatableAbilities 중 승인된 Gameplay Abilities를 가지고 있다.
  • 언제나 ActivatableAbilities.Items를 통해 iterate를 하려 한다면,
    List의 변화를 막기 위해 ABILITY_SCOPE_LOCK()을 반복문 앞에 추가해 Lock을 걸어야 한다.
  • 매 ABILITY_SCOPE_LOCK() 호출 범위마다 AbilityScopeLockCount가 증가하고,
    그 범위를 벗어나면 AbilityScopeLockCount가 감소한다.
  • ABILITY_SCOPE_LOCK() 호출 범위 내에서 Ability를 제거하지 말 것.
    • List가 Lock이 걸려 있으면 AbilityScopeLockCount를 확인하여 제거를 방지한다.

기본 요구사항

  • Actor가 Game Ability System를 사용하려면 다음 2가지를 충족해야 한다.
    • IAbilitySystemInterface 구현
    • GetAbilitySystemComponent 함수 override
  • Actor가 자체 ASC를 가지고 있는 것이 일반적이다.
  • 하지만 다른 Actor가 소유한 ASC를 사용하려는 경우가 있다.
    • Player의 Pawn/Character가 소환/소멸 되거나 처리할 때 리셋되면 안되는 것들이 포함되어 있기 때문.

Replication Mode

  • ASC는 GameplayEffects, GameplayTags, GameplayCues에 대해 3가지 Replication Mode를 제공한다.
  • Attributes는 속한 AttributeSet에 의해 Replicate된다.

Full

  • Single Player에서 사용
  • 모든 GameplayEffects가 모든 Client에 Replicate된다.

Mixed

  • Multiplayer에서 유저가 Control 하는 Actor
  • GameplayEffects가 소유한 Client에게만 Replicate된다.
  • GameplayTags와 GameplayCues만 모두에게 Replicate된다.

Advanced

  • 이 Mode에서 OwnerActor의 Owner가 Controller가 되는 것을 권장한다.
    • PlayerState의 Parent는 기본적으로 Controller이지만, Character는 그렇지 않다.
    • OwnerActor가 PlayerState가 아닌 상태에서 Mixed Mode를 사용하는 경우, 
      SetOwner() 함수를 호출해 OwnerActor를 Controller로 명시해야 한다.
    • 4.24 버전 이후(현재)에는 PossessedBy() 함수로 Owner를 Controller로 지정한다.

Minimal

  • Multiplayer에서 AI가 Control 하는 Actor
  • GameplayEffects가 어디에도 Replicate 되지 않는다.
  • GameplayTags와 GameplayCues만 모두에게 Replicate 된다.

예시

 

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

ASC가 Actor에 있는 경우

  • 더보기
    AGDPlayerState::AGDPlayerState()
    {
    	// Create ability system component, and set it to be explicitly replicated
    	AbilitySystemComponent = CreateDefaultSubobject<UGDAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
    	AbilitySystemComponent->SetIsReplicated(true);
    	//...
    }

     

    class APACharacterBase : public AActor, public IAbilitySystemInterface
    {
    	//~ IAbilitySystemInterface 시작
    	/** 어빌리티 시스템 컴포넌트를 반환합니다. */
    	virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
    	//~ IAbilitySystemInterface 끝
       
    	/** 어빌리티 시스템 컴포넌트입니다. 게임플레이 어트리뷰트 및 게임플레이 어빌리티를 사용하려면 필요합니다. */
    	UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Abilities")
    	UAbilitySystemComponent* AbilitySystemComponent;
    }

     

    void APACharacterBase::PossessedBy(AController * NewController)
    {
    	Super::PossessedBy(NewController);
    
    	if (AbilitySystemComponent)
    	{
    		AbilitySystemComponent->InitAbilityActorInfo(this, this);
    	}
    
    	// ASC MixedMode replication requires that the ASC Owner's Owner be the Controller.
    	SetOwner(NewController);
    }
    
    UAbilitySystemComponent* APACharacterBase::GetAbilitySystemComponent() const
    {
    	return AbilitySystemComponent;
    }

     

    void APAPlayerControllerBase::AcknowledgePossession(APawn* P)
    {
    	Super::AcknowledgePossession(P);
    
    	APACharacterBase* CharacterBase = Cast<APACharacterBase>(P);
    	if (CharacterBase)
    	{
    		CharacterBase->GetAbilitySystemComponent()->InitAbilityActorInfo(CharacterBase, CharacterBase);
    	}
    
    	//...
    }
    • ASC는 Server, Client 양쪽에서 모두 Initialize되어야 한다.
      • 또한 Controller가 Set(Possess)되고 나서 Initialize 되기를 원한다.
      • SinglePlay는 Server 파트에 대해서만 걱정을 하면 된다.
    • ASC가 설정된 Player는 각 파트에서 명시적으로 함수를 호출하여 Initialize를 해야 한다.
      • Server 파트: Pawn::PossessedBy()
      • Client 파트: APlayerController::AcknoledgePossession

고급 시나리오

  • 다른 Actor가 소유한 ASC를 사용하는 Actor를 구성할 수 있다.
    • ex) UPlayerState가 소유한 ASC를 사용하는 Pawn
  • 이를 위해 GetAbilitySystemComponent 함수가 Owner에서 ASC를 얻거나, 캐싱해야 한다.
  • 이 방식은 다음 속성이 있는 프로젝트에서 주로 발생한다.
    • Player가 제어하는 Actor가 리스폰을 하고, 그 과정에서 Game Ability System 정보가 필요한 경우
    • 장비나 Modular Machine, 신체 부위를 표현하기 위해 Actor가 자신에게 다른 Actor를 Attach하는 경우
    • 간단한 AI나 MOBA 게임
  • Attach 된 Actor의 Gameplay Ability System Interaction은 Parent의 ASC에 라우팅 할 수 있다.
    • 가장 간단하게 구현하는 방법은 Attach된 Actor의 GetAbilitySystemComponent가 Parent로 전달되는 것.
    • Actor 다른 ACtor에 Attach되거나 처리되는 동안 Cache 된 Pointer를 유지하면 Performance를 향상할 수 있다.
  • 단, ASC가 PlayerState에 있는 경우, PlayerState의 NetUpdateFrequency를 늘려야 한다.
  • Gameplay Ability System은 다수의 Actor가 하나의 Ability System Component를 공유하는 것은 지원한다.
    • 하지만 하나의 Actor가 다수의 Ability System Component를 가지는 것은 지원하지 않는다.
    • Actor의 Ability system Component에 변경사항을 적용하고 이를 얻을 때 모호함을 유발할 수 있기 때문.
 

Replicate Actor Properties In Unreal Engine | Unreal Engine 5.4 Documentation | Epic Developer Community

Learn how to replicate actor properties in Unreal Engine; including conditional replication, custom conditions, and object references.

dev.epicgames.com

ASC가 PlayerState에 있는 경우

  • 더보기
    // Server only
    void AGDHeroCharacter::PossessedBy(AController * NewController)
    {
    	Super::PossessedBy(NewController);
    
    	AGDPlayerState* PS = GetPlayerState<AGDPlayerState>();
    	if (PS)
    	{
    		// Set the ASC on the Server. Clients do this in OnRep_PlayerState()
    		AbilitySystemComponent = Cast<UGDAbilitySystemComponent>(PS->GetAbilitySystemComponent());
    
    		// AI won't have PlayerControllers so we can init again here just to be sure. No harm in initing twice for heroes that have PlayerControllers.
    		PS->GetAbilitySystemComponent()->InitAbilityActorInfo(PS, this);
    	}
    	
    	//...
    }

     

    // Client only
    void AGDHeroCharacter::OnRep_PlayerState()
    {
    	Super::OnRep_PlayerState();
    
    	AGDPlayerState* PS = GetPlayerState<AGDPlayerState>();
    	if (PS)
    	{
    		// Set the ASC for clients. Server does this in PossessedBy.
    		AbilitySystemComponent = Cast<UGDAbilitySystemComponent>(PS->GetAbilitySystemComponent());
    
    		// Init ASC Actor Info for clients. Server will init its ASC when it possesses a new Actor.
    		AbilitySystemComponent->InitAbilityActorInfo(PS, this);
    	}
    
    	// ...
    }
    •  ASC가 PlayerState에 있는 경우에는 명시적으로 Initialize 하는데 호출되는 함수가 달라진다.
      • Server: Pawn::PossessedBy()
      • Client: Pawn::Onrep_PlayerState()
    • 이 모든 과정은 Client에 PlayerState가 있다는 것을 상정하고 작업된다.
    • 만약 아래 로그가 발생하면, ASC가 Client에서 Initialize가 되지 않은 것이다.
      • LogAbilitySystem: Warning: Can't activate LocalOnly or LocalPredicted ability %s when not local!

'UE5 > GAS' 카테고리의 다른 글

[GAS] Ability Task  (0) 2024.05.16
[GAS] Gameplay Effect  (0) 2024.05.15
[GAS] Attribute Set  (1) 2024.05.14
[GAS] Gameplay Attribute  (0) 2024.05.13
[GAS] Gameplay Ability System 소개  (0) 2024.05.10

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/gameplay-ability-system-for-unreal-engine?application_version=5.3

 

언리얼 엔진의 게임플레이 어빌리티 시스템 | 언리얼 엔진 5.4 문서 | Epic Developer Community

게임플레이 어빌리티 시스템 개요

dev.epicgames.com

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/understanding-the-unreal-engine-gameplay-ability-system?application_version=5.3

 

언리얼 엔진 게임플레이 어빌리티 시스템을 살펴봅니다. | 언리얼 엔진 5.4 문서 | Epic Developer Commu

게임플레이 어빌리티 시스템 및 각 컴포넌트 클래스가 어빌리티에 기여하는 방법을 살펴봅니다.

dev.epicgames.com

Gameplay Ability란?

  • RPG나 MOBA의 Ability, Attribute 구축을 지원하는 고도로 유연한 Framework
  • 다양한 인게임 Ability를 설계, 구현하고 효과적으로 연결할 수 있도록 기능을 지원
    • Action의 결과로 다양한 Attribute를 조절하거나 Effect를 생성
    • Timer나 자원 비용을 구현하여 Action의 빈도 조절
    • Ability의 Level과 그에 따른 Effect 변경
    • Particle 및 Sound Effect를 활성화
    • 점프와 같은 단순한 Action 기능
  • Gameplay Ability System과 상호작용하는 Actor에는 Ability System Component가 있어야 한다.
    • Ability를 활성화
    • Attribute 저장
    • Effect Update
    • Actor간 상호작용 처리
  • Game Ability System을 생성하고 Ability System Component가 있는 Actor를 생성하면
    Ability를 생성하고, Actor가 Ability에 어떻게 반응하는지 결정할 수 있다.

Gameplay Ability

  • UGameplayAbility를 상속받은 Class나 BP들을 지칭
    • 특정 Ability에서 어떤 역할을 하는지 정의
    • 해당 Ability의 Replication과 Instancing Activity와 같은 요소가 어떻게 처리될지 정의

Ability Task

  • Gameplay Ability에서 호출하는 일련의 비동기 구성 요소
    • UAbilityTask Class를 상속 받음.
  • C++로 작성된다.
  • 작업이 끝날 때 cpp의 Delegate나 BP의 출력 실행 핀을 호출한다.

Gameplay Attribute

  • FGameplayAttribute 구조체에 저장되는 float 값
    • Game이나 Actor에 특정한 방식으로 영향을 미치는 값들
  • 캐릭터의 health, power, jump height 등의 수치들이 이에 해당한다.

Gameplay Effects

  • Gameplay Attribute를 변경하는 수단
    • 즉시 변경할 수도 있고, 일정 시간동안만 변경할 수도 있다.
    • 후자는 버프/디버프

Owner Tracking

  • Ability와 Effect는 "Owner"라는 개념을 유지해야 한다.
    • Effect가 계산을 수행할 때에는 Owner가 누구인지 알아야 Attribute를 사용할 수 있다.
  • 이 때 Owner는 Network Replication의 것과 다르다.

State Tracking

  • Ability는 다음과 같은 State 순환을 Tracking 할 수 있어야 한다.
    • Ability가 활성화 된 시점
    • Ability의 실행이 현재 진행중인 시점
    • Ability가 완전히 완료되고 더 이상 활성화되지 않은 시점

실행 조정하기

  • Ability는 실행 도중 특정 타이밍에 System과 다양한 Interaction을 할 수 있어야 한다.
    • Animation Montage 활성화
    • Character의 Movmenet를 일시적으로 제어
    • Visual Effect Trigger
    • Overlap 및 Collision Effect 수행
    • 일시적 혹은 영구적으로 Attribute 변경
    • 인게임 Resource 증감
    • 다른 Ability 활성화 허용 또는 차단
    • Ability 사용을 제한하기 위한 쿨다운 처리
    • 인게임 Evnet에 의한 중단
    • 진행 중인 다른 Ability 취소
    • Character의 주요 State 변경사항 적용
      • 새로운 MovementMode 활성화
    • 다른 Interaction 도중 입력에 반응
    • UI에 Ability의 인게임 상태 전달

시스템 구성

  • Edit -> Plugin에서 Gameplay Abilities를 검색해 활성화, Editor 재시작
  • [MyProjectName].Build.cs에 다음 항목 추가
    • GameplayAbilities
    • GameplayTags
    • GameplayTasks

Various Components of Gameplay Ability System

Owner Tracking

  • Gameplay Ability를 사용하기 위해서는 Ability System Component를 Actor에 Attach해야 한다.
  • Ability System Component는 다음 기능을 제공한다.
    • Actor에 Ability 추가/제거
    • Actor가 소유한 Ability가 무엇인지 지속적으로 Tracking, 활성화
    • Ability system의 Context에서 소유 Actor를 나타내는 주요 수단
    • 다양한 요소를 Tracking하기 위한 System과 소유 Actor에 직접 Access하기 위한 Interface 제공
      • Attribute
      • 진행중인 Effect
      • Gameplay Tag
      • Gameplay Effect
  • Multiplayer Game 환경에서 Ability System Component는 다음 기능또한 제공한다.
    • Client 정보 Replicate
    • Server에 Player Action 전달
    • Client가 Ability System Component의 State를 변경 할 권한이 있는지 확인
  • Ability System Component의 부모 Actor는 원격 활성화를 수행하기 위해 로컬에서 제어되는 Player가 소유해야 한다.

Ability 활성화 및 실행 처리

  • Gameplay Ability는 Ability의 모든 Event를 실행하는 것을 담당하는 BP Object이다.
    • Animatino 재생
    • Effect Trigger
    • Owner로부터 Attribute 가져오기
    • Visual Effect 표시

Ability 활성화 제어

  • Gameplay Ability를 활성화 할 경우 System은 Ability가 진행중인 것으로 인식
  • 이후 활성화 Event에 Attach 된 Code를 실행해 완료 함수를 호출
    • Ability의 실행이 완료됨을 알리기 전까지 각 함수와 Gameplay 작업을 거쳐 이동
  • 정리 작업을 추가로 징행해야 하는 경우 완료 시 Event에 Code를 추가로 Attach 할 수 있다.
  • Ability를 취소하여 실행을 중단할 수 있다.
  • Gameplay Ability는 Gameplay Tag를 사용하여 실행하는 것을 제한한다.
    • 모든 Ability는 활성화 될 때까지 2가지 Tag 목록을 가지고 있는다.
      • 소유 Actor에 추가하는 Tag 목록
      • 활성화를 차단하거나 Ability를 자동으로 취소하는 Tag 목록
  • 자체 Code로 Ability 실행을 수동을 취소/차단/허용 할 수 있다.
  • Gameplay Ability는 다음 4가지 방법을 통해 활성화 할 수 있다.
    • Gameplay Ability Handle
      • BP나 C++에서 Ability를 명시적으로 활성화
      • Ability가 부여될 때 Ability System Component에 의해 제공 됨
    • Gameplay Event
      • 일치하는  Ability Trigger로 모든 Ability를 실행
      • Input/Define Machanism을 추상화해야 하는 경우에 유연성 극대화를 위해서는 이 방식을 적극 권장 함
    • 일치하는 Tag와 함께 Gameplay Effect
      • 일치하는 Ability Trigger로 모든 Ability 실행
      • GamePlay Effect의 Trigger Ability를 끄려는 경우 선호 됨
      • 비활성화 된 Animation을 재생하고 다른 Game Action을 차단하는 Ability를 Trigger하는 Sleep Deburf
    • Input Code
      • Ability system Component에 의해 추가 됨
      • 호출 시 일치하는 모든 Ability Trigger
      • Gameplay Event와 유사

Ability 실행 제어

  • Gameplay Ability는 일반적으로 사용되는 다양한 사용 사례와 기능을 제공
    • Cooldown
    • Resource 비용 할당
    • Animation
    • 기타 일반적인 Unreal Engine System을 처리하는 Preprocessed Gameplay Ability Working Library
  • 표준 BP Function Node가 실행을 즉시 종료할 때 상태를 계속 Tracking 하며 도중에 다른 Event를 실행
  • 부모 Gameplay Ability가 적절하게 취소되고 정리되었는지 Tracking 가능
  • Game에서 Gameplay Ability 작업을 확장해 Custom Gameplay Logic 구현하는 것이 일반적인 용례이다.
  • 소유 Actor에서 Gameplay Tag와 Event Data Struct를 수신하기 위해 대기하는 Gameplay Event에 응답 가능.

Attribute Set 및 Attribute

  • Gameplay Attribute System은 Attribute로 구성된 Attribute Set를 통해 Actor와 Interact한다.
    • Attribute Set은 계산에 사용되는 소수값으로, 보통 핵심 통계값을 Tracking 하는데 사용된다.
  • 기본 변수 대비 Gameplay Attribute 사용을 했을 시 얻는 이점이 몇가지 있다.
    • System을 Build할 수 있는 일관적이고 재사용 가능한 Attribute Group을 제공
    • Reflection을 통해 Access 할 수 있어 BL Editor에서 간단한 계산과 Effect 생성 가능
    • Default/Current/Max Value를 별도로 Tracking해 일시적인 수정 및 지속적인 Effect를 쉽게 생성
    • 값을 Client에 Replicate해 UI 시각화에 안전함
  • Actor에서 Gameplay Ability를 사용하려면 Ability System Component에 Attribute Set을 추가해야 한다.
    • 그러면 Ability System Component는 Attribute Set에 할당된 Attribute에 Access할 수 있게 된다.

Gameplay Effect 처리

  • Gameplay Ability System은 Gameplay Effect를 사용해 Target Actor에 변경사항을 적용한다.
    • Damage 적용
    • 지속되는 Effect를 적용하는 One-shot Effect
      • 이 경우에 Gameplay Effect는 제거될 때까지 Target Actor에 자신을 Attach한다.
      • 만료되거나 정리되기 전까지로 제한된 수명을 미리 설정 할 수 있다.
      • 혹은 Target Actor의 Gameplay attribute에 적용된 변경사항을 실행 취소할 수도 있다.
  • Gameplay Effect는 Attribute에 기반하여 계산을 한다.
    • BP Editor에서 간단하게 계산을 직접 생성할 수도 있다.
    • 하지만 복잡한 로직을 가지고 있고,
      다수의 Attribute에 동시에 영향을 미치는 Custom Effect 계산은 C++로 작업하는 것이 적절하다.
  • 보통 Gameplay Ability의 Owner Actor와 Target Actor 모두에서 정보를 처리할 수 있다.
    • 그렇기에 일반적으로 계산을 재사용이 가능한 코드로 결합할 수 있다.

Cosmetic Effect 처리

  • Gameplay Queue는 Visual Effect와 Sound Effect 실행을 담당하는 Actor 및 UObject이다.
    • Multiplay Game에서는Cosmetic Feedback을 Replicate하는데 선호되는 Method이다.
    • 생성할 때 Event Graph 내에서 재생할 Effect를 위한 Logic을 실행
    • 일련의 Gameplay Tag와 연관될 수 있으며, 일치하는 Gameplay Effect를 자동으로 적용한다.
  • Gameplay Queue는 신뢰할 수 있는 Replication을 사용하지 않는다.
    • 때문에 일부 Client가 Queue를 수신하지 못하거나, Feedback을 표시하지 못하는 경우가 있다.
  • Gameplay Code와 결합되어 있는 경우 이 때문에 동기화가 해제될 수 있다.
    • 따라서 Gmaeplay Queue는 Cosmetic Effect에서만 사용되어야 한다.
  • 모든 Client가 Replicate 되어야 하는 경우에는 Replication 처리를 위해 Ability 작업에 의존해야 한다.
    • Montage 재생 Ability 작업이 이에 적합한 예시이다.

Network Multiplay

Ability System Component 및 Replication

  • Ability System Component가 모든 Component에 전체 State를 Replicate하면 안된다.
    • 대역폭 절약 및 부정행위 방지 목적
  • 모든 Client에 Ability와 Gamplay Effect를 Replicate하면 안된다.
    • 영향을 받는 Gameplay Attribute와 Tag만 Replicate 해야 한다.

Ability Replicate 및 Prediction 사용

  • Network Game의 Ability 대부분은 Server에서 실행되고 Client로 Replicate되어야 한다.
    • 이로 인해 Ability 활성화에 지연이 발생하는 것은 일반적인 현상이다.
  • 만약 빠른 속도로 진행되어야 하는 Multiplay Game의 경우 Ability를 Local로 활성화 하고,
    그 사실을 Server에 전달해 동기화 하는 방식을 사용한다.
  • Server가 Ability 활성화를 거부하는 경우 Local로 적용한 변경사항을 실행 취소해야 한다.
    • 일부 Gameplay Effect의 경우 Server에 의해 거부된 경우에 대한 롤백 기능을 지원한다.
    • 대부분의 즉각적이지 않은 Gameplay Effect가 포함된다.
    • Damage나 즉각적인 기타 Attribute/Tag 변경사항은 제외된다.

Server가 소유한 Object와 Interact 하기 위한 Ability 사용

  • Gameplay Ability는 Bot, NPC, Server 소유의 기타 Actor 및 Object와의 Interact를 처리할 수 있다.
    • 이는 Local 소유의 Actor(Player/Pawn), Replicated Ability, 비 GAS Server RPC를 통해 수행되어야 한다.
    • 위 호출은 Interaction을 Server에 Replicate한 다음 NPC와의 변경사항을 수행하는 권한을 가진다.

'UE5 > GAS' 카테고리의 다른 글

[GAS] Ability Task  (0) 2024.05.16
[GAS] Gameplay Effect  (0) 2024.05.15
[GAS] Attribute Set  (1) 2024.05.14
[GAS] Gameplay Attribute  (0) 2024.05.13
[GAS] Ability System Component  (0) 2024.05.13

Source Control 활성화

Editor Preference

  • Edit -> Editor Preference -> Loading & Saving에서 활성화 가능

Automatically Checkout on Asset Modification

  • 활성화 시 변경된 Asset을 자동으로 Checkout 한다.

Prompt for Checkout on Package Modification

  • 활성화 시 Source Control로 제어되는 Package가 변경될 때 해당 Package를 Checkout 할지 묻는 창을 띄운다.

Add New Files when Modified

  • 새 파일이 추가되면 Source Control에도 추가된다.

Use Global Settings

  • Project 단위가 하닌 Global Source Control Login Setting을 사용한다.
  • 옵션 변경 시 다시 Login 해야 한다.

Tool for Diff'ing text

  • Text File Version비교에 사용할 Tool의 File Directory를 지정한다.

contents Browser

  • Asset/Folder 우클릭 -> Source Control Sesction에 Connect to Source Control 선택

상태 아이콘

Source Control 작업

  • Source Control 활성화 상태에서 Asset 우클릭 시 다음 메뉴를 볼 수 있다.

Check Out

  • Asset을 편집 가능한 상태로 Check out
  • Asset을 다른 사용자가 동시에 편집하지 못하도록 한다.

Refresh

  • Asset의 Source Control 상태를 새로고침 한다.

History

  • 선택된 Asset의 Revision History를 확인하여 기존 수정 내용을 볼 수 있다.

Diff Against Depot

  • 이 Asset을 현재 Source Control Depot에 저장된 Version과 비교할 수 있다.

Check Out/Check In

  • Asset 편집을 위해 Checkout 하기 위해서는 우클릭해서 Check Out을 선택하면 된다.
  • 하지만 Checkin 할 때에는 다음 규칙을 따라야 한다.
    • Asset 우클릭 -> Check In 선택
      • 선택 시 필요한 Changelist 설명을 입력하라는 창이 뜬다.
      • 필수 사항으로, 입력하지 않으면 Ok 버튼이 나타나지 않는다.
    • 설명을 입력하면 Asset의 Revision History에 추가된다.

Contents Hot Reload

  • Source Control 작업에 의해 Contents가 변경되었을 때 Eidtor 내 Source Control에서 Contents를 Reload 해준다.
  • 현재 Editor 내의 통합된 Source Control을 통해 작업을 할 때에만 작동한다.
    • 외부에서 변경했을 때에는 Reload가 발생하지 않는다.
  • Contents Hot Reload는 명령이 있을 때 ASset의 지난 저장 상태에서 Reload 하는 기능도 제공한다.
    • Asset 우클릭 -> Asset Action -> Reload 선택
    • Asset을 저장하지 않는 변경사항을 revert 하고 Disk 버전으로 되돌리고자 할 때 매우 유용하다.

Source Control 비활성화

  1. Level Editor 우상단 구석에 초록생 화살표 아이콘을 클릭해 Source Control Login 창으로 접속한다.
  2. Run Without Source Control 버튼을 클릭해 초록색 아이콘을 클릭한다.
    • 클릭하면 금지 아이콘으로 변경되어 Source Control이 비활성화 된다.

 

'UE5 > Utility' 카테고리의 다른 글

[Basic] Packaging  (0) 2024.05.09
[Programming Tool] LLM(Low-level Memory Tracker)  (0) 2024.05.07
[ProgrammingTool] Sparse Class Data  (0) 2024.05.07

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/packaging-unreal-engine-projects

 

프로젝트 패키징 | 언리얼 엔진 5.4 문서 | Epic Developer Community

언리얼 게임 프로젝트의 배포용 패키지 만들기입니다.

dev.epicgames.com

  • Packaging으로 모든 Code와 Contents가 최신인지, Target Plartform에서 실행하기 적합한 포맷인지 확인한다.
  • File -> Package Project 옵션에서 Package에서 만들 수 있는 대상 Platform 확인 가능

Process

  • Project Source Code를 Compile
  • Cooking
    • 필수 컨텐츠를 Target Platform에서 사용 가능한 Format으로 변환
  • Compile 된 Code와 Cooking된 Contents를 묶어 Windows Installer와 같은 배포 파일 생성
    • Android Packaging은 몇 가지 선택사항이 더 있다.

Game Default Map 설정

  • Packaging을 하려면 Game 시작 시 Load 할 Game Default Map 설정이 필요
    • Default Map이 설정되지 않으면 Packaging 된 Game 시작 시 검은 화면이 나온다.
    • Edit -> Project Setting -> Map & Modes에서 설정

Package 만들기

  • File -> Project Package -> [Platform Name] 선택
  • Target Directory 선택한다.
    • Target Directory를 확인하고 나면 선택된 Platform용으로 Project를 Packaging한다.
  •  Packaging은 매우 오래 걸리는 작업이라 Process가 Background에서 진행된다.
    • 이 때 Editor는 계속 사용할 수 있다.
    • Cancel로 취소 가능
    • Show Log 링크로 자세한 로그 표시 가능
      • Packaging Process가 실패했을 때 오류 사항을 확인하여 버그가 될 수 있는 Warning을 잡아낼 수 있다.
      • 보이지 않을 경우 Window -> Developer Tool -> Output Log / Message Log 옵션으로 활성화 가능
  • Packaging이 성공적으로 끝나면, Target Directory에 Packaging 된 Project가 들어간다.

Distribution

  • App Store나 Google Play Store에 제출하려면 Distribution Mode로 Game Package를 만들어줘야 한다.
    • Packaging -> Packaging Setting -> Distribution 옵션을 체크

IOS

  • Apple의 Developer site에서 배포 Certificate와 MobileProvision을 생성해줘야 한다.
  • Development Certificate와 같은 방식으로 Distribution Certificate 설치
    • 배포 Provision 이름에 "Distro_" 접두사를 붙여준다.
      • Distro_MyProject.mobileprovision이 MyProject.mobileprovision이 된다.

Android

  • .apk File Signing을 할 Key를 생성
  • BuildTool에 정보를 줘야 한다.
    • Engine/Build/Android/Java/SigningConfig.xml
    • 이 파일을 직접 수정하면 모든 Project에 적용
    • Project/Build/Android Directory에 복사하면 해당 Project에만 적용 된다.

Advanced Setting

  • File -> Project Package -> Package Setting
    • 또는 Edit -> Project Setting -> Packaging

Build Configuration

  • Code based Project를 Compile 할 때의 Build Configuration
    • Blueprint 전용 Project에서는 이 옵션이 없다.
  • Code Project Debuging의 경우 DebugGame 선택
    • 이 외의 Debuging은 최소한으로 지원
  • Performance가 좋은 개발용으로는 대부분 Development 선택
  • 최종 빌드의 경우 Debuging 정보와 기능이 제거된 Shipping을 선택

Statging Directory

  • Packaging 된 Build가 들어가게 될 Directory
  • Target Directory 선택 창에서 다른 Directory를 선택하면 자동으로 업데이트

Full Rebuild

  • Code를 전부 다 Compile 할지 여부
    • 비활성화 하면 수정된 Code만 Compile
    • 잘 활용하면 Packaging Process 속도가 빨라질 수 있다.
  • Shipping 빌드의 경우 누락/오래된 버전 갱신을 위해 항상 활성화를 해주는것이 좋다.
  • 기본적으로 활성화 되어있다.

Use Pak File

  • Project의 Asset들을 개별 Fiel로 둘지, 하나의 Package로 만들지 여부
    • 활성화하면 모든 Asset은 하나의 .pak 파일에 들어간다.
    • 비활성화 하면 모든 파일을 하나하나 복사한다.
  • Asset File이 많은 경우, 전송해야 하는 File 양이 줄어들어 배포가 쉬워질 수 있다.
  • 기본적으로 비활성화 되어 있다.

Generate Chunks

  • Streaming Install에 사용할 수 있는 .pak File Chunk 생성 여부

Build Http Chunk Install Data

  • HTTP Chunk Installer 용 Data 생성 여부
  • 이를 통해 Web Server에서 Hosting하여 Runtime에서 설치가 가능

Http Chunk Install Data Directory

  • Build가 완료되면 Data가 설치되는 Driectory

Http Chunk Install Data Version

  • HTTP Chunk Install Data의 Version Name

Include Prerequisites Install

  • Packaging된 Game의 사전 요구사항 Installer를 포함시킬지 여부
    • OS Component 재배포판

Signing and Crypto

  • 출시 때 .Pak 파일을 서명하거나 암호화
    • Data 추출이나 Hacking을 방지
    • Unreal Engine 4.22 이후에서는 OpenSSL Library로 통합 함
  • Project Setting -> Crypto Section

Encrypt Pak INI Files

  • Project의 .pak File에 존재하는 모든 .ini File을 암호화
    • 최소한의 Runtime 비용으로 Data Mining 혹은 변조를 방지할 수 있다.

Encrypt Pak Index

  • .Pak File Index를 암호화
    • 최소한의 Runtime 비용으로 UnrealPak File을 열고 확인
    • 제품의 .Pak File을 Unpack하지 못하도록 한다.

Encrypt UAsset Files

  • .Pak File의 .Uasset 파일을 암호화
    • 이 파일에는 Asset의 Header 정보만 있고 Data 자체는 없다.
  • 이 Data를 암호화 하면 보안성은 향상한다.
    • 하지만 Runtime 비용이 추가되고, Data Entropy가 증가해 Patch 크기가 커질 수 있다.

Encrypt Assets

  • ,Pak File 안의 모든 Asset을 암호화
    • Runtime File I/O Performance에 영향을 준다.
    • Package Data의 Entropy를 증가시켜 Patching System 효율을 떨어트린다.

Enable Pak Signing

  • .Pak File Sign을 활성화/비활성화한다.

Contents Cooking

  • Packaging 없이 특정 Target Platform Contents만 Cooking
    • 반복 작업에서 길고 지루한 Packaging Process를 피할 수 있음
  • File -> Contents Cooking -> [Platform Name] 선택
    • Local Developer Workspace에 있는 Contents를 Update하지, Staging Directory에 Asset을 복사하는 것은 아니다.

Optimize Load Time

  • Package Process 도중 Project Load Time을 최적화 하는 메서드 제공
    • 결과적으로 Game Load Time이 감소함

Event Driven Loader 및 Asynchronous Loading Thread 사용

Event Driven Loader

  • 기본적으로 활성화 되어있는 옵션
    • Project Setting - Engine - Streaming에서 비활성화 가능
  • 대부분의 Project에서는 EDL을 사용하면 Load Time이 절반으로 줄어든다.
    • 안정적이다.
    • Unreal Engine 구버전에 대한 하위 Porting, 수정dl rksmdgkek.
    • Customized Engine Version에 맞게 조정 가능하다.

Asynchronous Loading Thread

  • 기본적으로 비활성화 되어있는 옵션
    • Project Setting - Engine - Streaming에서 활성화 가능
  • Engine을 Customize한 경우 미세조정이 필요할 수 있지만, 보통 ALT는 전반적인 Load Time을 배가시켜준다.
    • 게임의 최초 Load Time
    • 지속적인 Data Streaming Time
  • ALT는 Serialize와 Post-Loading Code를 2개의 별도 Thread에 동시에 실행시키며 동작한다.
  • 이에 따라 Game Code의 다음 항목은 반드시 Thread-safe 해야 한다.
    • UObject Class Construct
    • PostInitProperties Function
    • Serialize Function
  • ALT는 가동되면 Load 속도가 2배가 된다.

Compress .pak File

  • Project Setting -> Packaging -> Advanced Option -> Create compressed cooked packages 체크
  • 대부분의 Platform에서는 자동 압축을 제공하지 않기에 .pak File을 압축하면 Load Time이 단축된다.
  • 다만 플랫폼 별로 고려사항이 있다.

Sony PlayStation 4

  • 모든 PS4에 자동 적용되는 압축과 .pak File 압축은 중복되어도 File Size가 줄어드는 혜택이 없다.
    • 압축 작업만 중복한다.
  • 때문에 .pak File 압축은 하지 않는 것이 좋다.

Nintendo Switch

  • 압축된 File Load가 더 빠를 수 있지만, 압축을 해제하는데 시간이 더 오래 걸릴 수 있다.
  • 타이틀마다 테스트를 해보고 결정해보는 것이 좋다.

Microsoft XBoxOne

  • 가장 빠른 Load Time을 내기 위해서 매우 중요하다.

Steam

  • 사용자가 다운로드 도중에 파일을 압축하므로 다운로드 시간이 .pak File에 영향을 받지 않는다.
    • 하지만 미압축 File의 경우 Steam의 Differential Patch Steam이 더 나을 수 있다. 
  • 압축된 .pak File은 소비자의 시스템 디스크 공간을 절약해준다.
    • 하지만 Patch 할 때 다운로드 시간은 길어진다.

Oculus

  • .pak file 압축을 지원하지 않는다.

Order pak File

  • Load Time 단축을 위해서는 .pak File 순서를 잘 지정해야 한다.
    • Unreal에서는 Data Asset의 필요 순서를 알아내어 더 빠른 Loading Package를 제작하는 Tool을 제공한다.
    • 개념적으로 이 Process는 Profile Drived Optimization와 비슷하다.
  1. -fileopenlog Command-line Option으로 Package Game을 build, Launch해 Engine이 File 순서를 기록하도록 한다.
  2. 게임 주요 영역을 전부 확인하고 게임을 종료한다,
    • 모든 Level, Character, Weapon, Vehicle 등
  3. GameOpenOrder.log 파일을 /Build/WindowsNoEditor/FileOpenOrder/ Directory에 복사한다.
    • Deployed Game에서 .pak File 순서 최적화에 필요한 정보가 들어있다.
    • 예를 들어 Windows Build는 WindowsNoEditor/(YourGame)/Build/WindowsNoEditor/FileOpenOrder/ 에 있다.
  4. Log File을 배치하고 .pak File을 리빌드한다.
    • 그러면 이번에 생성된 File가 앞에 생성된 모든 ,pak File은 Log File에 나타난 순서를 사용한다.
  • Production 환경에서는 다음 사항을 반드시 해줘야 한다.
    • Log File을 Source Control에 Check-in
    • 주기적으로 -fileopenlog를 붙여 실행한 뒤 업데이트
    • 발매 준비가 되면 마지막으로 한번 더 실행

'UE5 > Utility' 카테고리의 다른 글

[Basic] Source Control  (0) 2024.05.09
[Programming Tool] LLM(Low-level Memory Tracker)  (0) 2024.05.07
[ProgrammingTool] Sparse Class Data  (0) 2024.05.07

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/using-the-low-level-memory-tracker-in-unreal-engine?application_version=5.3

 

언리얼 엔진에서 로우 레벨 메모리 트래커 사용하기 | 언리얼 엔진 5.4 문서 | Epic Developer Community

언리얼 프로젝트의 메모리 사용량을 트래킹합니다.

dev.epicgames.com

  • Unreal에서 제공하는 Memory 사용량 Tracking tool
  • Scoped-Tag System을 사용해 Unreal Engine과 OS에서 할당한 모든 Memory에 대한 Account를 유지
  • Unreal Engine에서 사용하는 모든 Platform에 대한 LLM을 지원

LLM Tracker

Default Tracker

  • Engine의 모든 Alloc에서 사용하는 High-Level Tracker
  • FMemory::Malloc을 통해 이루어진 할당을 기록
  • Stat LLM, Stat LLMFULL Console Command에 대한 통계를 제공
  • Platform Tracker의 Subset

Platform Tracker

  • OS에서 이루어진 모든 Alloc을 기록하는 Low-Level Tracker
    • BindMalloc
      • OS 단위에서 발생하는 Memory Allocation을 더 효율적으로 사용하기 위한 Wrapper
      • 큰 규모의 Memory를 OS로부터 할당 받고, Application에 작은 단위로 쪼개서 제공한다.
    • AnsiMalloc
      • 표준 C++ Library 기반 Allocator
    • TBBMalloc
      • Intel의 Thread Building Blocks 기반의 Allocator
    • JeMalloc
      • 성능 최적화가 강조된 Allocator

LLM Configuration

Command-line Argument

  • -LLM
    • LLM 활성화
  • -LLMCSV
    • 모든 값을 csv 파일에 지속적으로 기록
      • saved/profiling/llm/ 에 기록
      • csv 파일에는 현재 값(MB)을 표시하는 각 Tag에 대한 Row가 포함된다.
      • 기본적으로 5초마다 새 Row가 작성된다.
        • Console 변수인 LLM.LLMWriteInterval을 사용하여 주기를 변경할 수 있다.
    • -LLM이 자동으로 활성화
  • -llmtagsets=Assets
    • 각 Asset별 할당된 합계를 표시
    • 5.3 기준 실험 기능
  • -llmTagSets=ASsetClasses
    • 각 UObject Class Type의 합계를 표시
    • 5.3 기준 실험 기능

Console Command

  • stat LLM
    • LLM 요약을 표시
    • 모든 Low-Level Engine 통계는 단일 통계로 그룹화 된다.
  • stat LLMFULL
    • 모든 LLM 통계를 표시
  • stat LLMPlatform
    • OS에서 할당된 모든 Memory에 대한 통계를 표시
  • stat LLMOverhead
    • LLM에서 내부적으로 사용하는 Memory 표시

LLM Tag

  • Engine과 Game Code에 의해 이루어진 모든 Memory Allocation에는 속한 Category를 식별하는 Tag가 할당된다.
    • 즉, Memory는 한번만 Tracking 되고, 누락되지 않는 한 두 번 계산되지 않는다.
    • 모든 Category의 합계가 Game에서 사용하는 총 Memory에 추가 된다.
  • Tag는 Tag Scope Macro를 사용하여 적용된다.
    • 해당 Tag 범위 내에서 이루어진 모든 Allocation은 지정된 Tag가 제겅된다.
  • LLM은 Tag Scope Stack을 유지/관리하고, Allocation에 최상위 Tag를 적용한다.

Engine에서 제공하는 Tag Category

UObject

  • UObject에서 상속된 모든 Class와 Property를 포함하여 해당 Class에 의해 Serialize 된 모든 것을 포함
    • UObject는 어떤 Category에서도 Tracking 되지 않는 모든 Engine/Game Memory에 대한 포괄적 Tag
  • 별도로 Tracking되는 Mesh, Animation Data는 포함되지 않는다.
  • Level에 배치된 Object의 수에 대응

EngineMisc

  • 다른 Category에서 Tracking되지 않는 모든 Low-Level Memory

TaskGraphTasksMisc

  • 자체 Category가 없는 Task Graph에서 시작되는 모든 Task
  • 보통은 상당히 낮아야 한다.

StaticMesh

  • UStaticMesh Class 및 관련 Property
  • 실제 Mesh Data를 포함하지는 않는다.

Custom Tag

  • Memory Profiling 할 때 Memory Insight에서 LLM Untracked Tag가 지정된 Allocated Memory가 있을 수 있다.
    • 이 때 LLM_DECLARE_TAG, LLM_DEFINE_TAG 매크로를 이용해 Custom Tag를 만들어 대응한다.
  • 이 매크로들은 Engine File을 수정하지 않고 Game Modue이나 Plugin에서 수행할 수 있다.
  • 자세한 구현은 Runtime/Core/Public/HAL/LowLevelMemTracker.h에서 확인할 수 있다.

LLM_DECLARE_TAG

  • 더보기
    /**
    * Declare a tag defined by LLM_DEFINE_TAG. It is used in LLM_SCOPE_BYTAG or referenced by name in other LLM_SCOPEs.
    * @param UniqueName - The name of the tag for looking up by name, must be unique across all tags passed to
    *        LLM_DEFINE_TAG, LLM_SCOPE, or ELLMTag.
    * @param ModuleName - (Optional, only in LLM_DECLARE_TAG_API) - The MODULENAME_API symbol to use to declare module
    *        linkage, aka ENGINE_API. If omitted, no module linkage will be used and the tag will create link errors if
    *        used from another module.
    */
    #define LLM_DECLARE_TAG(UniqueNameWithUnderscores) extern FLLMTagDeclaration PREPROCESSOR_JOIN(LLMTagDeclaration_, UniqueNameWithUnderscores)
    #define LLM_DECLARE_TAG_API(UniqueNameWithUnderscores, ModuleAPI) extern ModuleAPI FLLMTagDeclaration PREPROCESSOR_JOIN(LLMTagDeclaration_, UniqueNameWithUnderscores)

LLM_DEFINE_TAG

  • 더보기
    /**
     * Define a tag which can be used in LLM_SCOPE_BYTAG or referenced by name in other LLM_SCOPEs.
     * @param UniqueNameWithUnderscores - Modified version of the name of the tag. Used for looking up by name,
     *        must be unique across all tags passed to LLM_DEFINE_TAG, LLM_SCOPE, or ELLMTag.
     *        The modification: the usual separator / for parents must be replaced with _ in LLM_DEFINE_TAGs.
     * @param DisplayName - (Optional) - The name to display when tracing the tag; joined with "/" to the name of its
     *        parent if it has a parent, or NAME_None to use the UniqueName.
     * @param ParentTagName - (Optional) - The unique name of the parent tag, or NAME_None if it has no parent.
     * @param StatName - (Optional) - The name of the stat to populate with this tag's amount when publishing LLM data each
     *        frame, or NAME_None if no stat should be populated.
     * @param SummaryStatName - (Optional) - The name of the stat group to add on this tag's amount when publishing LLM
     *        data each frame, or NAME_None if no stat group should be added to.
     */
    #define LLM_DEFINE_TAG(UniqueNameWithUnderscores, ...) FLLMTagDeclaration PREPROCESSOR_JOIN(LLMTagDeclaration_, UniqueNameWithUnderscores)(TEXT(#UniqueNameWithUnderscores), ##__VA_ARGS__)

LLM_SCOPE_BYTAG

  • 더보기
    #define LLM_SCOPE_BYTAG(TagDeclName) 
    FLLMScope SCOPE_NAME(PREPROCESSOR_JOIN(LLMTagDeclaration_, TagDeclName).GetUniqueName(), false /* bIsStatTag */, ELLMTagSet::None, ELLMTracker::Default);\
    UE_MEMSCOPE(PREPROCESSOR_JOIN(LLMTagDeclaration_,TagDeclName).GetUniqueName());

Custom Tag macro 사용 방법

  1. LLM_DELCARE_TAG를 사용해 header File에 Custom LLM Tag를 선언
  2. 관련 .cpp File에서 LLM_DEFINE_TAG를 사용해 Custom LLM Tag를 정의
  3. LLM_SCOPE_BYTAG를 사용해 .cpp 파일의 Memory Tracking

Code 예시

  • CustomTagExample.h
    • 더보기
      #pragma once
      ...
      LLM_DECLARE_TAG(MyTestTag);
  • CustomTagExample.cpp
    • 더보기
      LLM_DEFINE_TAG(MyTestTag);
      
      AMyActor::AMyActor()
      {
          LLM_SCOPE_BYTAG(MyTestTag);
          MyLargeBuffer.Reset(new uint8[1024*1024*1024]);
      }

Tag Set

  • 실험 단계
  • LowLevelMemTracker.h안의 LLM_ALLOW_SSETS_TAGS를 정의하여 사용
  • Tag Set을 사용할 때 각 Allocation은 Asset이나 Object Class 이름을 추가로 저장한다.
  • Memory 사용량과 Runtime Performance에 모두 오버헤드가 추가된다.

Technical Detail Implementation

  • LLM은 Pointer를 Index로 하여 모든 Allocation의 Map을 유지하며 동작한다.
    • 이 Map은 각 Allocation의 크기와 할당된 Tag를 포함한다.
  • Game에는 동시에 최대 400만개의 Live Allocation을 가질 수 있다.
    • 때문에 Memory Overhead를 가능한 작게 유지하는 것이 중요하다.
  • 현재 구현에서는 Allocation 하나 당 21Byte를 사용한다.
    • Allocation Size
      Pointer 8 Byte
      Pointer Hash Key 4 Byte
      Size 4 Byte
      Tag 1 Byte
      Hash Map Index 4Byte
  • OnLowLevelAlloc 함수를 사용하여 Allocation을 Tracking하면
    Tack Stack 제일 위에 있는 Tag가 현재 Tag가 되고, Pointer를 Key로 사용해 그 Allocation이 Map에 저장이 된다.
  • Conflict를 피하기 위해 각 Tag의 Frame Delta는 별도의 FLLMThreadState Class Instance에서 Tracking 된다.
  • Frame이 끝나면 이 Delta를 합산해 통계 시스템과 .csv File에 게시한다.
  • LLM은 초기에 Initialize되므로 Default로 활성화 해야 한다.
    • Command-line에서 LLM이 활성화 되어있지 않으면 자동으로 종료가 된다.
    • 동시에 모든 Memory가 종료되어 Overhead가 발생하지 않는다.
  • LLM은 Test 및 Shipping Build에서 완전히 Compile 된다.
  • LLM은 통계 시스템 없이 실행할 수 있다.
    • 이 경우, 화면에 통계를 표시할 수는 없지만 여전히 .csv File에는 기록이 된다.
    • 이를 위해서는 LowLevelMemTracker.h에서 ENABLE_LOW_LEVEL_MEM_TRACKER를 수정해
      LLM을 활성화 해야 한다.
  • Tag는 Scope Macro를 사용하여 적용하는데, 이에는 2가지가 제공된다.
    • LLM_SCOPE
      • Default Tracker Scope 설정
    • LLM_PLATFORM_SCOPE
      • Platform Tracker Scope 설정
    • LLM_SCOPED_TAG_WITH_STAT
      • 위와 같이 통계를 사용하는 Scope Macro는 Deprecated 되었기에 사용하면 안된다.
  • LLM 내부에서 사용하는 모든 Memory는 Platform에서 제공하는 LLMAlloc/LLMFree 함수로 관리한다.
    • 때문에 LLM이 자체 Memory 사용량을 Tracking 하지 않도록,
      그리고 무한  Loop에 빠지지 않도록 다른 방식으로 할당하지 않는 것이 중요하다.

Additional Technical Detail

  • LLM의 Overhead는 100MB 이상이 될 수 있으므로 Console에서는 대용량 Memory Load를 지양하는 것이 좋다.
  • Test Setting에서의 LLM은 화면에 통계 페이지를 표시하지 않는다.
    • 하지만 .csv File에는 통계를 작성한다.
    • LLM은 Shipping에서 완전히 비활성화가 된다.
  • Asset Tag Tracking은 아직 초기 실험단계이다.

'UE5 > Utility' 카테고리의 다른 글

[Basic] Source Control  (0) 2024.05.09
[Basic] Packaging  (0) 2024.05.09
[ProgrammingTool] Sparse Class Data  (0) 2024.05.07

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/sparse-class-data-in-unreal-engine?application_version=5.3

  • 자주 사용되는 Actor Type의 주복 데이터를 제거하여 Memory를 절약
  • 제공하는 기능
    • Memory에 Property의 사본을 단 하나만 존재하도록 유지
    • Designer가 BP Graph에서 Property 값에 Access하고 Class Default에서 편집 가능
  • 사용을 권장하는 경우
    • In-Game에서 동시에 여러 개의 Instance를 가지는 Actor Class의 Property
      • 상당한 Memory가 중복된 사본에 사용되고 있음을 의미한다.
    • 배치된 Actor Instance를 변경하지 않는 Property
      • Actor Instance가 값을 override하지 않아 EditInstanceOnly나 EditAnywhere 지정자가 필요하지 않는 경우
    • C++ 코드로 수정되지 않는 Property
      • 직접 Access하는 모든 C++ 코드는 접근자 함수에 대한 호출로 대체 되어야 한다.
  • Sparse Class Data를 사용하기 위해서는 Native C++ 코드가 필요하다.

예제

  • 더보기
    USTRUCT(BlueprintType)
    struct FMySparseClassData
    {
        GENERATED_BODY()
    
        FMySparseClassData() 
        : MyFloatProperty(0.f)
        , MyIntProperty(0)
        , MyNameProperty(NAME_None)
        { }
    
        // 에디터에서 이 프로퍼티의 디폴트값을 세팅할 수 있습니다.
        // 블루프린트 그래프에서는 액세스할 수 없습니다.
        UPROPERTY(EditDefaultsOnly)
        float MyFloatProperty;
    
        // 이 프로퍼티의 값은 C++ 코드로 설정됩니다.
        // 블루프린트 그래프에서 액세스할 수 있습니다(변경은 불가능함).
        UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
        int32 MyIntProperty;
    
        // 에디터에서 이 프로퍼티의 디폴트값을 세팅할 수 있습니다.
        // 블루프린트 그래프에서 액세스할 수 있습니다(변경은 불가능함).
        // 'GetByRef'는 블루프린트 그래프가 사본 대신 const 참조에 액세스함을 의미합니다.
        UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta=(GetByRef))
        FName MyNameProperty;
    };
    • EditAnywhere, EditInstanceOnly, BlueprintReadWrite가 Tag된 Property 대상으로는 작업할 수 없다
    • Sparse Class Data를 포함하는 Struct는 USTRUCT(BlueprintType) 지정자가 선언되어 있어야 한다.
      • 각 Property는 반드시 EditDefaultsOnly 지정자를 포함해야 한다.
      • 별도의 Category가 없는 경우, Default인 'My Sparse Class Data'를 받게 된다.
  • 더보기
    UCLASS(BlueprintType, SparseClassDataTypes = MySparseClassData)
    class AMyActor : public AActor
    {
        GENERATED_BODY()
    
    #if WITH_EDITOR
    public:
        // ~ 이 함수는 기존 데이터를 FMySparseClassData로 전송합니다.
        virtual void MoveDataToSparseClassDataStruct() const override;
    #endif // WITH_EDITOR
    
    #if WITH_EDITORONLY_DATA
        //~ 이 프로퍼티는 FMySparseClassData 구조체로 이동합니다.
    private:
        // 이 프로퍼티는 에디터에서 변경할 수 있지만, 클래스별로만 변경됩니다.
        // 블루프린트 그래프에서는 액세스하거나 변경할 수 없습니다.
        // 희소 클래스 데이터의 잠재적 후보입니다.
        UPROPERTY()
        float MyFloatProperty_DEPRECATED;
    
        // 이 프로퍼티는 에디터에서 변경할 수 없습니다.
        // 블루프린트 그래프에서 액세스할 수 있지만, 변경할 수는 없습니다.
        // 희소 클래스 데이터의 잠재적 후보입니다.
        UPROPERTY()
        int32 MyIntProperty_DEPRECATED;
    
        // 이 프로퍼티는 에디터에서 클래스별로 편집할 수 있습니다.
        // 블루프린트 그래프에서 액세스할 수 있지만, 변경할 수는 없습니다.
        // 희소 클래스 데이터의 잠재적 후보입니다.
        UPROPERTY()
        FName MyNameProperty_DEPRECATED;
    #endif // WITH_EDITORONLY_DATA
    
        //~ 나머지 프로퍼티는 인스턴스별로 변경할 수 있습니다.
        //~ 따라서 나머지 프로퍼티는 희소 클래스 데이터 구현에서 제외됩니다.
    public:
        // 이 프로퍼티는 배치된 MyActor 인스턴스에서 편집할 수 있습니다.
        // 희소 클래스 데이터의 잠재적 후보가 아닙니다.
        UPROPERTY(EditAnywhere)
        FVector MyVectorProperty;
    
        // 이 프로퍼티는 블루프린트 그래프에서 변경할 수 있습니다.
        // 희소 클래스 데이터의 잠재적 후보가 아닙니다.
        UPROPERTY(BlueprintReadWrite)
        FVector2D MyVector2DProperty;
    };
    • UCLASS(SparseClassDataTypes = [SPARSE_CLASS_DATA_TYPE])
      • 이 선언을 통해 이 Class에서 어느 Sparse Class Data를 사용할지 연결한다.
      • Class당 1개의 Sparse Class Data만 지정할 수 있다.
        • 여러개가 필요한 경우 이를 하나로 묶은 Wrapper를 Sparse Class Data로 지정해야 한다.
      • Sparse Class Data를 지정하면 UBT가 자동으로 Get[SPARSE_CLASS_DATA_TYPE] 함수를 생성해준다.
    • 다음은 기존 변수를 무난하게 Sparse Class Data로 변환하며 값을 설정하기 위한 세팅이다.
      • Sparce Class Data로 이동하는 Property 주변을 #if WITH_EDITORONLY_DATA Precomiler 처리
      • 원본 Class의 property의 변수명에 _DEPRECATED 접미사 추가
        • _DEPRECATED 접미사가 붙은 변수는 Unreal에서 인지하고 사용 시 warning을 발생시켜준다.
      • 원본 Class의 UPROPERTY에 모든 지정자를 제거하고 접근자를 private로 세팅
      • Editor Build(#if WITH_EDITOR) 환경에서 MoveDataToSparseClassDataStruct 함수 override
        • 원본 Class에서 Sparse Class Data로 1회성 복사를 수행해 기존 값을 보존
  • 더보기
    #include "Engine/BlueprintGeneratedClass.h"
    
    #if WITH_EDITOR
    void AMyActor::MoveDataToSparseClassDataStruct() const
    {
        // 이미 저장된 희소 데이터를 덮어쓰지 않도록 하세요.
        UBlueprintGeneratedClass* BPClass = Cast<UBlueprintGeneratedClass>(GetClass());
        if (BPClass == nullptr || BPClass->bIsSparseClassDataSerializable == true)
        {
            return;
        }
    
        Super::MoveDataToSparseClassDataStruct();
    
    
        #if WITH_EDITORONLY_DATA
        // 언리얼 헤더 툴(Unreal Header Tool, UHT)은 GetMySparseClassData를 자동 생성합니다.
        FMySparseClassData* SparseClassData = GetMySparseClassData();
    
        // 이 줄을 수정하여 모든 희소 클래스 데이터 프로퍼티를 포함합니다.
        SparseClassData->MyFloatProperty = MyFloatProperty_DEPRECATED;
        SparseClassData->MyIntProperty = MyIntProperty_DEPRECATED;
        SparseClassData->MyNameProperty = MyNameProperty_DEPRECATED;
        #endif // WITH_EDITORONLY_DATA
    }
    #endif // WITH_EDITOR
  • Sparse Class Data의 영향을 받은 Property를 Editor에서 편집/Access 하는 사용자는 차이를 느끼지 못한다.
  • Memory 사용량은 줄어든다.
  • Property가 C++에서 Reference되는 경우, Getter 함수 호출로 모두 대체 되어야 한다.
    • 예를 들어 SparceClassData::MyFloatPropertyData에 접근하려면 GetMyFloatProperty를 대신 호출해야 한다.
    • 다행히도 이러한 함수는 UHT가 자동으로 생성한다.
  • 해당 PROPERTY에 NoGetter MetaData 지정자를 선언하면 Getter 함수 생성을 방지할 수 있다.
  • 해당 PROPERTY에 값 대신 상수 참조로 Access 하고 싶은 경우 GetByRef MetaData 지정자를 사용해야 한다.

'UE5 > Utility' 카테고리의 다른 글

[Basic] Source Control  (0) 2024.05.09
[Basic] Packaging  (0) 2024.05.09
[Programming Tool] LLM(Low-level Memory Tracker)  (0) 2024.05.07

+ Recent posts