第一次提交
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "AbilityTasks/GGA_AbilityTask_NetworkSyncPoint.h"
|
||||
#include "Engine/World.h"
|
||||
#include "TimerManager.h"
|
||||
#include "AbilitySystemComponent.h"
|
||||
|
||||
UGGA_AbilityTask_NetworkSyncPoint* UGGA_AbilityTask_NetworkSyncPoint::WaitNetSyncWithTimeout(UGameplayAbility* OwningAbility, EAbilityTaskNetSyncType InSyncType, float InTimeout)
|
||||
{
|
||||
UGGA_AbilityTask_NetworkSyncPoint* MyObj = NewAbilityTask<UGGA_AbilityTask_NetworkSyncPoint>(OwningAbility);
|
||||
MyObj->SyncType = InSyncType;
|
||||
MyObj->Time = InTimeout;
|
||||
return MyObj;
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_NetworkSyncPoint::Activate()
|
||||
{
|
||||
Super::Activate();
|
||||
if (TaskState != EGameplayTaskState::Finished && AbilitySystemComponent.IsValid())
|
||||
{
|
||||
UWorld* World = GetWorld();
|
||||
TimeStarted = World->GetTimeSeconds();
|
||||
if (Time <= 0.0f)
|
||||
{
|
||||
World->GetTimerManager().SetTimerForNextTick(this, &ThisClass::OnTimeFinish);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use a dummy timer handle as we don't need to store it for later but we don't need to look for something to clear
|
||||
FTimerHandle TimerHandle;
|
||||
World->GetTimerManager().SetTimer(TimerHandle, this, &ThisClass::OnTimeFinish, Time, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_NetworkSyncPoint::OnTimeFinish()
|
||||
{
|
||||
if (ShouldBroadcastAbilityTaskDelegates())
|
||||
{
|
||||
SyncFinished();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "AbilityTasks/GGA_AbilityTask_PlayMontageAndWaitForEvent.h"
|
||||
#include "AbilitySystemComponent.h"
|
||||
#include "AbilitySystemGlobals.h"
|
||||
#include "GGA_LogChannels.h"
|
||||
#include "Animation/AnimMontage.h"
|
||||
#include "Animation/AnimInstance.h"
|
||||
#include "GameFramework/Character.h"
|
||||
|
||||
static bool GUseAggressivePlayMontageAndWaitEndTask = true;
|
||||
static FAutoConsoleVariableRef CVarAggressivePlayMontageAndWaitEndTask(
|
||||
TEXT("GGA.PlayMontage.AggressiveEndTask"), GUseAggressivePlayMontageAndWaitEndTask,
|
||||
TEXT("This should be set to true in order to avoid multiple callbacks off an GGA_AbilityTask_PlayMontageAndWaitForEvent node"));
|
||||
|
||||
static bool GPlayMontageAndWaitFireInterruptOnAnimEndInterrupt = true;
|
||||
static FAutoConsoleVariableRef CVarPlayMontageAndWaitFireInterruptOnAnimEndInterrupt(
|
||||
TEXT("GGA.PlayMontage.FireInterruptOnAnimEndInterrupt"), GPlayMontageAndWaitFireInterruptOnAnimEndInterrupt,
|
||||
TEXT("This is a fix that will cause GGA_AbilityTask_PlayMontageAndWaitForEvent to fire its Interrupt event if the underlying AnimInstance ends in an interrupted"));
|
||||
|
||||
|
||||
UGGA_AbilityTask_PlayMontageAndWaitForEvent::UGGA_AbilityTask_PlayMontageAndWaitForEvent(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
Rate = 1.f;
|
||||
bAllowInterruptAfterBlendOut = false;
|
||||
bStopWhenAbilityEnds = true;
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_PlayMontageAndWaitForEvent::OnMontageBlendingOut(UAnimMontage* Montage, bool bInterrupted)
|
||||
{
|
||||
const bool bPlayingThisMontage = (Montage == MontageToPlay) && Ability && Ability->GetCurrentMontage() == MontageToPlay;
|
||||
if (bPlayingThisMontage)
|
||||
{
|
||||
// Reset AnimRootMotionTranslationScale
|
||||
ACharacter* Character = Cast<ACharacter>(GetAvatarActor());
|
||||
if (Character && (Character->GetLocalRole() == ROLE_Authority ||
|
||||
(Character->GetLocalRole() == ROLE_AutonomousProxy && Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::LocalPredicted)))
|
||||
{
|
||||
Character->SetAnimRootMotionTranslationScale(1.f);
|
||||
}
|
||||
}
|
||||
|
||||
if (bPlayingThisMontage && (bInterrupted || !bAllowInterruptAfterBlendOut))
|
||||
{
|
||||
if (UAbilitySystemComponent* ASC = AbilitySystemComponent.Get())
|
||||
{
|
||||
ASC->ClearAnimatingAbility(Ability);
|
||||
}
|
||||
}
|
||||
|
||||
if (ShouldBroadcastAbilityTaskDelegates())
|
||||
{
|
||||
if (bInterrupted)
|
||||
{
|
||||
bAllowInterruptAfterBlendOut = false;
|
||||
OnInterrupted.Broadcast(FGameplayTag(), FGameplayEventData());
|
||||
|
||||
if (GUseAggressivePlayMontageAndWaitEndTask)
|
||||
{
|
||||
EndTask();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnBlendOut.Broadcast(FGameplayTag(), FGameplayEventData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_PlayMontageAndWaitForEvent::OnMontageBlendedIn(UAnimMontage* Montage)
|
||||
{
|
||||
if (ShouldBroadcastAbilityTaskDelegates())
|
||||
{
|
||||
OnInterrupted.Broadcast(FGameplayTag(), FGameplayEventData());
|
||||
}
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_PlayMontageAndWaitForEvent::OnGameplayAbilityCancelled()
|
||||
{
|
||||
if (StopPlayingMontage() || bAllowInterruptAfterBlendOut)
|
||||
{
|
||||
// Let the BP handle the interrupt as well
|
||||
if (ShouldBroadcastAbilityTaskDelegates())
|
||||
{
|
||||
OnCancelled.Broadcast(FGameplayTag(), FGameplayEventData());
|
||||
}
|
||||
}
|
||||
|
||||
if (GUseAggressivePlayMontageAndWaitEndTask)
|
||||
{
|
||||
EndTask();
|
||||
}
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_PlayMontageAndWaitForEvent::OnMontageEnded(UAnimMontage* Montage, bool bInterrupted)
|
||||
{
|
||||
if (!bInterrupted)
|
||||
{
|
||||
if (ShouldBroadcastAbilityTaskDelegates())
|
||||
{
|
||||
OnCompleted.Broadcast(FGameplayTag(), FGameplayEventData());
|
||||
}
|
||||
}
|
||||
else if (bAllowInterruptAfterBlendOut && GUseAggressivePlayMontageAndWaitEndTask)
|
||||
{
|
||||
if (ShouldBroadcastAbilityTaskDelegates())
|
||||
{
|
||||
OnInterrupted.Broadcast(FGameplayTag(), FGameplayEventData());
|
||||
}
|
||||
}
|
||||
|
||||
EndTask();
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_PlayMontageAndWaitForEvent::OnGameplayEvent(FGameplayTag EventTag, const FGameplayEventData* Payload)
|
||||
{
|
||||
if (ShouldBroadcastAbilityTaskDelegates())
|
||||
{
|
||||
FGameplayEventData TempData = *Payload;
|
||||
TempData.EventTag = EventTag;
|
||||
|
||||
EventReceived.Broadcast(EventTag, TempData);
|
||||
}
|
||||
}
|
||||
|
||||
UGGA_AbilityTask_PlayMontageAndWaitForEvent* UGGA_AbilityTask_PlayMontageAndWaitForEvent::PlayMontageAndWaitForEvent(UGameplayAbility* OwningAbility,
|
||||
FName TaskInstanceName, UAnimMontage* MontageToPlay,
|
||||
FGameplayTagContainer EventTags, float Rate, FName StartSection,
|
||||
bool bStopWhenAbilityEnds, float AnimRootMotionTranslationScale,
|
||||
float StartTimeSeconds, bool bAllowInterruptAfterBlendOut)
|
||||
{
|
||||
UAbilitySystemGlobals::NonShipping_ApplyGlobalAbilityScaler_Rate(Rate);
|
||||
|
||||
UGGA_AbilityTask_PlayMontageAndWaitForEvent* MyObj = NewAbilityTask<UGGA_AbilityTask_PlayMontageAndWaitForEvent>(OwningAbility, TaskInstanceName);
|
||||
MyObj->MontageToPlay = MontageToPlay;
|
||||
MyObj->Rate = Rate;
|
||||
MyObj->StartSection = StartSection;
|
||||
MyObj->AnimRootMotionTranslationScale = AnimRootMotionTranslationScale;
|
||||
MyObj->bStopWhenAbilityEnds = bStopWhenAbilityEnds;
|
||||
MyObj->bAllowInterruptAfterBlendOut = bAllowInterruptAfterBlendOut;
|
||||
MyObj->StartTimeSeconds = StartTimeSeconds;
|
||||
MyObj->EventTags = EventTags;
|
||||
|
||||
return MyObj;
|
||||
}
|
||||
|
||||
UGGA_AbilityTask_PlayMontageAndWaitForEvent* UGGA_AbilityTask_PlayMontageAndWaitForEvent::PlayMontageAndWaitForEventExt(UGameplayAbility* OwningAbility,
|
||||
FGGA_PlayMontageAndWaitForEventTaskParams Params)
|
||||
{
|
||||
UAbilitySystemGlobals::NonShipping_ApplyGlobalAbilityScaler_Rate(Params.Rate);
|
||||
|
||||
UGGA_AbilityTask_PlayMontageAndWaitForEvent* MyObj = NewAbilityTask<UGGA_AbilityTask_PlayMontageAndWaitForEvent>(OwningAbility, Params.TaskInstanceName);
|
||||
MyObj->MontageToPlay = Params.MontageToPlay;
|
||||
MyObj->Rate = Params.Rate;
|
||||
MyObj->StartSection = Params.StartSection;
|
||||
MyObj->AnimRootMotionTranslationScale = Params.AnimRootMotionTranslationScale;
|
||||
MyObj->bStopWhenAbilityEnds = Params.bStopWhenAbilityEnds;
|
||||
MyObj->bAllowInterruptAfterBlendOut = Params.bAllowInterruptAfterBlendOut;
|
||||
MyObj->StartTimeSeconds = Params.StartTimeSeconds;
|
||||
MyObj->EventTags = Params.EventTags;
|
||||
|
||||
return MyObj;
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_PlayMontageAndWaitForEvent::Activate()
|
||||
{
|
||||
if (Ability == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool bPlayedMontage = false;
|
||||
|
||||
if (UAbilitySystemComponent* ASC = AbilitySystemComponent.Get())
|
||||
{
|
||||
const FGameplayAbilityActorInfo* ActorInfo = Ability->GetCurrentActorInfo();
|
||||
UAnimInstance* AnimInstance = ActorInfo->GetAnimInstance();
|
||||
if (AnimInstance != nullptr)
|
||||
{
|
||||
if (ASC->PlayMontage(Ability, Ability->GetCurrentActivationInfo(), MontageToPlay, Rate, StartSection) > 0.f)
|
||||
{
|
||||
// Playing a montage could potentially fire off a callback into game code which could kill this ability! Early out if we are pending kill.
|
||||
if (ShouldBroadcastAbilityTaskDelegates() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Bind to event callback
|
||||
EventHandle = ASC->AddGameplayEventTagContainerDelegate(
|
||||
EventTags, FGameplayEventTagMulticastDelegate::FDelegate::CreateUObject(this, &UGGA_AbilityTask_PlayMontageAndWaitForEvent::OnGameplayEvent));
|
||||
|
||||
InterruptedHandle = Ability->OnGameplayAbilityCancelled.AddUObject(this, &UGGA_AbilityTask_PlayMontageAndWaitForEvent::OnGameplayAbilityCancelled);
|
||||
|
||||
BlendedInDelegate.BindUObject(this, &UGGA_AbilityTask_PlayMontageAndWaitForEvent::OnMontageBlendedIn);
|
||||
AnimInstance->Montage_SetBlendedInDelegate(BlendedInDelegate, MontageToPlay);
|
||||
|
||||
BlendingOutDelegate.BindUObject(this, &UGGA_AbilityTask_PlayMontageAndWaitForEvent::OnMontageBlendingOut);
|
||||
AnimInstance->Montage_SetBlendingOutDelegate(BlendingOutDelegate, MontageToPlay);
|
||||
|
||||
MontageEndedDelegate.BindUObject(this, &UGGA_AbilityTask_PlayMontageAndWaitForEvent::OnMontageEnded);
|
||||
AnimInstance->Montage_SetEndDelegate(MontageEndedDelegate, MontageToPlay);
|
||||
|
||||
ACharacter* Character = Cast<ACharacter>(GetAvatarActor());
|
||||
if (Character && (Character->GetLocalRole() == ROLE_Authority ||
|
||||
(Character->GetLocalRole() == ROLE_AutonomousProxy && Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::LocalPredicted)))
|
||||
{
|
||||
Character->SetAnimRootMotionTranslationScale(AnimRootMotionTranslationScale);
|
||||
}
|
||||
|
||||
bPlayedMontage = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogGGA_Tasks, Warning, TEXT("GGA_AbilityTask_PlayMontageAndWaitForEvent call to PlayMontage failed!"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogGGA_Tasks, Warning, TEXT("GGA_AbilityTask_PlayMontageAndWaitForEvent called on invalid AbilitySystemComponent"));
|
||||
}
|
||||
|
||||
if (!bPlayedMontage)
|
||||
{
|
||||
UE_LOG(LogGGA_Tasks, Warning, TEXT("GGA_AbilityTask_PlayMontageAndWaitForEvent called in Ability %s failed to play montage %s; Task Instance Name %s."), *Ability->GetName(),
|
||||
*GetNameSafe(MontageToPlay), *InstanceName.ToString());
|
||||
if (ShouldBroadcastAbilityTaskDelegates())
|
||||
{
|
||||
OnCancelled.Broadcast(FGameplayTag(), FGameplayEventData());
|
||||
}
|
||||
}
|
||||
|
||||
SetWaitingOnAvatar();
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_PlayMontageAndWaitForEvent::ExternalCancel()
|
||||
{
|
||||
if (ShouldBroadcastAbilityTaskDelegates())
|
||||
{
|
||||
OnCancelled.Broadcast(FGameplayTag(), FGameplayEventData());
|
||||
}
|
||||
|
||||
Super::ExternalCancel();
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_PlayMontageAndWaitForEvent::OnDestroy(bool AbilityEnded)
|
||||
{
|
||||
// Note: Clearing montage end delegate isn't necessary since its not a multicast and will be cleared when the next montage plays.
|
||||
// (If we are destroyed, it will detect this and not do anything)
|
||||
|
||||
// This delegate, however, should be cleared as it is a multicast
|
||||
if (Ability)
|
||||
{
|
||||
Ability->OnGameplayAbilityCancelled.Remove(InterruptedHandle);
|
||||
if (AbilityEnded && bStopWhenAbilityEnds)
|
||||
{
|
||||
StopPlayingMontage();
|
||||
}
|
||||
}
|
||||
|
||||
if (UAbilitySystemComponent* ASC = AbilitySystemComponent.Get())
|
||||
{
|
||||
ASC->RemoveGameplayEventTagContainerDelegate(EventTags, EventHandle);
|
||||
}
|
||||
|
||||
Super::OnDestroy(AbilityEnded);
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_PlayMontageAndWaitForEvent::EndTaskByOwner()
|
||||
{
|
||||
TaskOwnerEnded();
|
||||
}
|
||||
|
||||
bool UGGA_AbilityTask_PlayMontageAndWaitForEvent::StopPlayingMontage()
|
||||
{
|
||||
if (Ability == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const FGameplayAbilityActorInfo* ActorInfo = Ability->GetCurrentActorInfo();
|
||||
if (ActorInfo == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UAnimInstance* AnimInstance = ActorInfo->GetAnimInstance();
|
||||
if (AnimInstance == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the montage is still playing
|
||||
// The ability would have been interrupted, in which case we should automatically stop the montage
|
||||
UAbilitySystemComponent* ASC = AbilitySystemComponent.Get();
|
||||
if (ASC && Ability)
|
||||
{
|
||||
if (ASC->GetAnimatingAbility() == Ability
|
||||
&& ASC->GetCurrentMontage() == MontageToPlay)
|
||||
{
|
||||
// Unbind delegates so they don't get called as well
|
||||
FAnimMontageInstance* MontageInstance = AnimInstance->GetActiveInstanceForMontage(MontageToPlay);
|
||||
if (MontageInstance)
|
||||
{
|
||||
MontageInstance->OnMontageBlendedInEnded.Unbind();
|
||||
MontageInstance->OnMontageBlendingOutStarted.Unbind();
|
||||
MontageInstance->OnMontageEnded.Unbind();
|
||||
}
|
||||
|
||||
ASC->CurrentMontageStop();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
FString UGGA_AbilityTask_PlayMontageAndWaitForEvent::GetDebugString() const
|
||||
{
|
||||
UAnimMontage* PlayingMontage = nullptr;
|
||||
if (Ability)
|
||||
{
|
||||
const FGameplayAbilityActorInfo* ActorInfo = Ability->GetCurrentActorInfo();
|
||||
UAnimInstance* AnimInstance = ActorInfo->GetAnimInstance();
|
||||
|
||||
if (AnimInstance != nullptr)
|
||||
{
|
||||
PlayingMontage = AnimInstance->Montage_IsActive(MontageToPlay) ? ToRawPtr(MontageToPlay) : AnimInstance->GetCurrentActiveMontage();
|
||||
}
|
||||
}
|
||||
|
||||
return FString::Printf(TEXT("PlayMontageAndWaitForEvent. MontageToPlay: %s (Currently Playing): %s"), *GetNameSafe(MontageToPlay), *GetNameSafe(PlayingMontage));
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
//// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
//
|
||||
//
|
||||
//#include "AbilityTasks/GGA_AbilityTask_RunCustomAbilityTask.h"
|
||||
//
|
||||
//#include "GameplayTasksComponent.h"
|
||||
//#include "CustomTasks/GGA_CustomAbilityTask.h"
|
||||
//#include "Net/UnrealNetwork.h"
|
||||
//
|
||||
//UGGA_AbilityTask_RunCustomAbilityTask::UGGA_AbilityTask_RunCustomAbilityTask()
|
||||
//{
|
||||
//}
|
||||
//
|
||||
//void UGGA_AbilityTask_RunCustomAbilityTask::GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const
|
||||
//{
|
||||
// Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
// DOREPLIFETIME(UGGA_AbilityTask_RunCustomAbilityTask, TaskInstance);
|
||||
//}
|
||||
//
|
||||
//UGGA_AbilityTask_RunCustomAbilityTask* UGGA_AbilityTask_RunCustomAbilityTask::RunCustomAbilityTask(UGameplayAbility* OwningAbility, TSoftClassPtr<UGGA_CustomAbilityTask> AbilityTaskClass)
|
||||
//{
|
||||
// if (TSubclassOf<UGGA_CustomAbilityTask> RealClass = AbilityTaskClass.LoadSynchronous())
|
||||
// {
|
||||
// UGGA_AbilityTask_RunCustomAbilityTask* MyObj = NewAbilityTask<UGGA_AbilityTask_RunCustomAbilityTask>(OwningAbility);
|
||||
//
|
||||
// TObjectPtr<UGGA_CustomAbilityTask> CustomAbilityTask = NewObject<UGGA_CustomAbilityTask>(MyObj, RealClass);
|
||||
// CustomAbilityTask->SetSourceTask(MyObj);
|
||||
// MyObj->bSimulatedTask = CustomAbilityTask->bSimulatedTask;
|
||||
// MyObj->bTickingTask = CustomAbilityTask->bTickingTask;
|
||||
// return MyObj;
|
||||
// }
|
||||
// return nullptr;
|
||||
//}
|
||||
//
|
||||
//void UGGA_AbilityTask_RunCustomAbilityTask::Activate()
|
||||
//{
|
||||
// if (UGameplayTasksComponent* Component = GetGameplayTasksComponent())
|
||||
// {
|
||||
// if (IsSimulatedTask())
|
||||
// {
|
||||
// if (Component->IsUsingRegisteredSubObjectList() && Component->IsReadyForReplication())
|
||||
// {
|
||||
// Component->AddReplicatedSubObject(TaskInstance, COND_SkipOwner);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// TaskInstance->OnTaskActivate();
|
||||
//}
|
||||
//
|
||||
//void UGGA_AbilityTask_RunCustomAbilityTask::TickTask(float DeltaTime)
|
||||
//{
|
||||
// TaskInstance->OnTaskTick(DeltaTime);
|
||||
//}
|
||||
//
|
||||
//void UGGA_AbilityTask_RunCustomAbilityTask::OnDestroy(bool bInOwnerFinished)
|
||||
//{
|
||||
// if (!bWasSuccessfullyDestroyed)
|
||||
// {
|
||||
// if (UGameplayTasksComponent* Component = GetGameplayTasksComponent())
|
||||
// {
|
||||
// if (IsSimulatedTask())
|
||||
// {
|
||||
// if (Component->IsUsingRegisteredSubObjectList())
|
||||
// {
|
||||
// Component->RemoveReplicatedSubObject(TaskInstance);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// TaskInstance->OnTaskDestroy(bInOwnerFinished);
|
||||
// }
|
||||
// Super::OnDestroy(bInOwnerFinished);
|
||||
//}
|
||||
//
|
||||
//void UGGA_AbilityTask_RunCustomAbilityTask::InitSimulatedTask(UGameplayTasksComponent& InGameplayTasksComponent)
|
||||
//{
|
||||
// Super::InitSimulatedTask(InGameplayTasksComponent);
|
||||
// TaskInstance->OnInitSimulatedTask();
|
||||
//}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "AbilityTasks/GGA_AbilityTask_ServerWaitForClientTargetData.h"
|
||||
#include "AbilitySystemComponent.h"
|
||||
|
||||
UGGA_AbilityTask_ServerWaitForClientTargetData::UGGA_AbilityTask_ServerWaitForClientTargetData(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
}
|
||||
|
||||
UGGA_AbilityTask_ServerWaitForClientTargetData* UGGA_AbilityTask_ServerWaitForClientTargetData::ServerWaitForClientTargetData(UGameplayAbility* OwningAbility, FName TaskInstanceName, bool TriggerOnce)
|
||||
{
|
||||
UGGA_AbilityTask_ServerWaitForClientTargetData* MyObj = NewAbilityTask<UGGA_AbilityTask_ServerWaitForClientTargetData>(OwningAbility, TaskInstanceName);
|
||||
MyObj->bTriggerOnce = TriggerOnce;
|
||||
return MyObj;
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_ServerWaitForClientTargetData::Activate()
|
||||
{
|
||||
// ClientPath
|
||||
if (!Ability || !Ability->GetCurrentActorInfo()->IsNetAuthority())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// ServerPath
|
||||
FGameplayAbilitySpecHandle SpecHandle = GetAbilitySpecHandle();
|
||||
FPredictionKey ActivationPredictionKey = GetActivationPredictionKey();
|
||||
AbilitySystemComponent->AbilityTargetDataSetDelegate(SpecHandle, ActivationPredictionKey).AddUObject(this, &UGGA_AbilityTask_ServerWaitForClientTargetData::OnTargetDataReplicatedCallback);
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_ServerWaitForClientTargetData::OnTargetDataReplicatedCallback(const FGameplayAbilityTargetDataHandle& Data, FGameplayTag ActivationTag)
|
||||
{
|
||||
FGameplayAbilityTargetDataHandle MutableData = Data;
|
||||
AbilitySystemComponent->ConsumeClientReplicatedTargetData(GetAbilitySpecHandle(), GetActivationPredictionKey());
|
||||
|
||||
if (ShouldBroadcastAbilityTaskDelegates())
|
||||
{
|
||||
ValidData.Broadcast(MutableData);
|
||||
}
|
||||
|
||||
if (bTriggerOnce)
|
||||
{
|
||||
EndTask();
|
||||
}
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_ServerWaitForClientTargetData::OnDestroy(bool AbilityEnded)
|
||||
{
|
||||
if (AbilitySystemComponent.IsValid())
|
||||
{
|
||||
FGameplayAbilitySpecHandle SpecHandle = GetAbilitySpecHandle();
|
||||
FPredictionKey ActivationPredictionKey = GetActivationPredictionKey();
|
||||
AbilitySystemComponent->AbilityTargetDataSetDelegate(SpecHandle, ActivationPredictionKey).RemoveAll(this);
|
||||
}
|
||||
|
||||
Super::OnDestroy(AbilityEnded);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "AbilityTasks/GGA_AbilityTask_WaitDelayOneFrame.h"
|
||||
#include "Engine/World.h"
|
||||
#include "TimerManager.h"
|
||||
|
||||
UGGA_AbilityTask_WaitDelayOneFrame::UGGA_AbilityTask_WaitDelayOneFrame(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_WaitDelayOneFrame::Activate()
|
||||
{
|
||||
GetWorld()->GetTimerManager().SetTimerForNextTick(this, &UGGA_AbilityTask_WaitDelayOneFrame::OnDelayFinish);
|
||||
}
|
||||
|
||||
UGGA_AbilityTask_WaitDelayOneFrame* UGGA_AbilityTask_WaitDelayOneFrame::WaitDelayOneFrame(UGameplayAbility* OwningAbility)
|
||||
{
|
||||
UGGA_AbilityTask_WaitDelayOneFrame* MyObj = NewAbilityTask<UGGA_AbilityTask_WaitDelayOneFrame>(OwningAbility);
|
||||
return MyObj;
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_WaitDelayOneFrame::OnDelayFinish()
|
||||
{
|
||||
if (ShouldBroadcastAbilityTaskDelegates())
|
||||
{
|
||||
OnFinish.Broadcast();
|
||||
}
|
||||
EndTask();
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#include "AbilityTasks/GGA_AbilityTask_WaitGameplayEvents.h"
|
||||
#include "AbilitySystemGlobals.h"
|
||||
#include "AbilitySystemComponent.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GGA_AbilityTask_WaitGameplayEvents)
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
UGGA_AbilityTask_WaitGameplayEvents::UGGA_AbilityTask_WaitGameplayEvents(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
}
|
||||
|
||||
UGGA_AbilityTask_WaitGameplayEvents* UGGA_AbilityTask_WaitGameplayEvents::WaitGameplayEvents(UGameplayAbility* OwningAbility, FGameplayTagContainer EventTags, AActor* OptionalExternalTarget,
|
||||
bool OnlyTriggerOnce)
|
||||
{
|
||||
UGGA_AbilityTask_WaitGameplayEvents* MyObj = NewAbilityTask<UGGA_AbilityTask_WaitGameplayEvents>(OwningAbility);
|
||||
MyObj->EventTags = EventTags;
|
||||
MyObj->SetExternalTarget(OptionalExternalTarget);
|
||||
MyObj->OnlyTriggerOnce = OnlyTriggerOnce;
|
||||
|
||||
return MyObj;
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_WaitGameplayEvents::Activate()
|
||||
{
|
||||
UAbilitySystemComponent* ASC = GetTargetASC();
|
||||
if (ASC)
|
||||
{
|
||||
MyHandle = ASC->AddGameplayEventTagContainerDelegate(
|
||||
EventTags, FGameplayEventTagMulticastDelegate::FDelegate::CreateUObject(this, &UGGA_AbilityTask_WaitGameplayEvents::GameplayEventContainerCallback));
|
||||
}
|
||||
|
||||
Super::Activate();
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_WaitGameplayEvents::GameplayEventContainerCallback(FGameplayTag MatchingTag, const FGameplayEventData* Payload)
|
||||
{
|
||||
if (ShouldBroadcastAbilityTaskDelegates())
|
||||
{
|
||||
ensureMsgf(Payload, TEXT("GameplayEventCallback expected non-null Payload"));
|
||||
FGameplayEventData TempPayload = Payload ? *Payload : FGameplayEventData{};
|
||||
TempPayload.EventTag = MatchingTag;
|
||||
EventReceived.Broadcast(MatchingTag, TempPayload);
|
||||
}
|
||||
if (OnlyTriggerOnce)
|
||||
{
|
||||
EndTask();
|
||||
}
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_WaitGameplayEvents::SetExternalTarget(AActor* Actor)
|
||||
{
|
||||
if (Actor)
|
||||
{
|
||||
UseExternalTarget = true;
|
||||
OptionalExternalTarget = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(Actor);
|
||||
}
|
||||
}
|
||||
|
||||
UAbilitySystemComponent* UGGA_AbilityTask_WaitGameplayEvents::GetTargetASC()
|
||||
{
|
||||
if (UseExternalTarget)
|
||||
{
|
||||
return OptionalExternalTarget;
|
||||
}
|
||||
|
||||
return AbilitySystemComponent.Get();
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_WaitGameplayEvents::OnDestroy(bool AbilityEnding)
|
||||
{
|
||||
UAbilitySystemComponent* ASC = GetTargetASC();
|
||||
if (ASC && MyHandle.IsValid())
|
||||
{
|
||||
ASC->RemoveGameplayEventTagContainerDelegate(EventTags, MyHandle);
|
||||
}
|
||||
|
||||
Super::OnDestroy(AbilityEnding);
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "AbilityTasks/GGA_AbilityTask_WaitInputPressWithTags.h"
|
||||
#include "Engine/World.h"
|
||||
#include "AbilitySystemComponent.h"
|
||||
|
||||
UGGA_AbilityTask_WaitInputPressWithTags::UGGA_AbilityTask_WaitInputPressWithTags(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
StartTime = 0.f;
|
||||
bTestInitialState = false;
|
||||
}
|
||||
|
||||
UGGA_AbilityTask_WaitInputPressWithTags* UGGA_AbilityTask_WaitInputPressWithTags::WaitInputPressWithTags(UGameplayAbility* OwningAbility, FGameplayTagContainer RequiredTags,
|
||||
FGameplayTagContainer IgnoredTags,
|
||||
bool bTestAlreadyPressed)
|
||||
{
|
||||
UGGA_AbilityTask_WaitInputPressWithTags* Task = NewAbilityTask<UGGA_AbilityTask_WaitInputPressWithTags>(OwningAbility);
|
||||
Task->bTestInitialState = bTestAlreadyPressed;
|
||||
Task->TagQuery = FGameplayTagQuery::BuildQuery(FGameplayTagQueryExpression().AllTagsMatch().AddTags(RequiredTags).NoTagsMatch().AddTags(IgnoredTags));
|
||||
return Task;
|
||||
}
|
||||
|
||||
UGGA_AbilityTask_WaitInputPressWithTags* UGGA_AbilityTask_WaitInputPressWithTags::WaitInputPressWithTagQuery(UGameplayAbility* OwningAbility, const FGameplayTagQuery& TagQuery,
|
||||
bool bTestAlreadyPressed)
|
||||
{
|
||||
UGGA_AbilityTask_WaitInputPressWithTags* Task = NewAbilityTask<UGGA_AbilityTask_WaitInputPressWithTags>(OwningAbility);
|
||||
Task->bTestInitialState = bTestAlreadyPressed;
|
||||
Task->TagQuery = TagQuery;
|
||||
return Task;
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_WaitInputPressWithTags::OnPressCallback()
|
||||
{
|
||||
float ElapsedTime = GetWorld()->GetTimeSeconds() - StartTime;
|
||||
|
||||
UAbilitySystemComponent* ASC = AbilitySystemComponent.Get();
|
||||
|
||||
if (!Ability || !ASC)
|
||||
{
|
||||
EndTask();
|
||||
return;
|
||||
}
|
||||
|
||||
const FGameplayTagContainer CurrentTags = ASC->GetOwnedGameplayTags();
|
||||
if (!TagQuery.Matches(CurrentTags))
|
||||
{
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
ASC->AbilityReplicatedEventDelegate(EAbilityGenericReplicatedEvent::InputPressed, GetAbilitySpecHandle(), GetActivationPredictionKey()).Remove(DelegateHandle);
|
||||
|
||||
FScopedPredictionWindow ScopedPrediction(ASC, IsPredictingClient());
|
||||
|
||||
if (IsPredictingClient())
|
||||
{
|
||||
// Tell the server about this
|
||||
ASC->ServerSetReplicatedEvent(EAbilityGenericReplicatedEvent::InputPressed, GetAbilitySpecHandle(), GetActivationPredictionKey(),
|
||||
ASC->ScopedPredictionKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
ASC->ConsumeGenericReplicatedEvent(EAbilityGenericReplicatedEvent::InputPressed, GetAbilitySpecHandle(), GetActivationPredictionKey());
|
||||
}
|
||||
|
||||
// We are done. Kill us so we don't keep getting broadcast messages
|
||||
if (ShouldBroadcastAbilityTaskDelegates())
|
||||
{
|
||||
OnPress.Broadcast(ElapsedTime);
|
||||
}
|
||||
|
||||
EndTask();
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_WaitInputPressWithTags::Activate()
|
||||
{
|
||||
StartTime = GetWorld()->GetTimeSeconds();
|
||||
if (Ability)
|
||||
{
|
||||
if (bTestInitialState && IsLocallyControlled())
|
||||
{
|
||||
FGameplayAbilitySpec* Spec = Ability->GetCurrentAbilitySpec();
|
||||
if (Spec && Spec->InputPressed)
|
||||
{
|
||||
OnPressCallback();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DelegateHandle = AbilitySystemComponent->AbilityReplicatedEventDelegate(EAbilityGenericReplicatedEvent::InputPressed, GetAbilitySpecHandle(), GetActivationPredictionKey()).AddUObject(
|
||||
this, &UGGA_AbilityTask_WaitInputPressWithTags::OnPressCallback);
|
||||
if (IsForRemoteClient())
|
||||
{
|
||||
if (!AbilitySystemComponent->CallReplicatedEventDelegateIfSet(EAbilityGenericReplicatedEvent::InputPressed, GetAbilitySpecHandle(), GetActivationPredictionKey()))
|
||||
{
|
||||
SetWaitingOnRemotePlayerData();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_WaitInputPressWithTags::OnDestroy(bool AbilityEnded)
|
||||
{
|
||||
AbilitySystemComponent->AbilityReplicatedEventDelegate(EAbilityGenericReplicatedEvent::InputPressed, GetAbilitySpecHandle(), GetActivationPredictionKey()).Remove(DelegateHandle);
|
||||
|
||||
ClearWaitingOnRemotePlayerData();
|
||||
|
||||
Super::OnDestroy(AbilityEnded);
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_WaitInputPressWithTags::Reset()
|
||||
{
|
||||
AbilitySystemComponent->AbilityReplicatedEventDelegate(EAbilityGenericReplicatedEvent::InputPressed, GetAbilitySpecHandle(), GetActivationPredictionKey()).Remove(DelegateHandle);
|
||||
|
||||
DelegateHandle = AbilitySystemComponent->AbilityReplicatedEventDelegate(EAbilityGenericReplicatedEvent::InputPressed, GetAbilitySpecHandle(), GetActivationPredictionKey()).AddUObject(
|
||||
this, &UGGA_AbilityTask_WaitInputPressWithTags::OnPressCallback);
|
||||
if (IsForRemoteClient())
|
||||
{
|
||||
if (!AbilitySystemComponent->CallReplicatedEventDelegateIfSet(EAbilityGenericReplicatedEvent::InputPressed, GetAbilitySpecHandle(), GetActivationPredictionKey()))
|
||||
{
|
||||
SetWaitingOnRemotePlayerData();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "AbilityTasks/GGA_AbilityTask_WaitTargetDataUsingActor.h"
|
||||
|
||||
#include "AbilitySystemComponent.h"
|
||||
#include "TargetActors/GGA_AbilityTargetActor_Trace.h"
|
||||
|
||||
UGGA_AbilityTask_WaitTargetDataUsingActor* UGGA_AbilityTask_WaitTargetDataUsingActor::WaitTargetDataWithReusableActor(
|
||||
UGameplayAbility* OwningAbility, FName TaskInstanceName,
|
||||
TEnumAsByte<EGameplayTargetingConfirmation::Type> ConfirmationType, AGameplayAbilityTargetActor* InTargetActor,
|
||||
bool bCreateKeyIfNotValidForMorePrediction)
|
||||
{
|
||||
UGGA_AbilityTask_WaitTargetDataUsingActor* MyObj = NewAbilityTask<UGGA_AbilityTask_WaitTargetDataUsingActor>(
|
||||
OwningAbility, TaskInstanceName); //Register for task list here, providing a given FName as a key
|
||||
MyObj->TargetActor = InTargetActor;
|
||||
MyObj->ConfirmationType = ConfirmationType;
|
||||
MyObj->bCreateKeyIfNotValidForMorePrediction = bCreateKeyIfNotValidForMorePrediction;
|
||||
return MyObj;
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_WaitTargetDataUsingActor::Activate()
|
||||
{
|
||||
if (!IsValid(this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Ability && TargetActor)
|
||||
{
|
||||
/** server&client 注册TargetActor上的Ready(Confirm)/Cancel事件 */
|
||||
InitializeTargetActor();
|
||||
/** server注册TargetDeta */
|
||||
RegisterTargetDataCallbacks();
|
||||
|
||||
FinalizeTargetActor();
|
||||
}
|
||||
else
|
||||
{
|
||||
EndTask();
|
||||
}
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_WaitTargetDataUsingActor::OnTargetDataReplicatedCallback(const FGameplayAbilityTargetDataHandle& Data,
|
||||
FGameplayTag ActivationTag)
|
||||
{
|
||||
FGameplayAbilityTargetDataHandle MutableData = Data;
|
||||
|
||||
if (UAbilitySystemComponent* ASC = AbilitySystemComponent.Get())
|
||||
{
|
||||
AbilitySystemComponent->ConsumeClientReplicatedTargetData(GetAbilitySpecHandle(), GetActivationPredictionKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Call into the TargetActor to sanitize/verify the data. If this returns false, we are rejecting
|
||||
* the replicated target data and will treat this as a cancel.
|
||||
*
|
||||
* This can also be used for bandwidth optimizations. OnReplicatedTargetDataReceived could do an actual
|
||||
* trace/check/whatever server side and use that data. So rather than having the client send that data
|
||||
* explicitly, the client is basically just sending a 'confirm' and the server is now going to do the work
|
||||
* in OnReplicatedTargetDataReceived.
|
||||
*/
|
||||
if (TargetActor && !TargetActor->OnReplicatedTargetDataReceived(MutableData))
|
||||
{
|
||||
if (ShouldBroadcastAbilityTaskDelegates())
|
||||
{
|
||||
Cancelled.Broadcast(MutableData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ShouldBroadcastAbilityTaskDelegates())
|
||||
{
|
||||
ValidData.Broadcast(MutableData);
|
||||
}
|
||||
}
|
||||
|
||||
if (ConfirmationType != EGameplayTargetingConfirmation::CustomMulti)
|
||||
{
|
||||
EndTask();
|
||||
}
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_WaitTargetDataUsingActor::OnTargetDataReplicatedCancelledCallback()
|
||||
{
|
||||
if (ShouldBroadcastAbilityTaskDelegates())
|
||||
{
|
||||
Cancelled.Broadcast(FGameplayAbilityTargetDataHandle());
|
||||
}
|
||||
EndTask();
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_WaitTargetDataUsingActor::OnTargetDataReadyCallback(const FGameplayAbilityTargetDataHandle& Data)
|
||||
{
|
||||
|
||||
UAbilitySystemComponent* ASC = AbilitySystemComponent.Get();
|
||||
|
||||
if (!Ability || !ASC)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// client path
|
||||
FScopedPredictionWindow ScopedPrediction(ASC,
|
||||
ShouldReplicateDataToServer() && (bCreateKeyIfNotValidForMorePrediction &&
|
||||
!ASC->ScopedPredictionKey.IsValidForMorePrediction()
|
||||
));
|
||||
|
||||
const FGameplayAbilityActorInfo* Info = Ability->GetCurrentActorInfo();
|
||||
|
||||
// client path
|
||||
if (IsPredictingClient())
|
||||
{
|
||||
// Rpc发送TargetData到服务器
|
||||
if (!TargetActor->ShouldProduceTargetDataOnServer)
|
||||
{
|
||||
FGameplayTag ApplicationTag; // Fixme: where would this be useful?
|
||||
ASC->CallServerSetReplicatedTargetData(GetAbilitySpecHandle(),
|
||||
GetActivationPredictionKey(), Data,
|
||||
ApplicationTag,
|
||||
AbilitySystemComponent->ScopedPredictionKey);
|
||||
}
|
||||
else if (ConfirmationType == EGameplayTargetingConfirmation::UserConfirmed)
|
||||
{
|
||||
// Rpc告诉服务器确认了。
|
||||
// We aren't going to send the target data, but we will send a generic confirmed message.
|
||||
ASC->ServerSetReplicatedEvent(EAbilityGenericReplicatedEvent::GenericConfirm,
|
||||
GetAbilitySpecHandle(), GetActivationPredictionKey(),
|
||||
AbilitySystemComponent->ScopedPredictionKey);
|
||||
}
|
||||
}
|
||||
|
||||
if (ShouldBroadcastAbilityTaskDelegates())
|
||||
{
|
||||
ValidData.Broadcast(Data);
|
||||
}
|
||||
|
||||
if (ConfirmationType != EGameplayTargetingConfirmation::CustomMulti)
|
||||
{
|
||||
EndTask();
|
||||
}
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_WaitTargetDataUsingActor::OnTargetDataCancelledCallback(const FGameplayAbilityTargetDataHandle& Data)
|
||||
{
|
||||
UAbilitySystemComponent* ASC = AbilitySystemComponent.Get();
|
||||
|
||||
if(!ASC)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//client path
|
||||
FScopedPredictionWindow ScopedPrediction(ASC, IsPredictingClient());
|
||||
|
||||
//client path
|
||||
if (IsPredictingClient())
|
||||
{
|
||||
if (!TargetActor->ShouldProduceTargetDataOnServer)
|
||||
{
|
||||
ASC->ServerSetReplicatedTargetDataCancelled(
|
||||
GetAbilitySpecHandle(), GetActivationPredictionKey(), ASC->ScopedPredictionKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We aren't going to send the target data, but we will send a generic confirmed message.
|
||||
ASC->ServerSetReplicatedEvent(EAbilityGenericReplicatedEvent::GenericCancel,
|
||||
GetAbilitySpecHandle(), GetActivationPredictionKey(),
|
||||
ASC->ScopedPredictionKey);
|
||||
}
|
||||
}
|
||||
|
||||
// client&& server path.
|
||||
Cancelled.Broadcast(Data);
|
||||
EndTask();
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_WaitTargetDataUsingActor::ExternalConfirm(bool bEndTask)
|
||||
{
|
||||
if (TargetActor)
|
||||
{
|
||||
if (TargetActor->ShouldProduceTargetData())
|
||||
{
|
||||
TargetActor->ConfirmTargetingAndContinue();
|
||||
}
|
||||
}
|
||||
Super::ExternalConfirm(bEndTask);
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_WaitTargetDataUsingActor::ExternalCancel()
|
||||
{
|
||||
if (ShouldBroadcastAbilityTaskDelegates())
|
||||
{
|
||||
Cancelled.Broadcast(FGameplayAbilityTargetDataHandle());
|
||||
}
|
||||
Super::ExternalCancel();
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_WaitTargetDataUsingActor::InitializeTargetActor() const
|
||||
{
|
||||
check(TargetActor);
|
||||
check(Ability);
|
||||
|
||||
TargetActor->PrimaryPC = Ability->GetCurrentActorInfo()->PlayerController.Get();
|
||||
|
||||
TargetActor->TargetDataReadyDelegate.AddUObject(
|
||||
const_cast<UGGA_AbilityTask_WaitTargetDataUsingActor*>(this), &UGGA_AbilityTask_WaitTargetDataUsingActor::OnTargetDataReadyCallback);
|
||||
TargetActor->CanceledDelegate.AddUObject(
|
||||
const_cast<UGGA_AbilityTask_WaitTargetDataUsingActor*>(this), &UGGA_AbilityTask_WaitTargetDataUsingActor::OnTargetDataCancelledCallback);
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_WaitTargetDataUsingActor::RegisterTargetDataCallbacks()
|
||||
{
|
||||
if (!ensure(IsValid(this) == true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UAbilitySystemComponent* ASC = AbilitySystemComponent.Get();
|
||||
|
||||
if (!ASC)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
check(Ability);
|
||||
|
||||
const bool bIsLocalControlled = Ability->GetCurrentActorInfo()->IsLocallyControlled();
|
||||
const bool bShouldProduceTargetDataOnServer = TargetActor->ShouldProduceTargetDataOnServer;
|
||||
|
||||
/** server path. 若不是本地控制的(server for remote client),查看TargetData是否已发送,否则在到达此处时注册回调 */
|
||||
if (!bIsLocalControlled)
|
||||
{
|
||||
//如果我们希望客户端发送TargetData回调,就注册TargetData回调
|
||||
if (!bShouldProduceTargetDataOnServer) // produce on client
|
||||
{
|
||||
FGameplayAbilitySpecHandle SpecHandle = GetAbilitySpecHandle();
|
||||
FPredictionKey ActivationPredictionKey = GetActivationPredictionKey();
|
||||
|
||||
/** 注册TargetDataSet事件 */
|
||||
ASC->AbilityTargetDataSetDelegate(SpecHandle, ActivationPredictionKey).AddUObject(
|
||||
this, &UGGA_AbilityTask_WaitTargetDataUsingActor::OnTargetDataReplicatedCallback);
|
||||
|
||||
/** 注册TargetDataCancel事件*/
|
||||
ASC->AbilityTargetDataCancelledDelegate(SpecHandle, ActivationPredictionKey).AddUObject(
|
||||
this, &UGGA_AbilityTask_WaitTargetDataUsingActor::OnTargetDataReplicatedCancelledCallback);
|
||||
|
||||
// 检查TargetData是否已经Confirm/Cancel并执行相关操作。
|
||||
ASC->CallReplicatedTargetDataDelegatesIfSet(SpecHandle, ActivationPredictionKey);
|
||||
|
||||
SetWaitingOnRemotePlayerData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGGA_AbilityTask_WaitTargetDataUsingActor::FinalizeTargetActor() const
|
||||
{
|
||||
check(TargetActor);
|
||||
check(Ability);
|
||||
|
||||
TargetActor->StartTargeting(Ability);
|
||||
|
||||
if (TargetActor->ShouldProduceTargetData())
|
||||
{
|
||||
// If instant confirm, then stop targeting immediately.
|
||||
// Note this is kind of bad: we should be able to just call a static func on the CDO to do this.
|
||||
// But then we wouldn't get to set ExposeOnSpawnParameters.
|
||||
if (ConfirmationType == EGameplayTargetingConfirmation::Instant)
|
||||
{
|
||||
TargetActor->ConfirmTargeting();
|
||||
}
|
||||
else if (ConfirmationType == EGameplayTargetingConfirmation::UserConfirmed)
|
||||
{
|
||||
// Bind to the Cancel/Confirm Delegates (called from local confirm or from repped confirm)
|
||||
TargetActor->BindToConfirmCancelInputs();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void UGGA_AbilityTask_WaitTargetDataUsingActor::OnDestroy(bool AbilityEnded)
|
||||
{
|
||||
if (TargetActor)
|
||||
{
|
||||
AGGA_AbilityTargetActor_Trace* TraceTargetActor = Cast<AGGA_AbilityTargetActor_Trace>(TargetActor);
|
||||
if (TraceTargetActor)
|
||||
{
|
||||
// TargetActor 基类没有StopTracing函数.
|
||||
TraceTargetActor->StopTargeting();
|
||||
}
|
||||
else
|
||||
{
|
||||
// TargetActor doesn't have a StopTargeting function
|
||||
TargetActor->SetActorTickEnabled(false);
|
||||
|
||||
// Clear added callbacks
|
||||
TargetActor->TargetDataReadyDelegate.RemoveAll(this);
|
||||
TargetActor->CanceledDelegate.RemoveAll(this);
|
||||
|
||||
AbilitySystemComponent->GenericLocalConfirmCallbacks.RemoveDynamic(
|
||||
TargetActor, &AGameplayAbilityTargetActor::ConfirmTargeting);
|
||||
AbilitySystemComponent->GenericLocalCancelCallbacks.RemoveDynamic(
|
||||
TargetActor, &AGameplayAbilityTargetActor::CancelTargeting);
|
||||
TargetActor->GenericDelegateBoundASC = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Super::OnDestroy(AbilityEnded);
|
||||
}
|
||||
|
||||
bool UGGA_AbilityTask_WaitTargetDataUsingActor::ShouldReplicateDataToServer() const
|
||||
{
|
||||
if (!Ability || !TargetActor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const FGameplayAbilityActorInfo* Info = Ability->GetCurrentActorInfo();
|
||||
if (!Info->IsNetAuthority() && !TargetActor->ShouldProduceTargetDataOnServer)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
Reference in New Issue
Block a user