Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于4.5.4.1 多个Multiplay Modifiers共同作用的问题 #8

Open
JamesHuang0331 opened this issue Nov 5, 2024 · 0 comments
Open

Comments

@JamesHuang0331
Copy link

非issue,共同讨论一下文档中的提出的问题。

译者注: 说实话, 我没有搞懂下文中原文档作者的逻辑, 可能是没有充分了解项目的原因? 比如在样例项目中, 删除BP_DamageVolume的GameplayEffect中的Executions, 并按照下文例4添加两个Multiply Multipliers, Attribute均为GDAttributeSetBase.XP, Modifier Magnitude均为Scalable Float/5.0, 回到游戏, 击杀一个小兵使XP增加到1, 然后进入BP_DamageVolume, 会发现XP依次变为25, 625..., 进行调试也会发现是Modifier依次相乘的, 并不是作者所说的乘法分配律逻辑. 还有就是为什么符合公式规则的1 + (0.5 - 1) + (1.1 - 1) = 0.6是正确的而不符合公式规则的1 + (0.5 - 1) + (0.5 - 1) = 0和1 + (5 - 1) + (5 - 1) = 9就是错误预期? 这个正确和错误预期是以什么为评判标准的? 是否符合公式规则么? 如果各位明白其中道理, 还请不吝赐教, 在此感谢!

最近学习GAS,也遇到了一样的问题,根本原因是在遇到多个对于同一Attribute的Multiply/Divide Modifier时,GAS对于Instant GE,和Duration | Infinite GE 的处理不一样。
比方说,连续两个针对同一个属性的Multiply Modifier,系数都是1.5,那么:

  • Instant GE,最终效果是 1.5 * 1.5 = 2.25
  • Duration | Infinite GE,最终效果是 1 + (1.5 - 1) + (1.5 - 1) = 2

原因是:
对于Instant GE,会使用UAbilitySystemComponent::ExecuteGameplayEffect函数来计算,会通过遍历Modifier来迭代计算其对于属性的影响。

// FActiveGameplayEffectsContainer::ExecuteActiveEffectsFrom() 函数
for (int32 ModIdx = 0; ModIdx < SpecToUse.Modifiers.Num(); ++ModIdx)
{
	const FGameplayModifierInfo& ModDef = SpecToUse.Def->Modifiers[ModIdx];

	// ... 省略
		
	FGameplayModifierEvaluatedData EvalData(ModDef.Attribute, ModDef.ModifierOp, SpecToUse.GetModifierMagnitude(ModIdx, true));
	ModifierSuccessfullyExecuted |= InternalExecuteMod(SpecToUse, EvalData);
}

而对于Duration | Infinite GE,则会通过UAbilitySystemComponent::OnAttributeAggregatorDirty函数来响应其对属性的影响,而这个函数会调用到EvaluateWithBase()函数,通过SumMods预计算所有Multiply Modifier的系数和,来计算。

// FAggregatorModChannel::EvaluateWithBase() 函数
float Additive = SumMods(Mods[EGameplayModOp::Additive], GameplayEffectUtilities::GetModifierBiasByModifierOp(EGameplayModOp::Additive), Parameters);
float Multiplicitive = SumMods(Mods[EGameplayModOp::Multiplicitive], GameplayEffectUtilities::GetModifierBiasByModifierOp(EGameplayModOp::Multiplicitive), Parameters);
float Division = SumMods(Mods[EGameplayModOp::Division], GameplayEffectUtilities::GetModifierBiasByModifierOp(EGameplayModOp::Division), Parameters);

if (FMath::IsNearlyZero(Division))
{
	ABILITY_LOG(Warning, TEXT("Division summation was 0.0f in FAggregatorModChannel."));
	Division = 1.f;
}

return ((InlineBaseValue + Additive) * Multiplicitive) / Division;

而还有一个藏得更深的坑,对于Instant GE,客户端会有预测逻辑,会将其看作是Infinite GE看待。

// UAbilitySystemComponent::ApplyGameplayEffectSpecToSelf() 函数

// Clients should treat predicted instant effects as if they have infinite duration. The effects will be cleaned up later.
bool bTreatAsInfiniteDuration = GetOwnerRole() != ROLE_Authority && PredictionKey.IsLocalClientKey() && Spec.Def->DurationPolicy == EGameplayEffectDurationType::Instant;

也就会造成,在单个或者多个Instant GE针对某个Attribute拥有多个Multiply / Divide Modifier的情况下,客户端预测计算出来的值和实际服务器计算的值不一样!!这个错误的值可能会在一段时间后(如果当前网络状态较差,或者弱网络丢包,情况更严重),通过服务器的属性同步,修正为正确的值。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant