Unreal Engine 5 - Array of Gameplay Effects
Currently, I am working my way through an Unreal Engine 5 Course for the Gameplay Ability System on Udemy by Stephen Ulibarri.
Link to the course:
In the course, there are certain sections where Stephen gives you a quest to try to solve a problem on your own. These quests are usually followed up with a segment where he shows you his solution. There are also other sections called "Side Quests”. These sections are optional, and you aren’t given a solution at the end. This is a good way to practice what you have been learning and allow you to come up with a solution all by yourself. This post is a solution for the first side quest.
This side quest challenges you to create an array of different Effects
that can be assigned to a Blueprint. The example I will use later is for a Fire Area blueprint that has a particle effect for fire and a bounding box. Once the player collides with the bounding box, a Gameplay Effect triggers. This allows us to apply damage to the player. This same mechanism can also be used to heal the player when they pick up potions, restore mana, etc. Since we can apply multiple different types of effects, having an array of Effects
rules for the Effects
allows us to apply all sorts of different effects to any given object. For example, maybe we have a spell that deals impact damage but also has a chance to apply a burn effect. We can add both of these effects with this approach.
Now that I have explained what this is for, I just wanted to share my solution for splitting the effects into an array for the first side quest.
Firstly, I created this struct to hold each Effect
’s Policies
and GameplayEffect
’s:
USTRUCT(BlueprintType) struct FEffectType { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Applied Effects") TSubclassOf<UGameplayEffect> GameplayEffect; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Applied Effects") EEffectApplicationPolicy ApplicationPolicy = EEffectApplicationPolicy::DoNotApply; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Applied Effects") EEffectRemovalPolicy RemovalPolicy = EEffectRemovalPolicy::DoNotRemove };
I also added this line to my AuraEffectActor
:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Applied Effects") TArray<FEffectType> Effects;
Then with my OnOverlap
, I refactored it so that I am looping through the Effects
that have been set on the AuraEffectActor
in the Editor.
This allows me to simplify it so that I don’t need to have the 3 different types of Effects
(e.g Instant
, Duration
, Infinite
) as separate variables with similar logic
void AAuraEffectActor::OnOverlap(AActor* TargetActor) { for (const FEffectType& Effect : Effects) { if (Effect.ApplicationPolicy == EEffectApplicationPolicy::ApplyOnOverlap) { ApplyEffectToTarget(TargetActor, Effect); } } }
For OnEndOverlap
, like OnOverlap
, I refactored it to also loop over the Effects
on the AuraEffectActor
and also check the application policy if the ApplyOnEndOverlap
ApplicationPolicy
is on the current Effect
being iterated over.
Next, I check for the DurationPolicy
on the GameplayEffect
using GetDefaultObject
and compare it against the EGameplayEffectDurationPolicy
Enum to check if the Policy on the gameplay effect is set to Infinite
. From there It’s mostly the same logic.
void AAuraEffectActor::OnEndOverlap(AActor* TargetActor) { for (const FEffectType& Effect : Effects) { if (Effect.ApplicationPolicy == EEffectApplicationPolicy::ApplyOnEndOverlap) { ApplyEffectToTarget(TargetActor, Effect); } if (Effect.GameplayEffect.GetDefaultObject()->DurationPolicy == EGameplayEffectDurationType::Infinite) { if (Effect.RemovalPolicy == EEffectRemovalPolicy::RemoveOnEndOverlap) { UAbilitySystemComponent* TargetAbilitySystemComponent = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent( TargetActor ); if (!IsValid(TargetAbilitySystemComponent)) return; TArray<FActiveGameplayEffectHandle> HandlesToRemove; for (TTuple<FActiveGameplayEffectHandle, UAbilitySystemComponent*> HandlePair : ActiveEffectHandles) { if (TargetAbilitySystemComponent == HandlePair.Value) { TargetAbilitySystemComponent->RemoveActiveGameplayEffect(HandlePair.Key, 1); HandlesToRemove.Add(HandlePair.Key); } } for (FActiveGameplayEffectHandle& Handle : HandlesToRemove) { ActiveEffectHandles.FindAndRemoveChecked(Handle); } } } } }
Lastly, I do another comparison with the EGameplayEffectDurationType
enum to check if the DurationPolicy
is set to Infinite
at the bottom of my ApplyEffectToTarget
function.
the function now looks like this:
void AAuraEffectActor::ApplyEffectToTarget( AActor* TargetActor, const FEffectType& Effect ) { UAbilitySystemComponent* TargetAbilitySystemComponent = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent( TargetActor ); if (TargetAbilitySystemComponent == nullptr) return; check(Effect.GameplayEffect); FGameplayEffectContextHandle EffectContextHandle = TargetAbilitySystemComponent->MakeEffectContext(); EffectContextHandle.AddSourceObject(this); const FGameplayEffectSpecHandle EffectSpecHandle = TargetAbilitySystemComponent->MakeOutgoingSpec( Effect.GameplayEffect, 1.0f, EffectContextHandle ); const FActiveGameplayEffectHandle ActiveEffectHandle = TargetAbilitySystemComponent->ApplyGameplayEffectSpecToSelf( *EffectSpecHandle.Data.Get() ); if (EffectSpecHandle.Data.Get()->Def.Get()->DurationPolicy == EGameplayEffectDurationType::Infinite) { if (Effect.RemovalPolicy == EEffectRemovalPolicy::RemoveOnEndOverlap) { ActiveEffectHandles.Add(ActiveEffectHandle, TargetAbilitySystemComponent); } } }
Now when I go to the Blueprint for a given Effect
i.e BP_FireArea
. I can Add multiple Effects
. In the example below I have added a Heal effect when I leave the fire area so I get health back when exiting the area.
That’s pretty much it!
I’ll be sure to post more solutions as I go through the course.