注:1.这个分类是按照源码里的注释分类的
2.本篇是通读并给出一些注释形式的,并不涉及结构性的分析
3.看之前要对UE的GAS系统的定义有初步了解
4.因为都是接口函数,有些没细看的研究那一部分的时候会细看
Call预测性添加的GC,移除标签并尝试InvokeGC事件ByTag
/** Called for predictively added gameplay cue. Needs to remove tag count and possible invoke OnRemove event if misprediction */
virtual void OnPredictiveGameplayCueCatchup(FGameplayTag Tag);
实现:
void UAbilitySystemComponent::OnPredictiveGameplayCueCatchup(FGameplayTag Tag)
{
// Remove it
RemoveLooseGameplayTag(Tag);
if (HasMatchingGameplayTag(Tag) == 0)
{
// Invoke Removed event if we no longer have this tag (probably a mispredict)
InvokeGameplayCueEvent(Tag, EGameplayCueEvent::Removed);
}
}
声明:
输入Handle,返回
/** Returns the total duration of a gameplay effect */
float GetGameplayEffectDuration(FActiveGameplayEffectHandle Handle) const;
实现:
调用FActiveGameplayEffectsContainer类型的GetGameplayEffectStartTimeAndDuration
float UAbilitySystemComponent::GetGameplayEffectDuration(FActiveGameplayEffectHandle Handle) const
{
float StartEffectTime = 0.0f;
float Duration = 0.0f;
ActiveGameplayEffects.GetGameplayEffectStartTimeAndDuration(Handle, StartEffectTime, Duration);
return Duration;
}
到了GetGameplayEffectStartTimeAndDuration中匹配Handle,然后调用下边这个获取
EffectStartTime = ActiveEffect.StartWorldTime;
EffectDuration = ActiveEffect.GetDuration();
通过游戏状态同步服务器时间的时候调用,来保持冷却时间与服务器的同步
/** Called whenever the server time replicates via the game state to keep our cooldown timers in sync with the server */
virtual void RecomputeGameplayEffectStartTimes(const float WorldTime, const float ServerWorldTime);
调用FActiveGameplayEffectsContainer类型的RecomputeStartWorldTimes
void UAbilitySystemComponent::RecomputeGameplayEffectStartTimes(const float WorldTime, const float ServerWorldTime)
{
ActiveGameplayEffects.RecomputeStartWorldTimes(WorldTime, ServerWorldTime);
}
他的声明和实现:
/** Recomputes the start time for all active abilities */
void RecomputeStartWorldTimes(const float WorldTime, const float ServerWorldTime);
void FActiveGameplayEffectsContainer::RecomputeStartWorldTimes(const float WorldTime, const float ServerWorldTime)
{
for (FActiveGameplayEffect& ActiveEffect : this)
{
ActiveEffect.RecomputeStartWorldTime(WorldTime, ServerWorldTime);
}
}
还是被我找到核心了,就是把服务器端的StartWorldTime同步到客户端
void FActiveGameplayEffect::RecomputeStartWorldTime(const float WorldTime, const float ServerWorldTime)
{
StartWorldTime = WorldTime - (ServerWorldTime - StartServerWorldTime);
}
一个接口,调用前面说过的FActiveGameplayEffectsContainer类型里的GetGameplayEffectStartTimeAndDuration
void UAbilitySystemComponent::GetGameplayEffectStartTimeAndDuration(FActiveGameplayEffectHandle Handle, float& StartEffectTime, float& Duration) const
{
return ActiveGameplayEffects.GetGameplayEffectStartTimeAndDuration(Handle, StartEffectTime, Duration);
}
声明://某些GE修改参数的幅值是SetByCaller的,也就是由调用者设置
/** Dynamically update the set-by-caller magnitude for an active gameplay effect */
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = GameplayEffects)
virtual void UpdateActiveGameplayEffectSetByCallerMagnitude(FActiveGameplayEffectHandle ActiveHandle, UPARAM(meta=(Categories = "SetByCaller"))FGameplayTag SetByCallerTag, float NewValue);
/** Dynamically update multiple set-by-caller magnitudes for an active gameplay effect */
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = GameplayEffects)
virtual void UpdateActiveGameplayEffectSetByCallerMagnitudes(FActiveGameplayEffectHandle ActiveHandle, const TMap& NewSetByCallerValues);
实现://接口继续调用FActiveGameplayEffectsContainer类型里的
void UAbilitySystemComponent::UpdateActiveGameplayEffectSetByCallerMagnitude(FActiveGameplayEffectHandle ActiveHandle, FGameplayTag SetByCallerTag, float NewValue)
{
ActiveGameplayEffects.UpdateActiveGameplayEffectSetByCallerMagnitude(ActiveHandle, SetByCallerTag, NewValue);
}
更新指定Handle的SetByCallerMagnitude,调用标签版本的,下边会贴一下
然后是计算Modifier
UpdateAllAggregatorModMagnitudes把要更新的加入一个堆AttributesToUpdate,然后调用UpdateAggregatorModMagnitudes(AttributesToUpdate, ActiveEffect);
void FActiveGameplayEffectsContainer::UpdateActiveGameplayEffectSetByCallerMagnitude(FActiveGameplayEffectHandle ActiveHandle, const FGameplayTag& SetByCallerTag, float NewValue)
{
if (FActiveGameplayEffect* Effect = GetActiveGameplayEffect(ActiveHandle))
{
Effect->Spec.SetSetByCallerMagnitude(SetByCallerTag, NewValue);
Effect->Spec.CalculateModifierMagnitudes();
MarkItemDirty(*Effect);
UpdateAllAggregatorModMagnitudes(*Effect);
}
}
void FGameplayEffectSpec::SetSetByCallerMagnitude(FName DataName, float Magnitude)
{
if (DataName != NAME_None)
{
SetByCallerNameMagnitudes.FindOrAdd(DataName) = Magnitude;
}
}
void FGameplayEffectSpec::SetSetByCallerMagnitude(FGameplayTag DataTag, float Magnitude)
{
if (DataTag.IsValid())
{
SetByCallerTagMagnitudes.FindOrAdd(DataTag) = Magnitude;
}
}
UpdateAggregatorModMagnitudes的实现:
void FActiveGameplayEffectsContainer::UpdateAggregatorModMagnitudes(const TSet& AttributesToUpdate, FActiveGameplayEffect& ActiveEffect)
{
const FGameplayEffectSpec& Spec = ActiveEffect.Spec;
for (const FGameplayAttribute& Attribute : AttributesToUpdate)
{
// skip over any modifiers for attributes that we don't have
if (!Owner || Owner->HasAttributeSetForAttribute(Attribute) == false)
{
continue;
}
FAggregator* Aggregator = FindOrCreateAttributeAggregator(Attribute).Get();
check(Aggregator);
// Update the aggregator Mods.
Aggregator->UpdateAggregatorMod(ActiveEffect.Handle, Attribute, Spec, ActiveEffect.PredictionKey.WasLocallyGenerated(), ActiveEffect.Handle);
}
}
UpdateActiveGameplayEffectSetByCallerMagnitudes就不贴了,逻辑上就是多了一步遍历调用UpdateActiveGameplayEffectSetByCallerMagnitude
声明:
/** Updates the level of an already applied gameplay effect. The intention is that this is 'seemless' and doesnt behave like removing/reapplying */
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = GameplayEffects)
virtual void SetActiveGameplayEffectLevel(FActiveGameplayEffectHandle ActiveHandle, int32 NewLevel);
追溯过程同理,这里直接贴最后调用的GE里的核心代码:
可以看到重新计算了Modifier
Effect.Spec.SetLevel(NewLevel);
MarkItemDirty(Effect);
Effect.Spec.CalculateModifierMagnitudes();
UpdateAllAggregatorModMagnitudes(Effect);
UsingQuery版本多了一步查询:
TArray ActiveGameplayEffectHandles = ActiveGameplayEffects.GetActiveEffects(Query);
约束(Inhibit)激活的GE使其无效但没有被移除
/** Inhibit an active gameplay effect so that it is disabled, but not removed */
UE_DEPRECATED(5.4, "Use SetActiveGameplayEffectInhibit with a MoveTemp(ActiveGEHandle) so it's clear the Handle is no longer valid. Check (then use) the returned FActiveGameplayEffectHandle to continue your operation.")
virtual void InhibitActiveGameplayEffect(FActiveGameplayEffectHandle ActiveGEHandle, bool bInhibit, bool bInvokeGameplayCueEvents);
这行比较关键,调用了SetActiveGameplayEffectInhibit
FActiveGameplayEffectHandle ContinuationHandle = SetActiveGameplayEffectInhibit(MoveTemp(ActiveGEHandle), bInhibit, bInvokeGameplayCueEvents);
SetActiveGameplayEffectInhibit的实现:
检查有效性
保证线程的安全性
更新约束状态
处理标签和修改器
处理状态变化的回调
FActiveGameplayEffectHandle UAbilitySystemComponent::SetActiveGameplayEffectInhibit(FActiveGameplayEffectHandle&& ActiveGEHandle, bool bInhibit, bool bInvokeGameplayCueEvents)
{
FActiveGameplayEffect* ActiveGE = ActiveGameplayEffects.GetActiveGameplayEffect(ActiveGEHandle);
if (!ActiveGE)
{
ABILITY_LOG(Error, TEXT("%s received bad Active GameplayEffect Handle: %s"), ANSI_TO_TCHAR(__func__), *ActiveGEHandle.ToString());
return FActiveGameplayEffectHandle();
}
if (ActiveGE->bIsInhibited != bInhibit)
{
ActiveGE->bIsInhibited = bInhibit;
// It's possible the adding or removing of the tags can invalidate the ActiveGE. As such,
// let's make sure we hold on to that memory until this function is done.
FScopedActiveGameplayEffectLock ScopeLockActiveGameplayEffects(ActiveGameplayEffects);
// All OnDirty callbacks must be inhibited until we update this entire GameplayEffect.
FScopedAggregatorOnDirtyBatch AggregatorOnDirtyBatcher;
if (bInhibit)
{
// Remove our ActiveGameplayEffects modifiers with our Attribute Aggregators
ActiveGameplayEffects.RemoveActiveGameplayEffectGrantedTagsAndModifiers(*ActiveGE, bInvokeGameplayCueEvents);
}
else
{
ActiveGameplayEffects.AddActiveGameplayEffectGrantedTagsAndModifiers(*ActiveGE, bInvokeGameplayCueEvents);
}
// The act of executing anything on the ActiveGE can invalidate it. So we need to recheck if we can continue to execute the callbacks.
if (!ActiveGE->IsPendingRemove)
{
ActiveGE->EventSet.OnInhibitionChanged.Broadcast(ActiveGEHandle, ActiveGE->bIsInhibited);
}
// We lost that it was active somewhere along the way, let the caller know
if (ActiveGE->IsPendingRemove)
{
return FActiveGameplayEffectHandle();
}
}