Casting abilities in Unreal Engine 5 using the Gameplay Ability System

3 min read

While I was working on a game, I had some problems while making the casting system, similar to those found in MOBAs like Dota 2, or MMOs like World of Warcraft.

I created this guide to help others avoid the challenges I faced and achieve great results more efficiently.

The task at hand

This is what the system will achieve:

  • Delay ability activation until the casting progress is completed.
  • Has to work in multiplayer games.
  • Show in the local UI the expected casting time remaining in realtime.
  • Be able to reuse the same Gameplay Effect for all the abilities, as to avoid code & asset duplication.

You might find some things missing from the list, like how to handle interruptions. I haven't made the interruption part of the system for my game yet, so I can not provide help with that part. But I think I will handling it with Gameplay Events and that should be compatible with this guide.

Guide

This guide was made for version 5.4 of Unreal Engine. Although it should work for older versions with some tweaks.

The system depends mainly of two things:

  • A casting GameplayEffect.
  • The GameplayAbility that will handle the casting GameplayEffect.

The Gameplay Effect

The casting GameplayEffect is quite straight forward:

  • Set the Duration Policy to Has Duration, and the duration to 0.
  • Add a Grant Tags to Target Actor component that adds a GameplayTag to indicate the casting state (in my case I use Combat.State.Casting).
  • Optionally, modifiers can be added to the character, like slowing it down while the casting is in progress.

This is how mine looks: Casting Gameplay Effect

The Gameplay Ability

An instance of the casting GameplayEffect should created with its duration modified before being applied to the owner. This allows the system to reuse the same casting GameplayEffect for all abilities.

Take into account the Duration modification happens on the GameplayEffectSpec and not on GameplayEffect. Altought this does not matter for this use case, it might matter for more complex setups.

After the casting Gameplay Effect is applied the ability will wait for the casting Gameplay Tag to be removed (Combat.State.Casting in my case). When its removed the casting has finished.

This works in multiplayer games as GameplayTags are replicated and removed on both the server and the client.

Set duration example

I also experimented using OnGameplayEffectRemoved but I had some problems on the clients, as the Gameplay Effect was instantly removed (maybe because what is applied locally is a predicted version that is removed as soon as the server-replicated one is applied...?).

Recommendation
Programming in a Blueprint Macro will allow all our abilities to reuse the same code.

Extra: Modifying casting duration from attributes

For my game I need to be able to modify the casting time based on an Attribute. For this I have created the following C++ code that is available for all my Gameplay Abilities.

It recalculates from a BaseCastingTime (the default time the ability should take to cast) the real time based on an attribute called CastingSpeed that allows Gameplay Effects like buffs and debuffs to change the final casting time.

FGameplayEffectSpecHandle UBaseGameplayAbility::GenerateCastingGameplayEffectSpec(
	const FGameplayEffectSpecHandle& GameplayEffectSpecHandle,
	const float BaseCastingTime
) {
	const FGameplayEffectSpec Spec = *GameplayEffectSpecHandle.Data.Get();
	const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
	const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();

	FAggregatorEvaluateParameters EvaluationParameters;
	EvaluationParameters.SourceTags = SourceTags;
	EvaluationParameters.TargetTags = TargetTags;

    // Calculate the new CastingTime from the modifiers (buffs/debuffs)
    // of the CastingSpeed attribute
	bool bSuccess;
	const float TotalCastingTime = UAbilitySystemBlueprintLibrary::EvaluateAttributeValueWithTagsAndBase(
		Spec.GetContext().GetInstigatorAbilitySystemComponent(),
		UFocusAttributeSet::GetCastingSpeedAttribute(),
		*Spec.CapturedSourceTags.GetAggregatedTags(),
		*Spec.CapturedTargetTags.GetAggregatedTags(),
		BaseCastingTime, // Use the BaseCastingTime to calculate the value from
		bSuccess
	);

    // Set the duration of the Gameplay Effect Spec
	GameplayEffectSpecHandle.Data->SetDuration(TotalCastingTime, true);

	return GameplayEffectSpecHandle;
}

This code can replace the SetDuration method in blueprints.

Set duration with cpp code

Conclusion

Unreal's Gameplay Ability System provides a phenomenal framework for developers to extend with their game specific mechanics.

The casting system explored in this guide is independent, expandable, composable and can be used in all the Gameplay Abilities the developer wants.

Always keep in mind that there might be better ways to create a casting system for your specific game mechanics, keep exploring your game needs.

I hope you have learned something new!
Made by Lorwen with 🧡