Download

GameplayAbilities questions

Howdy!

I’m currently working on adding the GameplayAbilities system into my project and have a few questions about how the system works.

  1. In the GameplayEffect editor, how do you get Attributes from certain AttributeSets to show up in the modifier list? Right now all I see are AbilityComponent.Incoming/OutgoingDuration.

  2. Why are certain Attributes supposed to be done in the execution section (like health) vs the modifier section? Is this just for attributes that scale with level?

  3. How would I go about building an ExecutionCalculation class? I’m not sure I understand how they work.

Thanks a lot. I’m really excited to get this set up for our game!

Edit: May be worth noting I’m running in 4.7 preview.

-Matt

I’m going to start my answer with this big disclaimer: The abilities system is still under active development and isn’t considered ready for widespread external use. It’s possible that we will make big changes that break all of your data. The further we get with things the less likely this is but it is still a possibility at this point. When we think this feature is ready for more general use there will be an announcement. For now, use at your own risk.

  1. To appear in the attributes drop down in the modifier list an attribute needs to be a member of a class derived form UAttributeSet, have a UProperty, and be of type float.

class UMyAttributeSet : public UAttributeSet
{
	...

	UProperty()
	float MyAttribute;
};

  1. Most things should modify attributes using modifiers. Executions are for custom code. Health is an example that could go either way depending on your game.

At the most basic level you could simply have a health attribute that is increased or decreased when you take damage or are healed.

For a slightly more complicated example, you might have three attributes: health, damage and healing. In this case players apply damage or healing to their target. Inside your attribute set you’d implement PostGameplayEffectExecute. If the modified attribute is damage or healing then you convert these to positive or negative health changes, apply the change to health, and zero the damage/healing attribute.

Finally, we might want to make our example really complicated. Let’s say I need to be able to do calculations on the damage at various stages and apply buffs/debuffs at each stage. For this scenario I’d use an execution instead of a modifier. For this you’d write a class that inherits from UGameplayEffectExecutionCalculation. Inside the Execute_Implementation function you’d put all of your logic to calculate the final damage numbers.

Hopefully that’s enough to get you started.

Is GameplayAbilities editor easiest to enable on 4.7 than 4.6.1?
When I’ve try to edit an AbilityBlueprint after enable then, the editor always crashes by lack of a GameplayTag about GameplayCue.

GameplayTagsManager line 313. :frowning:

Thanks Fred, that explanation clears things up a bit about when to use modifiers vs executions.

Going back through my code I realized that I had copied over a UCLASS parameter that was filtering all my attributes out of the editor (was looking at the unit test code).

Also, thanks for the disclaimer. I’m aware that things may be changing, and I have two options:

  1. Deal with changes as they come through the pipeline, and possibly rebuild things.
  2. Implement basically the same system, but be forever behind in robustness and quality.

I think for my specific case I’m better off with option 1 as it lets design start iterating now, freeing me up to work on other systems.

I sincerely appreciate you taking your time to come in here and help out with a feature that is not ready for full consumption yet.

-Matt

Creasso -

I had the same issue you’re having and was able to solve it by importing a csv file into a GameplayTags table with the GameplayCue tag and nothing else. My csv file looks like this:
,Tag,CategoryText
0,GameplayCue,

In your config file you need to point the GameplayTags system to the corresponding table:
[GameplayTags]
GameplayTagTableList=’/Game/tagstable.tagstable’

That should get you up and running with no crashes and allow you to trigger GameplayAbilities with no issues. The next one you’ll hit will be the reason I switched to 4.7. GameplayEffects are not blueprintable in 4.6.

Hope that helps.

-Matt

I’m guessing you meant the fact that UGameplayEffects are blueprints in more recent versions of the engine. This change was made to allow for inheritance to work as expected in gameplay effects. The initial solution using template classes wasn’t robust enough. Even though UGameplayEffects are blueprints you should never use their event graphs. Gameplay effects don’t get instanced and should be treated as just data.

Thanks Matt!
It’s enough to at least make some mess.
Now I’m like a child on a candy shop. lol

Fred:
I’ve read and do accept the terms/risks of mess with this pre-experimental feature.

Thanks guys!

Question. Why Effects are not instanced ?
Does instancing, objects to execute their event graphs, have some serious performance implications ? I’ve been looking trough code of this module and I see you guys generally try to avoid instancing of objects.
So I’m actually curious how big impact it can have on performance, with several thousands of instanced objects (bar in mind not all of them are executed at the same time).

I’ve been coding something similar, and I just well instance objects, when needed, to save myself headaches, and make it easier to use in blueprints.

There are a few reasons why we don’t want to instance gameplay effects. Gameplay effects in this system are intended to be data only. The only reason they are blueprints is for inheritance. The event graphs were never intended to be used and we have no plans to support event graphs running inside gameplay effects.

Instancing gameplay effects would have performance implications. One of the biggest considerations here is networking and replication. Replicating that many objects would be a bad idea.

If you need to use an event graph in this system you should do it at the ability level, not the effect level. Abilities can be instanced and support multiple options for how they are instanced.

Just wanted to echo Fred’s disclaimer and provide a little more info about executions. (Also potentially handy is the original thread about the system in the github sub-forum, but not much has changed re: status since my last post in there, everything is still WIP: https://forums.unrealengine.com/showthread.php?4961-Engine-Source-SkillSystem-Explanation).

To add onto what Fred mentioned about the execution classes, we currently use those for things that are otherwise too complicated to encapsulate with a simple modifier, or that rely heavily on additional information. As an example, we implement all of Fortnite’s damage calculation formula inside one execution class as a way to enforce that all sources of damage in our game use the exact same code path. The formula can be pretty complex though and cannot be represented with just a normal mod. We use a custom gameplay effect context so that when the execution goes off, it can additionally query stuff like the item acting as a damage source.

When you set up an execution class, you can optionally specify attributes from the source/caster or target that are potentially relevant to the calculation at hand and the execution will attempt to capture them for you so can query them from the execution parameters (see RelevantAttributesToCapture, we initialize them in our native execution constructor). In our damage formula, that manifests as capturing outgoing damage buffs from the source, etc. vs. incoming damage resistance from the target, etc., among others.

This is making a lot more sense now. Thanks for the more in-depth example, those are always very helpful for understanding the how/why of things.

-Matt

I have some questions regarding target data and how to get that fed into the system.

  1. First thing I found was GameplayAbilityTargetActor classes which spawn an actor to assist with targeting. When I initially tried to use this, I found that I was never getting target data, and upon digging saw that the predefined versions all seem to expect that a PlayerController is present for the selection process. I made a new version of the radius target actor which skips the check for a player controller and runs purely on the server. Is that the approach I should be using, or are these targeting classes really expecting a player to be assisting with the targeting at all times?

  2. Suppose I know my target already (have AI running that handles target selection) - are there any built in mechanisms to get this data into the ability?

Thanks again,

-Matt

Targeting is actually a part of the system I’m expecting will likely change again. Since re-integrating the system back to Fortnite, we’ve already changed how Fortnite does targeting through abilities (and doesn’t use the targeting actors at all), so I’m expecting a hybrid result back in the main engine branch once we figure out a good mix. I’ll see if I can fetch someone more familiar with the existing targeting to provide an answer as well.

I know I’m essentially asking for voodoo, but is there any sort of ETA on a stable release, either in time or version number?

Unfortunately not at the moment, though we’re working to try to figure that out alongside the engine team (hoping to get an example made with it, if possible). We’ll be sure to provide an update/announcement/etc. as soon as we figure these things out. I can tell you it definitely won’t be stable/documented/etc. by 4.7, but that’s all I know for now. In general, it’s solidifying more and more (except for maybe targeting), but it’s still very rough around the edges and not super user-friendly.

You should use an AbilityTask like WaitTargetData in your ability blueprint graph to spawn and manage a TargetingActor for a player controlled ability. The TargetingActor will handle the visualization and updating of the targeting preview, and will produce the TargetData that can be used for applying GameplayEffects or spawning actors/projectiles, etc.

For an AI, especially if you already know who you want to target, you can just use a function like AbilityTargetDataFromActor. This takes in an actor and returns the TargetData that can be used in other functions.

So I’m past all the targeting issues and have a few abilities working as expected in the game now.

I’ve hit my next roadblock, however. How do you end an ability from outside of the ability itself? For example - The AI changes targets, which requires the ability to be restarted due to internal state of the ability needing a reset.

I’ve tried using UAbilitySystemComponent::EndAbility(FGameplayAbilitySpecHandle AbilityToEnd) with the handle that I use in TryActivateAbility, but it doesn’t seem to stop the ability. Any ideas where I’m going wrong?

  • Matt

There could be a few things going on here. Is the ability instanced? It sounds like it should be instanced per actor for your AI example. Have you tried stepping into UAbilitySystemComponent::EndAbility in your debugger? Knowing how far it’s getting inside this function would make a difference for what your next steps should be.

Ok, I did some digging and it comes back to the same problem I was having with retrieving the time remaining in cooldown/total cooldown.

It appears as though in the current code for 4.7, the only way to get a pointer to the instanced ability is if it is instanced per execution:


	// If we are the server or this is a client authoritative 
	if (Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::Client || (NetMode == ROLE_Authority))
	{
		// Create instance of this ability if necessary
		if (Ability->GetInstancingPolicy() == EGameplayAbilityInstancingPolicy::InstancedPerExecution)
		{
			InstancedAbility = CreateNewInstanceOfAbility(*Spec, Ability);
			ActivationInfo.bCanBeEndedByOtherInstance = Ability->bServerRespectsRemoteAbilityCancelation;
			InstancedAbility->CallActivateAbility(Handle, ActorInfo, ActivationInfo, OnGameplayAbilityEndedDelegate);
			if (OutInstancedAbility)
			{
				*OutInstancedAbility = InstancedAbility;
			}
		}
		else
		{
			Ability->CallActivateAbility(Handle, ActorInfo, ActivationInfo, OnGameplayAbilityEndedDelegate);
		}
	}

So it looks like end ability is trying to grab that pointer to my ability that is instanced per actor and set some flags on it:



	// Attempt to activate the ability (server side) and tell the client if it succeeded or failed.
	if (TryActivateAbility(Handle, PredictionKey, &InstancedAbility))
	{
		ClientActivateAbilitySucceed(Handle, PredictionKey.Current);
		//Client commands to end the ability that come in after this point are considered for this instance
		if (InstancedAbility)
		{
			//Only applies to instanced abilities
			InstancedAbility->CurrentActivationInfo.bCanBeEndedByOtherInstance = true;
		}
	}

But it fails to get a pointer to the instanced ability, which causes this to fail:


void UAbilitySystemComponent::EndAbility(FGameplayAbilitySpecHandle AbilityToEnd)
{
	FGameplayAbilitySpec* AbilitySpec = FindAbilitySpecFromHandle(AbilityToEnd);
	if (AbilitySpec && AbilitySpec->Ability && AbilitySpec->IsActive() && AbilitySpec->ActivationInfo.bCanBeEndedByOtherInstance)



It looks like this is fixed in your master, but the changes haven’t been pushed to 4.7 yet.

If you’re willing to use the latest code from GitHub I’d suggest looking at bRetriggerInstancedAbility inside of UGameplayAbility. This bool can be set on the ability data so that when the ability is triggered again on the same actor it will automatically call EndAbility for you on the previous instance. This only works for abilities that are instanced per actor but it sounds like it’s what you want for your example.