//注:没全部看完,全局上的理解暂时太少,后边会逐渐添加相关联的理解
3 GA相关(按注释里的分类,并不是所有相关的)
3.1 几个委托
/** Called when ability fails to activate, passes along the failed ability and a tag explaining why */
//当能力没能激活时
DECLARE_MULTICAST_DELEGATE_TwoParams(FAbilityFailedDelegate, const UGameplayAbility*, const FGameplayTagContainer&);
/** Called when ability ends */
//能力结束时
DECLARE_MULTICAST_DELEGATE_OneParam(FAbilityEnded, UGameplayAbility*);
/** Notify interested parties that ability spec has been modified */
//将已经被修改的ability spec通知给感兴趣的部分
DECLARE_MULTICAST_DELEGATE_OneParam(FAbilitySpecDirtied, const FGameplayAbilitySpec&);
/** Used to register callbacks to ability-key input */
//注册能力绑定的input的回调
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAbilityAbilityKey, /*UGameplayAbility*, Ability, */int32, InputID);
/** Used to register callbacks to confirm/cancel input */
//注册确认/取消input的回调
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FAbilityConfirmOrCancel);
3.2 EConsiderPending
暂时是按注释理解的:不考虑,考虑即将添加的能力,考虑即将移除的能力,都考虑
/** When performing actions (such as gathering activatable abilities), how do we deal with Pending items (e.g. abilities not yet added or removed) */
/** 执行操作时(例如收集可激活的能力),如何处理待处理项目(例如尚未添加或移除的能力) */
enum class EConsiderPending : uint8
{
/** Don't consider any Pending actions (such as Pending Abilities Added or Removed) */
None = 0,
/** Consider Pending Adds when performing the action */
PendingAdd = (1 << 0),
/** Consider Pending Removes when performing the action */
PendingRemove = (1 << 1),
All = PendingAdd | PendingRemove
};
ENUM_CLASS_FLAGS(EConsiderPending)
3.3 GiveAbility
/*
* Grants an Ability.
* This will be ignored if the actor is not authoritative.
* Returns handle that can be used in TryActivateAbility, etc.
*
* @param AbilitySpec FGameplayAbilitySpec containing information about the ability class, level and input ID to bind it to.
*/
/*授予一个能力
如果角色没有权限,将忽略此操作
返回一个可以在 TryActivateAbility 等函数中使用的句柄
@param AbilitySpec FGameplayAbilitySpec包含关于类、等级和要绑定的输入 ID 的信息*/
FGameplayAbilitySpecHandle GiveAbility(const FGameplayAbilitySpec& AbilitySpec);
//下面是实现
FGameplayAbilitySpecHandle UAbilitySystemComponent::GiveAbility(const FGameplayAbilitySpec& Spec)
{
if (!IsValid(Spec.Ability))
{
ABILITY_LOG(Error, TEXT("GiveAbility called with an invalid Ability Class."));
return FGameplayAbilitySpecHandle();
}
//检查Spec中的Ability是否有效,如果无效,则记录错误并返回一个空的
if (!IsOwnerActorAuthoritative())
{
ABILITY_LOG(Error, TEXT("GiveAbility called on ability %s on the client, not allowed!"), *Spec.Ability->GetName());
return FGameplayAbilitySpecHandle();
}
//检查角色是否具有权限,如果没有权限(客户端),则记录错误并返回空
// If locked, add to pending list. The Spec.Handle is not regenerated when we receive, so returning this is ok.
//如果锁定了,就加入pending list,Spec.Handle不会再生成,所以可以直接返回这个Handle
if (AbilityScopeLockCount > 0)
{
UE_LOG(LogAbilitySystem, Verbose, TEXT("%s: GiveAbility %s delayed (ScopeLocked)"), *GetNameSafe(GetOwner()), *GetNameSafe(Spec.Ability));
AbilityPendingAdds.Add(Spec);
return Spec.Handle;
}
ABILITYLIST_SCOPE_LOCK();
FGameplayAbilitySpec& OwnedSpec = ActivatableAbilities.Items[ActivatableAbilities.Items.Add(Spec)];
if (OwnedSpec.Ability->GetInstancingPolicy() == EGameplayAbilityInstancingPolicy::InstancedPerActor)
{
// Create the instance at creation time
CreateNewInstanceOfAbility(OwnedSpec, Spec.Ability);
}
//否则,直接将能力添加到ActivatableAbilities列表中,并根据能力的实例化策略(InstancedPerActor)决定是否创建新实例
OnGiveAbility(OwnedSpec);
MarkAbilitySpecDirty(OwnedSpec, true);
//调用OnGiveAbility方法处理赋能逻辑,并标记能力为已更改
UE_LOG(LogAbilitySystem, Log, TEXT("%s: GiveAbility %s [%s] Level: %d Source: %s"), *GetNameSafe(GetOwner()), *GetNameSafe(Spec.Ability), *Spec.Handle.ToString(), Spec.Level, *GetNameSafe(Spec.SourceObject.Get()));
UE_VLOG(GetOwner(), VLogAbilitySystem, Log, TEXT("GiveAbility %s [%s] Level: %d Source: %s"), *GetNameSafe(Spec.Ability), *Spec.Handle.ToString(), Spec.Level, *GetNameSafe(Spec.SourceObject.Get()));
return OwnedSpec.Handle;
}
3.4 GiveAbilityAndActivateOnce
赋予能力,尝试激活一次并移除
前边跟GiveAbility一样的就不贴了
Spec.bActivateOnce = true;
FGameplayAbilitySpecHandle AddedAbilityHandle = GiveAbility(Spec);
FGameplayAbilitySpec* FoundSpec = FindAbilitySpecFromHandle(AddedAbilityHandle);
if (FoundSpec)
{
FoundSpec->RemoveAfterActivation = true;
if (!InternalTryActivateAbility(AddedAbilityHandle, FPredictionKey(), nullptr, nullptr, GameplayEventData))
{
// We failed to activate it, so remove it now
//尝试激活一次
ClearAbility(AddedAbilityHandle);
return FGameplayAbilitySpecHandle();
}
}
else if (GameplayEventData)
{
// Cache the GameplayEventData in the pending spec (if it was correctly queued)
//在pendingSpec里缓存GameplayEventData
FGameplayAbilitySpec& PendingSpec = AbilityPendingAdds.Last();
if (PendingSpec.Handle == AddedAbilityHandle)
{
PendingSpec.GameplayEventData = MakeShared<FGameplayEventData>(*GameplayEventData);
}
}
return AddedAbilityHandle;
再往下是3.3和3.4的K2版本,蓝图调用用的
3.5 ClearAllAbilities
具体理解写在了注释里
void UAbilitySystemComponent::ClearAllAbilities()
{
// If this is called inside an ability scope lock, postpone the workload until end of scope.
// This was introduced for abilities that trigger their owning actor's destruction on ability
// activation.
if (AbilityScopeLockCount > 0)
{
bAbilityPendingClearAll = true;
return;
}
if (!IsOwnerActorAuthoritative())
{
ABILITY_LOG(Error, TEXT("Attempted to call ClearAllAbilities() without authority."));
return;
}
// Note we aren't marking any old abilities pending kill. This shouldn't matter since they will be garbage collected.
ABILITYLIST_SCOPE_LOCK();
for (FGameplayAbilitySpec& Spec : ActivatableAbilities.Items)
{
OnRemoveAbility(Spec);
}
//移除能力并调用相关的一些行为
// Let's add some enhanced checking if requested
if (CVarEnsureAbilitiesEndGracefully.GetValueOnGameThread())
{
for (FGameplayAbilitySpec& Spec : ActivatableAbilities.Items)
{
if (Spec.IsActive())
{
ensureAlwaysMsgf(Spec.Ability->GetInstancingPolicy() != EGameplayAbilityInstancingPolicy::NonInstanced, TEXT("%hs: %s was still active (ActiveCount = %d). Since it's not instanced, it's likely that TryActivateAbility and EndAbility are not matched."), __func__, *GetNameSafe(Spec.Ability), Spec.ActiveCount);
ensureAlwaysMsgf(Spec.Ability->GetInstancingPolicy() == EGameplayAbilityInstancingPolicy::NonInstanced, TEXT("%hs: %s was still active. Since it's an instanced ability, it's likely that there's an issue with the flow of EndAbility or RemoveAbility (such as not calling the Super function)."), __func__, *GetNameSafe(Spec.Ability));
}
}
}
//如果开启了检查会再检查一次,确认一些信息
ActivatableAbilities.Items.Empty(ActivatableAbilities.Items.Num());
ActivatableAbilities.MarkArrayDirty();
//通过 Empty() 方法清空 ActivatableAbilities.Items 列表,彻底移除所有能力。同时,调用 MarkArrayDirty() 方法标记,通知系统数据已经发生了变化
CheckForClearedAbilities();
bAbilityPendingClearAll = false;
//将 bAbilityPendingClearAll 恢复为 false,表示能力清除操作已经完成。
}
3.6 ClearAllAbilitiesWithInputID
声明://用于清除所有绑定InputID的abilities
/**
* Clears all abilities bound to a given Input ID
* This will be ignored if the actor is not authoritative
*
* @param InputID The numeric Input ID of the abilities to remove
*/
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Gameplay Abilities")
void ClearAllAbilitiesWithInputID(int32 InputID = 0);
实现://找到所有匹配的abilities然后依次调用ClearAbility
void UAbilitySystemComponent::ClearAllAbilitiesWithInputID(int32 InputID /*= 0*/)
{
// find all matching abilities
TArray<const FGameplayAbilitySpec*> OutSpecs;
FindAllAbilitySpecsFromInputID(InputID, OutSpecs);
// iterate through the bound abilities
for (const FGameplayAbilitySpec* CurrentSpec : OutSpecs)
{
// clear the ability
ClearAbility(CurrentSpec->Handle);
}
}
3.7 ClearAbility
传入Handle来Clear
声明:
/**
* Removes the specified ability.
* This will be ignored if the actor is not authoritative.
*
* @param Handle Ability Spec Handle of the ability we want to remove
*/
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Gameplay Abilities")
void ClearAbility(const FGameplayAbilitySpecHandle& Handle);
实现:
大概流程还是先检查权限,之后如果在AbilityPendingAdds里就直接移除,后边就是根据维护的锁定状态来分情况,在锁就加入移除队列,没锁就执行移除、标记等相关操作
void UAbilitySystemComponent::ClearAbility(const FGameplayAbilitySpecHandle& Handle)
{
if (!IsOwnerActorAuthoritative())
{
ABILITY_LOG(Error, TEXT("Attempted to call ClearAbility() on the client. This is not allowed!"));
return;
}
for (int Idx = 0; Idx < AbilityPendingAdds.Num(); ++Idx)
{
if (AbilityPendingAdds[Idx].Handle == Handle)
{
AbilityPendingAdds.RemoveAtSwap(Idx, EAllowShrinking::No);
return;
}
}
for (int Idx = 0; Idx < ActivatableAbilities.Items.Num(); ++Idx)
{
check(ActivatableAbilities.Items[Idx].Handle.IsValid());
if (ActivatableAbilities.Items[Idx].Handle == Handle)
{
if (AbilityScopeLockCount > 0)
{
if (ActivatableAbilities.Items[Idx].PendingRemove == false)
{
ActivatableAbilities.Items[Idx].PendingRemove = true;
AbilityPendingRemoves.Add(Handle);
}
}
else
{
{
// OnRemoveAbility will possibly call EndAbility. EndAbility can "do anything" including remove this abilityspec again. So a scoped list lock is necessary here.
ABILITYLIST_SCOPE_LOCK();
OnRemoveAbility(ActivatableAbilities.Items[Idx]);
ActivatableAbilities.Items.RemoveAtSwap(Idx);
ActivatableAbilities.MarkArrayDirty();
}
CheckForClearedAbilities();
}
return;
}
}
}
3.8 SetRemoveAbilityOnEnd
在ability完成时进行移除,如果当前有效就立刻终止,同时clear InputID和Spec
/** Sets an ability spec to remove when its finished. If the spec is not currently active, it terminates it immediately. Also clears InputID of the Spec. */
void SetRemoveAbilityOnEnd(FGameplayAbilitySpecHandle AbilitySpecHandle);
如果处于激活状态就标记为激活后移除,否则就直接调用ClearAbility
void UAbilitySystemComponent::SetRemoveAbilityOnEnd(FGameplayAbilitySpecHandle AbilitySpecHandle)
{
FGameplayAbilitySpec* FoundSpec = FindAbilitySpecFromHandle(AbilitySpecHandle);
if (FoundSpec)
{
if (FoundSpec->IsActive())
{
FoundSpec->RemoveAfterActivation = true;
}
else
{
ClearAbility(AbilitySpecHandle);
}
}
}
3.9 GetActivatableGameplayAbilitySpecsByAllMatchingTags
先来看这段巨长无比的注释:
获取所有 符合GameplayTagContainer 中所有标签&&DoesAbilitySatisfyTagRequirements()返回 true 的 Activatable GA。后者的要求允许此函数找到正确的能力,而无需提前了解ability的详细信息。例如,如果有两个“近战”能力,其中一个要求有武器,而另一个要求没有武器,那么这些能力可以使用Blocking和Required标签来确定何时可以开打。使用“满足需要的标签”简化了很多使用案例。例如,行为树可以使用各种装饰器来测试通过这种机制获取的ability,并且在执行能力的任务时,不需要知道实际上可能存在多个这样的能力。
/**
* Gets all Activatable Gameplay Abilities that match all tags in GameplayTagContainer AND for which
* DoesAbilitySatisfyTagRequirements() is true. The latter requirement allows this function to find the correct
* ability without requiring advanced knowledge. For example, if there are two "Melee" abilities, one of which
* requires a weapon and one of which requires being unarmed, then those abilities can use Blocking and Required
* tags to determine when they can fire. Using the Satisfying Tags requirements simplifies a lot of usage cases.
* For example, Behavior Trees can use various decorators to test an ability fetched using this mechanism as well
* as the Task to execute the ability without needing to know that there even is more than one such ability.
*/
void GetActivatableGameplayAbilitySpecsByAllMatchingTags(const FGameplayTagContainer& GameplayTagContainer, TArray < struct FGameplayAbilitySpec* >& MatchingGameplayAbilities, bool bOnlyAbilitiesThatSatisfyTagRequirements = true) const;
实现:
前面那个bOnlyAbilitiesThatSatisfyTagRequirements如果为false后边就不用判断了,直接加入MathcingGameplayAbilities,这应该是一个控制需不需要进一步检查的变量
void UAbilitySystemComponent::GetActivatableGameplayAbilitySpecsByAllMatchingTags(const FGameplayTagContainer& GameplayTagContainer, TArray < struct FGameplayAbilitySpec* >& MatchingGameplayAbilities, bool bOnlyAbilitiesThatSatisfyTagRequirements) const
{
if (!GameplayTagContainer.IsValid())
{
return;
}
for (const FGameplayAbilitySpec& Spec : ActivatableAbilities.Items)
{
if (Spec.Ability && Spec.Ability->GetAssetTags().HasAll(GameplayTagContainer))
{
// Consider abilities that are blocked by tags currently if we're supposed to (default behavior).
// That way, we can use the blocking to find an appropriate ability based on tags when we have more than
// one ability that match the GameplayTagContainer.
if (!bOnlyAbilitiesThatSatisfyTagRequirements || Spec.Ability->DoesAbilitySatisfyTagRequirements(*this))
{
MatchingGameplayAbilities.Add(const_cast<FGameplayAbilitySpec*>(&Spec));
}
}
}
}
3.10 TryActivateAbilitiesByTag/ByClass
这两个TryActivate就放一起了,其实就是加了一些条件来筛选,核心是下一个TryActivateAbility
bool UAbilitySystemComponent::TryActivateAbilitiesByTag(const FGameplayTagContainer& GameplayTagContainer, bool bAllowRemoteActivation)
{
TArray<FGameplayAbilitySpec*> AbilitiesToActivatePtrs;
GetActivatableGameplayAbilitySpecsByAllMatchingTags(GameplayTagContainer, AbilitiesToActivatePtrs);
if (AbilitiesToActivatePtrs.Num() < 1)
{
return false;
}
//获取所有能激活的能力,如果没有就直接返回false
// Convert from pointers (which can be reallocated, since they point to internal data) to copies of that data
TArray<FGameplayAbilitySpec> AbilitiesToActivate;
AbilitiesToActivate.Reserve(AbilitiesToActivatePtrs.Num());
Algo::Transform(AbilitiesToActivatePtrs, AbilitiesToActivate, [](FGameplayAbilitySpec* SpecPtr) { return *SpecPtr; });
//将指针转换为副本
bool bSuccess = false;
for (const FGameplayAbilitySpec& GameplayAbilitySpec : AbilitiesToActivate)
{
ensure(IsValid(GameplayAbilitySpec.Ability));
bSuccess |= TryActivateAbility(GameplayAbilitySpec.Handle, bAllowRemoteActivation);
}
//遍历来激活
return bSuccess;
}
bool UAbilitySystemComponent::TryActivateAbilityByClass(TSubclassOf<UGameplayAbility> InAbilityToActivate, bool bAllowRemoteActivation)
{
bool bSuccess = false;
//获取这个类的默认对象
const UGameplayAbility* const InAbilityCDO = InAbilityToActivate.GetDefaultObject();
for (const FGameplayAbilitySpec& Spec : ActivatableAbilities.Items)
{
if (Spec.Ability == InAbilityCDO)//于默认对象相等则满足条件,分别TryActive
{
bSuccess |= TryActivateAbility(Spec.Handle, bAllowRemoteActivation);
break;
}
}
return bSuccess;
}
3.11 TryActivateAbility
这段看起来巨长无比,其实就是分个情况调用别的函数,先进行一堆检查,然后通过策略选择在客户端激活还是在服务器端激活
bool UAbilitySystemComponent::TryActivateAbility(FGameplayAbilitySpecHandle AbilityToActivate, bool bAllowRemoteActivation)
{
FGameplayTagContainer FailureTags;
FGameplayAbilitySpec* Spec = FindAbilitySpecFromHandle(AbilityToActivate);
if (!Spec)
{
ABILITY_LOG(Warning, TEXT("TryActivateAbility called with invalid Handle"));
return false;
}
// don't activate abilities that are waiting to be removed
if (Spec->PendingRemove || Spec->RemoveAfterActivation)
{
return false;
}
UGameplayAbility* Ability = Spec->Ability;
if (!Ability)
{
ABILITY_LOG(Warning, TEXT("TryActivateAbility called with invalid Ability"));
return false;
}
const FGameplayAbilityActorInfo* ActorInfo = AbilityActorInfo.Get();
// make sure the ActorInfo and then Actor on that FGameplayAbilityActorInfo are valid, if not bail out.
if (ActorInfo == nullptr || !ActorInfo->OwnerActor.IsValid() || !ActorInfo->AvatarActor.IsValid())
{
return false;
}
const ENetRole NetMode = ActorInfo->AvatarActor->GetLocalRole();
// This should only come from button presses/local instigation (AI, etc).
if (NetMode == ROLE_SimulatedProxy)
{
return false;
}
bool bIsLocal = AbilityActorInfo->IsLocallyControlled();
// Check to see if this a local only or server only ability, if so either remotely execute or fail
if (!bIsLocal && (Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::LocalOnly || Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::LocalPredicted))
{
if (bAllowRemoteActivation)
{
ClientTryActivateAbility(AbilityToActivate);
return true;
}
ABILITY_LOG(Log, TEXT("Can't activate LocalOnly or LocalPredicted ability %s when not local."), *Ability->GetName());
return false;
}
if (NetMode != ROLE_Authority && (Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::ServerOnly || Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::ServerInitiated))
{
if (bAllowRemoteActivation)
{
FScopedCanActivateAbilityLogEnabler LogEnabler;
if (Ability->CanActivateAbility(AbilityToActivate, ActorInfo, nullptr, nullptr, &FailureTags))
{
// No prediction key, server will assign a server-generated key
CallServerTryActivateAbility(AbilityToActivate, Spec->InputPressed, FPredictionKey());
return true;
}
else
{
NotifyAbilityFailed(AbilityToActivate, Ability, FailureTags);
return false;
}
}
ABILITY_LOG(Log, TEXT("Can't activate ServerOnly or ServerInitiated ability %s when not the server."), *Ability->GetName());
return false;
}
return InternalTryActivateAbility(AbilityToActivate);
}
3.12 HasActivatableTriggeredAbility
bool 类型返回是否存在能触发的ability
(篇幅原因后边简单的就不贴了,介绍下功能就行)
3.13 TriggerAbilityFromGameplayEvent
这个还是得看下声明,实现就是处理一些条件信息后,用这些信息去InternalTryActivateAbility
/** Triggers an ability from a gameplay event, will only trigger on local/server depending on execution flags */
bool TriggerAbilityFromGameplayEvent(FGameplayAbilitySpecHandle AbilityToTrigger, FGameplayAbilityActorInfo* ActorInfo, FGameplayTag Tag, const FGameplayEventData* Payload, UAbilitySystemComponent& Component);
这篇先到这,但是还没结束,还差Ability Cancelling/Interrupts,到这其实我稍微理解点了,我还发现这么写太浪费精力了,之后我会只贴一些我不太理解的或者需要注意的点,将重点放在调用的流程上。
(1.19补充)4 AbilityCancelling/Interrupts
4.1 CancelAbility
从AcivatableAbilities.Items中找对应的ability,调用CancelAbilitySpec
void UAbilitySystemComponent::CancelAbility(UGameplayAbility* Ability)
{
ABILITYLIST_SCOPE_LOCK();
for (FGameplayAbilitySpec& Spec : ActivatableAbilities.Items)
{
if (Spec.Ability == Ability)
{
CancelAbilitySpec(Spec, nullptr);
}
}
}
4.2 CancelAbilityHandle
同上,就是换成了指定Handle
4.3 CancelAbilities
这个源码也很简单就不贴了,总结来说就是遍历找到激活的能力,取消符合相关标签限制的Ability
4.4 CancelAllAbilities
遍历调用CancelAbilitySpec
4.5 DestroyActiveState
这个Destroy是分权限的,后边的情况还是用的标记为Garbage解决的
void UAbilitySystemComponent::DestroyActiveState()
{
// If we haven't already begun being destroyed
//如果还没开始销毁
if (!bDestroyActiveStateInitiated && ((GetFlags() & RF_BeginDestroyed) == 0))
{
// Avoid re-entrancy (ie if during CancelAbilities() an EndAbility callback destroys the Actor owning this ability system)
//防止再次进入
bDestroyActiveStateInitiated = true;
// Cancel all abilities before we are destroyed.
//在Destroyed之前先取消所有能力
FGameplayAbilityActorInfo* ActorInfo = AbilityActorInfo.Get();
// condition needed since in edge cases canceling abilities
// while not having valid owner/ability component can crash
if (ActorInfo && ActorInfo->OwnerActor.IsValid(true) && ActorInfo->AbilitySystemComponent.IsValid(true))
{
CancelAbilities();
}
if (IsOwnerActorAuthoritative())
{
// We should now ClearAllAbilities because not all abilities CanBeCanceled().
// This will gracefully call EndAbility and clean-up all instances of the abilities.
ClearAllAbilities();
//调用ClearAllAbilities,会调用EndAbility
}
else
{
// If we're a client, ClearAllAbilities won't execute and we should clean up these instances manually.
// CancelAbilities() will only MarkPending kill InstancedPerExecution abilities.
// TODO: Is it correct to simply mark these as Garbage rather than EndAbility? I suspect not, but this
// is ingrained behavior (circa 2015). Perhaps better to allow ClearAllAbilities on client if bDestroyActiveStateInitiated (Nov 2023).
for (FGameplayAbilitySpec& Spec : ActivatableAbilities.Items)
{
TArray<UGameplayAbility*> AbilitiesToCancel = Spec.GetAbilityInstances();
for (UGameplayAbility* InstanceAbility : AbilitiesToCancel)
{
if (InstanceAbility)
{
InstanceAbility->MarkAsGarbage();
}
}
Spec.ReplicatedInstances.Empty();
Spec.NonReplicatedInstances.Empty();
}
//这里还是标记为Garbage,清空Spec.ReplicatedInstances和Spec.NonReplicatedInstances
}
}
}
4.6 ApplyAbilityBlockAndCancelTags
这个之前的GAS--GA的Tag篇提到过,就是调用Block和Cancel带有相关Tag的ability
4.7 HandleChangeAbilityCanBeCanceled
这个函数在ability变为能取消/不能取消的时候调用,默认不做任何事,可以自己绑定一些事件
4.8 AreAbilityTagsBlocked
有任何BlockedAbilityTags里的就返回真,否则返回假
return Tags.HasAny(BlockedAbilityTags.GetExplicitGameplayTags());
4.9 BlockAbilitiesWithTags/UnBlockAbilitiesWithTags/BlockAbilitiyByInputID/UnBlockAbilityByInputID
字面意思看下实现就懂了:
void UAbilitySystemComponent::BlockAbilitiesWithTags(const FGameplayTagContainer& Tags)
{
BlockedAbilityTags.UpdateTagCount(Tags, 1);
}
void UAbilitySystemComponent::UnBlockAbilitiesWithTags(const FGameplayTagContainer& Tags)
{
BlockedAbilityTags.UpdateTagCount(Tags, -1);
}
void UAbilitySystemComponent::BlockAbilityByInputID(int32 InputID)
{
const TArray<uint8>& ConstBlockedAbilityBindings = GetBlockedAbilityBindings();
if (InputID >= 0 && InputID < ConstBlockedAbilityBindings.Num())
{
++GetBlockedAbilityBindings_Mutable()[InputID];
}
}
void UAbilitySystemComponent::UnBlockAbilityByInputID(int32 InputID)
{
const TArray<uint8>& ConstBlockedAbilityBindings = GetBlockedAbilityBindings();
if (InputID >= 0 && InputID < ConstBlockedAbilityBindings.Num() && ConstBlockedAbilityBindings[InputID] > 0)
{
--GetBlockedAbilityBindings_Mutable()[InputID];
}
}
4.10 IsAbilityInputBlocked
指定InputID是否被Block,是就返回true,否则返回false
bool UAbilitySystemComponent::IsAbilityInputBlocked(int32 InputID) const
{
// Check if this ability's input binding is currently blocked
const TArray<uint8>& ConstBlockedAbilityBindings = GetBlockedAbilityBindings();
if (InputID >= 0 && InputID < ConstBlockedAbilityBindings.Num() && ConstBlockedAbilityBindings[InputID] > 0)
{
return true;
}
return false;
}
这篇到这就结束了,这篇的作用相当于通读一遍理解一下,有些调用到的函数还不知道具体做了什么,但能知道大概都做了什么,随着看的越来越多最后都会看全的,到时候应该就通透了。