UE学习日志#4 GAS--ASC源码简要分析2

//注:没全部看完,全局上的理解暂时太少,后边会逐渐添加相关联的理解

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;
}

        这篇到这就结束了,这篇的作用相当于通读一遍理解一下,有些调用到的函数还不知道具体做了什么,但能知道大概都做了什么,随着看的越来越多最后都会看全的,到时候应该就通透了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值