본문 바로가기

게임프로그래밍/실습2

[실습2] 25. PostGameplayEffectExecute() - 어트리뷰트 변경 후 호출되는 함수

1. PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)

 

PostGameplayEffectExecute

 

위의 함수는 게임플레이 이펙트가 적용되고 난 후 추가적인 작업을 할 때 사용하는 함수이다.

 

예를 들어 체력이 0이 되면 캐릭터를 데스 상태로 변환하는 등의 작업을 할 수 있다.


매개변수 FGameplayEffectModCallbackData

  • EvaluatedData: 변경된 Attribute와 그 값.
  • Target: 효과를 적용받는 대상.
  • Source: 효과를 유발한 원인(예: 공격자).
  • EffectSpec: 실행된 GameplayEffect의 상세 정보
  • PostGameplayEffectExecute 함수의 매개변수로 전달되는 FGameplayEffectModCallbackData는 다음과 같은 정보를 포함합니다:

void UAuraAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
    Super::PostGameplayEffectExecute(Data);

    if (Data.EvaluatedData.Attribute == GetHealthAttribute())
    {
       
    }
    
}

 

이런 식으로 변경된 값이 무엇인지 보고 추가 작업을 할 수 있는 것이다.

 

또 다른 작업을 해보자

void UAuraAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
    Super::PostGameplayEffectExecute(Data);

    // Source = 이펙트를 일으킨 유발자, Target = 이펙트를 받는 대상 (이 Attribute Set의 주인)
    
    const FGameplayEffectContextHandle EffectContextHandle = Data.EffectSpec.GetContext();
    const UAbilitySystemComponent* SourceASC = EffectContextHandle.GetOriginalInstigatorAbilitySystemComponent();

    if (IsValid(SourceASC) && SourceASC->AbilityActorInfo.IsValid() && SourceASC->AbilityActorInfo->AvatarActor.IsValid())
    {
       AActor* SourceAvatarActor = SourceASC->AbilityActorInfo->AvatarActor.Get();
       const AController* SourceController = SourceASC->AbilityActorInfo->PlayerController.Get();
       if (SourceController == nullptr && SourceAvatarActor != nullptr)
       {
          if (APawn* Pawn = Cast<APawn>(SourceAvatarActor))
          {
             SourceController = Pawn->GetController();
          }
       }
       if (SourceController)
       {
          ACharacter* SourceCharacter = Cast<ACharacter>(SourceController->GetPawn());
       }
    }

    if (Data.Target.AbilityActorInfo.IsValid() && Data.Target.AbilityActorInfo->AvatarActor.IsValid())
    {
       AActor* TargetAvatarActor = Data.Target.AbilityActorInfo->AvatarActor.Get();
       AController* TargetController = Data.Target.AbilityActorInfo->PlayerController.Get();
       ACharacter* TargetCharacter = Cast<ACharacter>(TargetActor);
       const UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(TargetActor);
    }
}

 

위의 구문은 두 단계로 나누어져 있는데 첫번째 단계에서는 소스의 컨트롤러와 아바타액터, 캐릭터 클래스 등을 구하는 것이고,

두번째 단계에서는 타겟의 액터와 컨트롤러, ASC 등을 구하는 중이다.

 

그리고 이러한 값들을 저장하면 나중에 다양하게 쓰일 수 있다. 단순히 변수로 저장해도 되지만 쉽게 모든 값을 다룰 수 있게 구조체로 만들어서 저장하려고 한다.


2. 정보들 구조체로 저장하기

 

어트리뷰트 세트의 헤더파일로 가서 구조체를 정의하자.

 

USTRUCT()
struct FEffectProperties
{
    GENERATED_BODY()

    FEffectProperties() {}

    FGameplayEffectContextHandle EffectContextHandle;

    UPROPERTY()
    UAbilitySystemComponent* SourceASC = nullptr;

    UPROPERTY()
    AActor* SourceAvatarActor = nullptr;

    UPROPERTY()
    AController* SourceController = nullptr;

    UPROPERTY()
    ACharacter* SourceCharacter = nullptr;

    UPROPERTY()
    UAbilitySystemComponent* TargetASC = nullptr;

    UPROPERTY()
    AActor* TargetAvatarActor = nullptr;

    UPROPERTY()
    AController* TargetController = nullptr;

    UPROPERTY()
    ACharacter* TargetCharacter = nullptr;
    
};

 

 

이제 이 구조체를 채우는 함수를 만들면 된다.

 

void SetEffectProperties(const FGameplayEffectModCallbackData& Data, FEffectProperties& Props);

 

해당 함수 내부에는 위에서 작성했던 로직을 그래로 붙여넣고 지역변수로 저장한 값을 구조체에 할당해주기만 하면 된다.

 

void UAuraAttributeSet::SetEffectProperties(const FGameplayEffectModCallbackData& Data, FEffectProperties& Props) const
{
    // Source = 이펙트를 일으킨 유발자, Target = 이펙트를 받는 대상 (이 Attribute Set의 주인)
    
    Props.EffectContextHandle = Data.EffectSpec.GetContext();
    Props.SourceASC = Props.EffectContextHandle.GetOriginalInstigatorAbilitySystemComponent();

    if (IsValid(Props.SourceASC) && Props.SourceASC->AbilityActorInfo.IsValid() && Props.SourceASC->AbilityActorInfo->AvatarActor.IsValid())
    {
       Props.SourceAvatarActor = Props.SourceASC->AbilityActorInfo->AvatarActor.Get();
       Props.SourceController = Props.SourceASC->AbilityActorInfo->PlayerController.Get();
       if (Props.SourceController == nullptr && Props.SourceAvatarActor != nullptr)
       {
          if (APawn* Pawn = Cast<APawn>(Props.SourceAvatarActor))
          {
             Props.SourceController = Pawn->GetController();
          }
       }
       if (Props.SourceController)
       {
          Props.SourceCharacter = Cast<ACharacter>(Props.SourceController->GetPawn());
       }
    }

    if (Data.Target.AbilityActorInfo.IsValid() && Data.Target.AbilityActorInfo->AvatarActor.IsValid())
    {
       Props.TargetAvatarActor = Data.Target.AbilityActorInfo->AvatarActor.Get();
       Props.TargetController = Data.Target.AbilityActorInfo->PlayerController.Get();
       Props.TargetCharacter = Cast<ACharacter>(Props.TargetAvatarActor);
       Props.TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(Props.TargetAvatarActor);
    }
}

 

이제 다시 아래 함수를 재정의하면 된다.

 

void UAuraAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
    Super::PostGameplayEffectExecute(Data);
    FEffectProperties Props;
    SetEffectProperties(Data, Props);
}

 

이렇게 해서 정보에 손 쉽게 접근이 가능해졌다. 이는 추후 전투 기능을 만들때 유용하게 쓰일 것이라고 한다.