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

Suggestion for section 4.5.4.1 #59

Open
GambaCodes opened this issue Aug 19, 2021 · 3 comments
Open

Suggestion for section 4.5.4.1 #59

GambaCodes opened this issue Aug 19, 2021 · 3 comments

Comments

@GambaCodes
Copy link

At the end of the section it's suggested that devs change engine code if they want their modifiers to be multiplied or divided instead of being added before the operation. While researching this issue, I ran into the concept of evaluation channels that were added in 4.14.

All you need to do is add the section below to DefaultGame.ini and then you can keep the original behavior and multiple or divide channels together.

[/Script/GameplayAbilities.AbilitySystemGlobals]
bAllowGameplayModEvaluationChannels=true
GameplayModEvaluationChannelAliases[0]="MyChannel01"
GameplayModEvaluationChannelAliases[1]="MyChannel02"

There are 10 channels defined in GameplayEffectTypes.h (EGameplayModEvaluationChannel), and for each channel you give a name to in the ini, you'll get to select that channel in the gameplay effect.

Then you can do Attribute * MyChannel01Mod * MyChannel02Mod.
Or even Attribute * MyChannel01Mod * (MyChannel02Mod1 + MyChannel02Mod2).

Since a lot of people rely on this documentation, which is great btw, I thought I'd suggest an alternate solution.

@GambaCodes GambaCodes changed the title Suggestion for section 4.5.41 Suggestion for section 4.5.4.1 Aug 19, 2021
@tranek
Copy link
Owner

tranek commented Sep 5, 2021

Hey, thank you.

I've heard of the evaluation channels before but have never used them. I don't want to include them without a working example.

Then you can do Attribute * MyChannel01Mod * MyChannel02Mod.
Or even Attribute * MyChannel01Mod * (MyChannel02Mod1 + MyChannel02Mod2).

Where does that happen?

@bohdon
Copy link

bohdon commented Oct 18, 2021

Excellent was looking for something like this, didn't realize it was a feature that was just hidden by default.

The description in those release notes is really helpful, quoting that here just because it was buried a bit:

New: Introduced the concept of "evaluation channels" to non-instant gameplay effects within the ability system! Evaluation channels enable game-specific control over the order in which modifiers are evaluated in attribute calculations.

A game may specify how many channels it would like to use, as well as what they are named, and then modifiers can specify which channel they work on. The channels are evaluated in order, with the numerical output of the first channel serving as the input to the second channel, and so on.

Example: Imagine a sample attribute with a base value of 100. Now apply a multiplicative mod with magnitude 1.1 in channel 0 and a multiplicative mod with magnitude 1.1 in channel 1. The result will evaluate as ((100 * 1.1) * 1.1) instead of (100 * 1.2).

To use channels in a game, two settings within the INI section for AbilitySystemGlobals must be configured. First, bAllowGameplayModEvaluationChannels must be set to true, and second, the channels that should be used (and their names) must be specified in GameplayModEvaluationChannelAliases. Additionally, a game can optionally specify a default channel to use via DefaultGameplayModEvaluationChannel.

In addition, added various new channel-related functionality to the ability classes' API, such as the ability to base the magnitude of a modifier off of an attribute only evaluated up to a certain channel.

And here's some more important snippets from source after reading into them more:

Each channel gets evaluated in order, with the final result from the previous channel used as the base value for the next channel. This happens in FAggregatorModChannelContainer::EvaluateWithBase:

float FAggregatorModChannelContainer::EvaluateWithBase(float InlineBaseValue, const FAggregatorEvaluateParameters& Parameters) const
{
	float ComputedValue = InlineBaseValue;

	for (auto& ChannelEntry : ModChannelsMap)
	{
		const FAggregatorModChannel& CurChannel = ChannelEntry.Value;
		ComputedValue = CurChannel.EvaluateWithBase(ComputedValue, Parameters);
	}

	return ComputedValue;
}

Note that although it's iterating a map, the order of the map is automatically managed in FAggregatorModChannelContainer::FindOrAddModChannel, to make sure that the channels are always evaluated in order:

FAggregatorModChannel& FAggregatorModChannelContainer::FindOrAddModChannel(EGameplayModEvaluationChannel Channel)
{
	FAggregatorModChannel* FoundChannel = ModChannelsMap.Find(Channel);
	if (!FoundChannel)
	{
		// Adding a new channel, need to resort the map to preserve key order for evaluation
		ModChannelsMap.Add(Channel);
		ModChannelsMap.KeySort(TLess<EGameplayModEvaluationChannel>());
		FoundChannel = ModChannelsMap.Find(Channel);
	}
	check(FoundChannel);
	return *FoundChannel;
}

Here's a practical example in which a character's move speed is being overridden by a Run ability, and then multiplied afterwards to apply a Slow debuff:

  • GE_Run sets the attribute to exactly 650
    gas_mod_channels01

  • GE_Slowed multiplies the attribute by 0.25
    gas_mod_channels02

Since all modifiers in Channel0 are fully evaluated before any modifiers in Channel1, the Override modifier op can be used here while still allowing Add or Multiply operations afterwards by putting them in a higher channel.

One last visual representation of how a subset of the modifier operations within each channel are used for the above example, with 350 as an arbitrary base value:

  • BaseValue = 350
  • Channel0Value = Override (650) OR ((BaseValue + Additive) * Multiplicitive) / Division
  • Channel1Value = Override OR ((Channel0Value + Additive) * Multiplicitive (0.25)) / Division
  • CurrentValue = (650 * 0.25) = 162.5

Thanks again for this great resource 🙏🏼

@namrog84
Copy link

This is really useful and great! I was about to start looking into how to modify EvaluateWithBase and really didn't want to dig into engine code to make a simple change. That fixes what I thought was a GAS shortcoming perfectly!

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

4 participants