보키_기록용
[GameplayAbilitySystem] Ability 써서 체력 닳게 하기 (2) 본문
- WaitSuccessFailEvent
원래는 엔진에 있던 Wait Gamplay Event 썼는데 결과 값에서 적이 맞든 안맞든 같은 결과가 나와서 Custom함. 따라서 기본 골자는 Wait Gamplay Event를 따라간다.
#include "CoreMinimal.h"
#include "Abilities/Tasks/AbilityTask.h"
#include "AbilityTask_SuccessFailEvent.generated.h"
class UAbilitySystemComponent;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FWaitSuccessFailEventDelegate, FGameplayEventData, Payload);
/**
* Custom Ability Task which uses a Success or Failed tag ro decide between two branches.
*/
UCLASS()
class GAMEPLAYABILITYTEST_API UAbilityTask_SuccessFailEvent : public UAbilityTask
{
GENERATED_UCLASS_BODY()
UPROPERTY(BlueprintAssignable)
FWaitSuccessFailEventDelegate SuccessEventReceived;
UPROPERTY(BlueprintAssignable)
FWaitSuccessFailEventDelegate FailedEventReceived;
/**
* Wait until the specified gameplay tag event is triggered. By default this will look at the owner of this ability. OptionalExternalTarget can be set to make this look at another actor's tags for changes
* It will keep listening as long as OnlyTriggerOnce = false
* If OnlyMatchExact = false it will trigger for nested tags
*/
UFUNCTION(BlueprintCallable, Category = "Ability|Tasks",
meta = (HidePin = "OwningAbility", DefaultToSelf = "OwningAbility", BlueprintInternalUseOnly = "TRUE"))
static UAbilityTask_SuccessFailEvent* WaitSuccessFailEvent(UGameplayAbility* OwningAbility, FGameplayTag SuccessTag, FGameplayTag FailedTag,
AActor* OptionalExternalTarget = nullptr, bool OnlyTriggerOnce = false, bool OnlyMatchExact = true);
void SetExternalTarget(AActor* Actor);
UAbilitySystemComponent* GetTargetASC() const;
virtual void Activate() override;
virtual void SuccessEventCallback(const FGameplayEventData* Payload);
virtual void FailedEventCallback(const FGameplayEventData* Payload);
virtual void SuccessEventContainerCallback(FGameplayTag MatchingTag, const FGameplayEventData* Payload);
virtual void FailedEventContainerCallback(FGameplayTag MatchingTag, const FGameplayEventData* Payload);
void OnDestroy(bool AbilityEnding) override;
FGameplayTag SuccessTag;
FGameplayTag FailedTag;
UPROPERTY()
UAbilitySystemComponent* OptionalExternalTarget;
bool UseExternalTarget;
bool OnlyTriggerOnce;
bool OnlyMatchExact;
FDelegateHandle SuccessHandle;
FDelegateHandle FailedHandle;
};
#include "Core/Ability/AbilityTask_SuccessFailEvent.h"
#include "AbilitySystemGlobals.h"
#include "AbilitySystemComponent.h"
UAbilityTask_SuccessFailEvent::UAbilityTask_SuccessFailEvent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
UAbilityTask_SuccessFailEvent* UAbilityTask_SuccessFailEvent::WaitSuccessFailEvent(UGameplayAbility* OwningAbility,
FGameplayTag SuccessTag, FGameplayTag FailedTag, AActor* OptionalExternalTarget, bool OnlyTriggerOnce,
bool OnlyMatchExact)
{
UAbilityTask_SuccessFailEvent* MyObj = NewAbilityTask<UAbilityTask_SuccessFailEvent>(OwningAbility);
MyObj->SuccessTag = SuccessTag;
MyObj->FailedTag = FailedTag;
MyObj->SetExternalTarget(OptionalExternalTarget);
MyObj->OnlyTriggerOnce = OnlyTriggerOnce;
MyObj->OnlyMatchExact = OnlyMatchExact;
return MyObj;
}
void UAbilityTask_SuccessFailEvent::SetExternalTarget(AActor* Actor)
{
if (Actor)
{
UseExternalTarget = true;
OptionalExternalTarget = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(Actor);
}
}
UAbilitySystemComponent* UAbilityTask_SuccessFailEvent::GetTargetASC() const
{
if (UseExternalTarget)
{
return OptionalExternalTarget;
}
return AbilitySystemComponent;
}
void UAbilityTask_SuccessFailEvent::Activate()
{
UAbilitySystemComponent* ASC = GetTargetASC();
if (ASC)
{
if (OnlyMatchExact)
{
SuccessHandle = ASC->GenericGameplayEventCallbacks.FindOrAdd(SuccessTag).AddUObject(this, &UAbilityTask_SuccessFailEvent::SuccessEventCallback);
FailedHandle = ASC->GenericGameplayEventCallbacks.FindOrAdd(FailedTag).AddUObject(this, &UAbilityTask_SuccessFailEvent::FailedEventCallback);
}
else
{
SuccessHandle = ASC->AddGameplayEventTagContainerDelegate(FGameplayTagContainer(SuccessTag), FGameplayEventTagMulticastDelegate::FDelegate::CreateUObject(this, &UAbilityTask_SuccessFailEvent::SuccessEventContainerCallback));
FailedHandle = ASC->AddGameplayEventTagContainerDelegate(FGameplayTagContainer(FailedTag), FGameplayEventTagMulticastDelegate::FDelegate::CreateUObject(this, &UAbilityTask_SuccessFailEvent::FailedEventContainerCallback));
}
}
Super::Activate();
}
void UAbilityTask_SuccessFailEvent::SuccessEventCallback(const FGameplayEventData* Payload)
{
SuccessEventContainerCallback(SuccessTag, Payload);
}
void UAbilityTask_SuccessFailEvent::FailedEventCallback(const FGameplayEventData* Payload)
{
FailedEventContainerCallback(FailedTag, Payload);
}
void UAbilityTask_SuccessFailEvent::SuccessEventContainerCallback(FGameplayTag MatchingTag,
const FGameplayEventData* Payload)
{
if (ShouldBroadcastAbilityTaskDelegates())
{
FGameplayEventData TempPayload = *Payload;
TempPayload.EventTag = MatchingTag;
SuccessEventReceived.Broadcast(MoveTemp(TempPayload));
}
if (OnlyTriggerOnce)
{
EndTask();
}
}
void UAbilityTask_SuccessFailEvent::FailedEventContainerCallback(FGameplayTag MatchingTag,
const FGameplayEventData* Payload)
{
if (ShouldBroadcastAbilityTaskDelegates())
{
FGameplayEventData TempPayload = *Payload;
TempPayload.EventTag = MatchingTag;
FailedEventReceived.Broadcast(MoveTemp(TempPayload));
}
if (OnlyTriggerOnce)
{
EndTask();
}
}
void UAbilityTask_SuccessFailEvent::OnDestroy(bool AbilityEnding)
{
UAbilitySystemComponent* ASC = GetTargetASC();
if (ASC && SuccessHandle.IsValid())
{
if (OnlyMatchExact)
{
ASC->GenericGameplayEventCallbacks.FindOrAdd(SuccessTag).Remove(SuccessHandle);
}
else
{
ASC->RemoveGameplayEventTagContainerDelegate(FGameplayTagContainer(SuccessTag), SuccessHandle);
}
}
if (ASC && FailedHandle.IsValid())
{
if (OnlyMatchExact)
{
ASC->GenericGameplayEventCallbacks.FindOrAdd(FailedTag).Remove(FailedHandle);
}
else
{
ASC->RemoveGameplayEventTagContainerDelegate(FGameplayTagContainer(FailedTag), FailedHandle);
}
}
Super::OnDestroy(AbilityEnding);
}
보면 알겠지만 Wait Gamplay Event에서 복붙하고 Success랑 Fail 추가함.
분석 관련 글은 추후 추가
- 캐릭터에서 HandlePunch 만들기
void AGASTestCharacter::HandlePunch()
{
TArray<AActor*> OverlappingActors;
AGASTestCharacter* OverlapCharacter{ nullptr };
AActor* InstigatorActor{ GetInstigator() };
FGameplayEventData Payload;
// 공격 범위 캡슐 컴포넌트에 Overlap한 캐릭터 리스트뽑기
if (AttackComponent)
{
AttackComponent->GetOverlappingActors(OverlappingActors, AGASTestCharacter::StaticClass());
}
//Overlap한 캐릭터가 있으면
if(OverlappingActors.Num() > 0)
{
for(AActor* OverlapActor : OverlappingActors)
{
OverlapCharacter = Cast<AGASTestCharacter>(OverlapActor);
if(OverlapCharacter)
{
// 공격 범위 안 캐릭터의 체력 로그 찍기
GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Green, FString::SanitizeFloat(OverlapCharacter->GetHealth()));
}
if(InstigatorActor && OverlapActor)
{
Payload.Instigator = InstigatorActor;
Payload.Target = OverlapActor;
Payload.TargetData = UAbilitySystemBlueprintLibrary::AbilityTargetDataFromActor(OverlapActor);
// 성공 시 GA_Punch > WaitSuccessFailEvent > Success Event Received로감
UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(InstigatorActor, FGameplayTag::RequestGameplayTag(TEXT("Weapon.Hit")), Payload);
}
}
}
else
{
// 실패 시 (공격 범위 안에 캐릭터가 없음) GA_Punch > WaitSuccessFailEvent > Failed Event Received로감
UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(InstigatorActor, FGameplayTag::RequestGameplayTag(TEXT("Weapon.NoHit")), Payload);
}
}
- Gameplay Effect 블루프린트 생성 (GE_Damage)
체력 어트리뷰트에 -35만큼의 체력을 더한다는 느낌.
- 결과
적캐릭터(얘도 GE_CharacterDefaults 입력된 상태) Punch 입력 키 누르면 해당 적캐릭터의 Health값이 까임.
이후에 체력이 0이되면 죽는 모션을 넣는다거나하면 될듯.
Gameplay Ability System 처음엔 이해하기 어려웠는데 쓰다보니 간단한거같다.
'언리얼 > Framework' 카테고리의 다른 글
[GameplayAbilitySystem] DOREPLIFETIME_CONDITION_NOTIFY에 대해 (Prediction) (0) | 2022.09.01 |
---|---|
[GameplayAbilitySystem] 정리 (0) | 2022.08.30 |
[GameplayAbilitySystem] Ability C++로 만들기 (0) | 2022.08.30 |
[GameplayAbilitySystem] Ability 써서 체력 닳게 하기 (1) (0) | 2022.08.26 |
[GameplayAbilitySystem] 시작하기 (0) | 2022.08.24 |
Comments