GameplayAbility
赋予GA
UGameplayAbility* Ability
AbilitySystemComponent->GiveAbility(FGameplayAbilitySpec(Ability, 0, INDEX_NONE, this))
UPROPERTY(ReplicatedUsing=OnRep_ActivateAbilities, BlueprintReadOnly, Category = "Abilities")
FGameplayAbilitySpecContainer ActivatableAbilities;
Params.Condition = COND_ReplayOrOwner;
DOREPLIFETIME_WITH_PARAMS_FAST(UAbilitySystemComponent, ActivatableAbilities, Params);
FGameplayAbilitySpecHandle UAbilitySystemComponent::GiveAbility(const FGameplayAbilitySpec& Spec)
FGameplayAbilitySpec& OwnedSpec = ActivatableAbilities.Items[ActivatableAbilities.Items.Add(Spec)];
if (OwnedSpec.Ability->GetInstancingPolicy() == EGameplayAbilityInstancingPolicy::InstancedPerActor)
CreateNewInstanceOfAbility(OwnedSpec, Spec.Ability);
MarkAbilitySpecDirty(OwnedSpec, true);
GA实例化策略
namespace EGameplayAbilityInstancingPolicy
enum Type
NonInstanced,
InstancedPerActor,
InstancedPerExecution
创建GASpec
FGameplayAbilitySpec::FGameplayAbilitySpec(UGameplayAbility* InAbility, int32 InLevel, int32 InInputID, UObject* InSourceObject)
: Ability(InAbility)
, Level(InLevel)
, InputID(InInputID)
, SourceObject(InSourceObject)
...
Handle.GenerateNewHandle();
void FGameplayAbilitySpecHandle::GenerateNewHandle()
static int32 GHandle = 1;
Handle = GHandle++;
创建GA实例
UGameplayAbility* UAbilitySystemComponent::CreateNewInstanceOfAbility(FGameplayAbilitySpec& Spec, UGameplayAbility* Ability)
if (Ability->HasAllFlags(RF_ClassDefaultObject))
AbilityInstance = NewObject<UGameplayAbility>(Owner, Ability->GetClass());
else
AbilityInstance = NewObject<UGameplayAbility>(Owner, Ability->GetClass(), NAME_None, RF_NoFlags, Ability);
if (AbilityInstance->GetReplicationPolicy() != EGameplayAbilityReplicationPolicy::ReplicateNo)
Spec.ReplicatedInstances.Add(AbilityInstance);
AllReplicatedInstancedAbilities.Add(AbilityInstance);
else
Spec.NonReplicatedInstances.Add(AbilityInstance);
激活GA
通过Handle激活GA
bool UAbilitySystemComponent::TryActivateAbility(FGameplayAbilitySpecHandle AbilityToActivate, bool bAllowRemoteActivation)
FGameplayAbilitySpec* Spec = FindAbilitySpecFromHandle(AbilityToActivate);
UGameplayAbility* Ability = Spec->Ability;
if (NetMode == ROLE_SimulatedProxy)
return false;
...
...
return InternalTryActivateAbility(AbilityToActivate);
当然,有其它上层的激活方法:
bool UAbilitySystemComponent::TryActivateAbilitiesByTag(const FGameplayTagContainer& GameplayTagContainer, bool bAllowRemoteActivation)
TArray<FGameplayAbilitySpec*> AbilitiesToActivate;
GetActivatableGameplayAbilitySpecsByAllMatchingTags(GameplayTagContainer, AbilitiesToActivate);
for (const FGameplayAbilitySpec& Spec : ActivatableAbilities.Items)
if (Spec.Ability && Spec.Ability->AbilityTags.HasAll(GameplayTagContainer))
if (!bOnlyAbilitiesThatSatisfyTagRequirements || Spec.Ability->DoesAbilitySatisfyTagRequirements(*this))
MatchingGameplayAbilities.Add(const_cast<FGameplayAbilitySpec*>(&Spec));
bSuccess |= TryActivateAbility(GameplayAbilitySpec->Handle, bAllowRemoteActivation);
是否有Tag的Miss、Block
bool UGameplayAbility::DoesAbilitySatisfyTagRequirements(const UAbilitySystemComponent& AbilitySystemComponent, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags, OUT FGameplayTagContainer* OptionalRelevantTags) const
bool bBlocked = false;
bool bMissing = false;
bool TryActivateAbilityByClass(TSubclassOf<UGameplayAbility> InAbilityToActivate, bool bAllowRemoteActivation = true)
if (Spec.Ability == InAbilityCDO)
bool TriggerAbilityFromGameplayEvent(FGameplayAbilitySpecHandle AbilityToTrigger, FGameplayAbilityActorInfo* ActorInfo, FGameplayTag Tag, const FGameplayEventData* Payload, UAbilitySystemComponent& Component);
if (Ability->ShouldAbilityRespondToEvent(ActorInfo, &TempEventData))
底层的激活函数都是InternalTryActivateAbility:
bool UAbilitySystemComponent::InternalTryActivateAbility(FGameplayAbilitySpecHandle Handle, FPredictionKey InPredictionKey, UGameplayAbility** OutInstancedAbility, FOnGameplayAbilityEnded::FDelegate* OnGameplayAbilityEndedDelegate, const FGameplayEventData* TriggerEventData)
FGameplayAbilitySpec* Spec = FindAbilitySpecFromHandle(Handle);
UGameplayAbility* const CanActivateAbilitySource = InstancedAbility ? InstancedAbility : Ability;
if (!CanActivateAbilitySource->CanActivateAbility(Handle, ActorInfo, SourceTags, TargetTags, &InternalTryActivateAbilityFailureTags))
NotifyAbilityFailed(Handle, CanActivateAbilitySource, InternalTryActivateAbilityFailureTags);
return false;
if (Ability->GetInstancingPolicy() == EGameplayAbilityInstancingPolicy::InstancedPerActor)
if (Spec->IsActive())
...
Spec->ActiveCount++;
if (Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::LocalOnly || (NetRole == ROLE_Authority))
else if (Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::LocalPredicted)
是否可以激活
bool UGameplayAbility::CanActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags, OUT FGameplayTagContainer* OptionalRelevantTags) const
!ShouldActivateAbility(AvatarActor->GetLocalRole())
!CheckCooldown(Handle, ActorInfo, OptionalRelevantTags)
!CheckCost(Handle, ActorInfo, OptionalRelevantTags)
!DoesAbilitySatisfyTagRequirements(*AbilitySystemComponent, SourceTags, TargetTags, OptionalRelevantTags)
AbilitySystemComponent->IsAbilityInputBlocked(Spec->InputID)
!K2_CanActivateAbility(*ActorInfo, Handle, OutTags)
最后的执行部分
void UGameplayAbility::CallActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, FOnGameplayAbilityEnded::FDelegate* OnGameplayAbilityEndedDelegate, const FGameplayEventData* TriggerEventData)
PreActivate(Handle, ActorInfo, ActivationInfo, OnGameplayAbilityEndedDelegate);
ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);
void UGameplayAbility::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
if (!CommitAbility(Handle, ActorInfo, ActivationInfo))
EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true, true);
...
可以自己设置Timer或者回调调用EndAbility或者CancelAbility
EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled);
void UGameplayAbility::CancelAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateCancelAbility)
在示例中,ActivateAbility创建GameplayTask进行非瞬时操作,通过绑定OnCompleted、OnCancelled、EventReceived进行GA流程控制
激活GA - 预测
执行策略:
namespace EGameplayAbilityNetExecutionPolicy
enum Type
LocalPredicted UMETA(DisplayName = "Local Predicted"),
LocalOnly UMETA(DisplayName = "Local Only"),
ServerInitiated UMETA(DisplayName = "Server Initiated"),
ServerOnly UMETA(DisplayName = "Server Only"),
通过PKey判断需要取消哪些GA
UAbilitySystemComponent::InternalTryActivateAbility
if (bCreateNewServerKey)
ActivationInfo.ServerSetActivationPredictionKey(FPredictionKey::CreateNewServerInitiatedKey(this));
else if (InPredictionKey.IsValidKey())
ActivationInfo.ServerSetActivationPredictionKey(InPredictionKey);
FScopedPredictionWindow ScopedPredictionWindow(this, ActivationInfo.GetActivationPredictionKey());
CallServerTryActivateAbility(Handle, Spec->InputPressed, ScopedPredictionKey);
ServerTryActivateAbility(AbilityHandle, InputPressed, PredictionKey);
InternalServerTryActivateAbility(Handle, InputPressed, PredictionKey, nullptr);
FGameplayAbilitySpec* Spec = FindAbilitySpecFromHandle(Handle);
FScopedPredictionWindow ScopedPredictionWindow(this, PredictionKey);
InternalTryActivateAbility(Handle, PredictionKey, &InstancedAbility, nullptr, TriggerEventData)
ClientActivateAbilityFailed(Handle, PredictionKey.Current);
Server失败的RPC通知
void UAbilitySystemComponent::ClientActivateAbilityFailed_Implementation(FGameplayAbilitySpecHandle Handle, int16 PredictionKey)
FGameplayAbilitySpec* Spec = FindAbilitySpecFromHandle(Handle);
if (Spec->ActivationInfo.GetActivationPredictionKey().Current == PredictionKey)
Spec->ActivationInfo.SetActivationRejected();
ActivationMode = EGameplayAbilityActivationMode::Rejected;
TArray<UGameplayAbility*> Instances = Spec->GetAbilityInstances();
for (UGameplayAbility* Ability : Instances)
if (Ability->CurrentActivationInfo.GetActivationPredictionKey().Current == PredictionKey)
Ability->K2_EndAbility();
EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicateEndAbility, bWasCancelled);
结束技能
void UGameplayAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled)
MyWorld->GetLatentActionManager().RemoveActionsForObject(this);
if (FAbilitySystemTweaks::ClearAbilityTimers)
MyWorld->GetTimerManager().ClearAllTimersForObject(this);
Task->TaskOwnerEnded();
AbilitySystemComponent->ReplicateEndOrCancelAbility(Handle, ActivationInfo, this, false);
AbilitySystemComponent->RemoveLooseGameplayTags(ActivationOwnedTags);
AbilitySystemComponent->RemoveGameplayCue(GameplayCueTag);
AbilitySystemComponent->HandleChangeAbilityCanBeCanceled(AbilityTags, this, false);
AbilitySystemComponent->ApplyAbilityBlockAndCancelTags(AbilityTags, this, false, BlockAbilitiesWithTag, false, CancelAbilitiesWithTag);
GameplayEffect
施加GE
FActiveGameplayEffectHandle UGameplayAbility::ApplyGameplayEffectSpecToOwner(const FGameplayAbilitySpecHandle AbilityHandle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEffectSpecHandle SpecHandle) const
if (SpecHandle.IsValid() && (HasAuthorityOrPredictionKey(ActorInfo, &ActivationInfo)))
return AbilitySystemComponent->ApplyGameplayEffectSpecToSelf(*SpecHandle.Data.Get(), AbilitySystemComponent->GetPredictionKeyForNewAction());
bool UGameplayAbility::HasAuthorityOrPredictionKey(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo* ActivationInfo) const
bool UAbilitySystemComponent::HasAuthorityOrPredictionKey(const FGameplayAbilityActivationInfo* ActivationInfo) const
bool UAbilitySystemComponent::CanPredict() const
bool IsValidForMorePrediction() const
return Current > 0 && bIsStale == false && bIsServerInitiated == false;
FPredictionKey GetPredictionKeyForNewAction() const
return ScopedPredictionKey.IsValidForMorePrediction() ? ScopedPredictionKey : FPredictionKey();
FActiveGameplayEffectHandle UAbilitySystemComponent::ApplyGameplayEffectSpecToSelf(const FGameplayEffectSpec &Spec, FPredictionKey PredictionKey)
if(PredictionKey.IsValidKey() && Spec.GetPeriod() > 0.f)
if(IsOwnerActorAuthoritative())
PredictionKey = FPredictionKey();
else
return FActiveGameplayEffectHandle();
FGameplayEffectSpec* OurCopyOfSpec = nullptr;
StackSpec = TSharedPtr<FGameplayEffectSpec>(new FGameplayEffectSpec(Spec));
OurCopyOfSpec = StackSpec.Get();
UAbilitySystemGlobals::Get().GlobalPreGameplayEffectSpecApply(*OurCopyOfSpec, this);
OurCopyOfSpec->CaptureAttributeDataFromTarget(this);
UAbilitySystemGlobals::Get().SetCurrentAppliedGE(OurCopyOfSpec);
ExecuteGameplayEffect(*OurCopyOfSpec, PredictionKey);
预测GE
当本地预测Instant GE的时候
FActiveGameplayEffectHandle UAbilitySystemComponent::ApplyGameplayEffectSpecToSelf(const FGameplayEffectSpec &Spec, FPredictionKey PredictionKey)
bool bTreatAsInfiniteDuration = GetOwnerRole() != ROLE_Authority && PredictionKey.IsLocalClientKey() && Spec.Def->DurationPolicy == EGameplayEffectDurationType::Instant;
if (Spec.Def->DurationPolicy != EGameplayEffectDurationType::Instant || bTreatAsInfiniteDuration)
AppliedEffect = ActiveGameplayEffects.ApplyGameplayEffectSpec(Spec, PredictionKey, bFoundExistingStackableGE);
将预测失败删除GE的函数绑定到PKey的同步,PKey同步说明了已经收到了服务器的GE,那么本地预测的部分就可以删除了
FActiveGameplayEffect* FActiveGameplayEffectsContainer::ApplyGameplayEffectSpec(const FGameplayEffectSpec& Spec, FPredictionKey& InPredictionKey, bool& bFoundExistingStackableGE)
AppliedActiveGE = new FActiveGameplayEffect(NewHandle, Spec, GetWorldTime(), GetServerWorldTime(), InPredictionKey);
*PendingGameplayEffectNext = AppliedActiveGE;
if (!(InPredictionKey.IsLocalClientKey() == false || IsNetAuthority()))
InPredictionKey.NewRejectOrCaughtUpDelegate(FPredictionKeyEvent::CreateUObject(Owner, &UAbilitySystemComponent::RemoveActiveGameplayEffect_NoReturn, AppliedActiveGE->Handle, -1));
void FActiveGameplayEffectsContainer::InternalOnActiveGameplayEffectAdded(FActiveGameplayEffect& Effect)
PKey使用FReplicatedPredictionKeyMap作为容器:
UPROPERTY(Replicated)
FReplicatedPredictionKeyMap UAbilitySystemComponent::ReplicatedPredictionKeyMap;
Params.Condition = COND_OwnerOnly;
DOREPLIFETIME_WITH_PARAMS_FAST(UAbilitySystemComponent, ReplicatedPredictionKeyMap, Params);
再看ScopedPredictionWindow的作用:在Scope结束后,恢复之前的PKey,并把当前的PKey加入ReplicatedPredictionKeyMap
FScopedPredictionWindow::FScopedPredictionWindow(UAbilitySystemComponent* AbilitySystemComponent, FPredictionKey InPredictionKey, bool InSetReplicatedPredictionKey )
if (AbilitySystemComponent->IsNetSimulating() == false)
Owner = AbilitySystemComponent;
RestoreKey = AbilitySystemComponent->ScopedPredictionKey;
AbilitySystemComponent->ScopedPredictionKey = InPredictionKey;
ClearScopedPredictionKey = true;
SetReplicatedPredictionKey = InSetReplicatedPredictionKey;
FScopedPredictionWindow::~FScopedPredictionWindow()
if (SetReplicatedPredictionKey)
if (OwnerPtr->ScopedPredictionKey.IsValidKey())
OwnerPtr->ReplicatedPredictionKeyMap.ReplicatePredictionKey(OwnerPtr->ScopedPredictionKey);
PredictionKeys[Index].PredictionKey = Key;
MarkItemDirty(PredictionKeys[Index]);
OwnerPtr->bIsNetDirty = true;
if (ClearScopedPredictionKey)
OwnerPtr->ScopedPredictionKey = RestoreKey;
ReplicatedPredictionKeyMap同步时:
bool FReplicatedPredictionKeyMap::NetDeltaSerialize(FNetDeltaSerializeInfo& DeltaParms)
return FastArrayDeltaSerialize<FReplicatedPredictionKeyItem>(PredictionKeys, DeltaParms, *this);
之后会调到
FReplicatedPredictionKeyItem
void PostReplicatedAdd(const struct FReplicatedPredictionKeyMap &InArray) { OnRep(); }
void PostReplicatedChange(const struct FReplicatedPredictionKeyMap &InArray) { OnRep(); }
在PKey同步的时候会触发委托
void FReplicatedPredictionKeyItem::OnRep()
FPredictionKeyDelegates::CatchUpTo(PredictionKey.Current);
for (auto& Delegate : DelPtr->CaughtUpDelegates)
Delegate.ExecuteIfBound();
void RemoveActiveGameplayEffect_NoReturn(FActiveGameplayEffectHandle Handle, int32 StacksToRemove=-1)
UAbilitySystemComponent::RemoveActiveGameplayEffect(Handle, StacksToRemove);
bool FActiveGameplayEffectsContainer::RemoveActiveGameplayEffect(FActiveGameplayEffectHandle Handle, int32 StacksToRemove)
InternalRemoveActiveGameplayEffect(ActiveGEIdx, StacksToRemove, true);
移除GE
bool FActiveGameplayEffectsContainer::InternalRemoveActiveGameplayEffect(int32 Idx, int32 StacksToRemove, bool bPrematureRemoval)
FActiveGameplayEffect& Effect = *GetActiveGameplayEffect(Idx);
if (StacksToRemove > 0 && Effect.Spec.StackCount > StacksToRemove)
Effect.Spec.StackCount -= StacksToRemove;
return false;
InternalOnActiveGameplayEffectRemoved(Effect, ShouldInvokeGameplayCueEvent, GameplayEffectRemovalInfo);
Owner->GetWorld()->GetTimerManager().ClearTimer(Effect.DurationHandle);
Owner->GetWorld()->GetTimerManager().ClearTimer(Effect.PeriodHandle);
InternalApplyExpirationEffects(Effect.Spec, bPrematureRemoval);
|