[GAS] What is the best way to spawn/despawn a "magic shield" Actor with collision when a GameplayEffect is applied/removed

Hello all,

I am creating a Gameplay Effect for a “magic shield” has blocks things (projectiles, enemies, etc.).
This Gameplay Effect is meant to be applied by various Gameplay Abilities (e.g. “apply magic shield to self”, “apply magic shield to target”, “apply magic shield to all in party”…)

The actual effect is a “magic shield” Actor with a collision, that I want to spawn and attach to the target of the Gameplay Effect when it is applied, and despawn when it is removed.

I am not sure how to best achieve this.
There is UGameplayEffect::OnApplied() where I could put my spawning logic, but I cannot find an equivalent when a Gameplay Effect is removed.
I thought of using a Gameplay Cue but since this is gameplay-critical is seems like a poor choice.

Any help would be appreciated.

I really want to compartmentalize the logic so that a GameplayEffect is self-contained and is able to run whatever logic it needs when applied and removed (in this particular case, spawning the magic shield Actor).

Here’s what I did:

UCLASS(Blueprintable)
class MY_API UScriptableGameplayEffect : public UGameplayEffect
{
	GENERATED_BODY()

public:
    /** Registers to receive GameplayEffect events */
    static void RegisterForGameplayEffectEvents(UAbilitySystemComponent* ASC);

    /** Called when this ScriptableGameplayEffect is applied to a target */
    UFUNCTION(BlueprintImplementableEvent)
    void OnApplied(UAbilitySystemComponent* ASC, const FGameplayEffectSpec& Spec, FActiveGameplayEffectHandle Handle, AActor* Target) const;

    /** Called when this ScriptableGameplayEffect is removed from a target */
    UFUNCTION(BlueprintImplementableEvent)
    void OnRemoved(UAbilitySystemComponent* ASC, const FGameplayEffectSpec& Spec, FActiveGameplayEffectHandle Handle, AActor* Target) const;
};
void UScriptableGameplayEffect::RegisterForGameplayEffectEvents(UAbilitySystemComponent* ASC)
{
	if (!ASC)
		return;

    // Register for GameplayEffect applied events
    auto OnGameplayEffectAppliedDelegateFunction = [](UAbilitySystemComponent* ASC, const FGameplayEffectSpec& Spec, FActiveGameplayEffectHandle Handle)
        {
            const UScriptableGameplayEffect* ScriptableGameplayEffect = Cast<UScriptableGameplayEffect>(Spec.Def.Get());
            if (ScriptableGameplayEffect)
            {
                ScriptableGameplayEffect->OnApplied(ASC, Spec, Handle, ASC->GetOwnerActor());
            }

            // Register for GameplayEffect removed events
            ASC->OnGameplayEffectRemoved_InfoDelegate(Handle)->AddLambda(
                [](const FGameplayEffectRemovalInfo& Info)
                {
                    const UScriptableGameplayEffect* ScriptableGameplayEffect = Cast<UScriptableGameplayEffect>(Info.ActiveEffect->Spec.Def.Get());
                    if (ScriptableGameplayEffect)
                    {
                        ScriptableGameplayEffect->OnRemoved(Info.ActiveEffect->Handle.GetOwningAbilitySystemComponent(), Info.ActiveEffect->Spec, Info.ActiveEffect->Handle, Info.ActiveEffect->Handle.GetOwningAbilitySystemComponent()->GetOwnerActor());
                    }
                }
            );
        };
    ASC->OnGameplayEffectAppliedDelegateToSelf.AddLambda(OnGameplayEffectAppliedDelegateFunction);
    ASC->OnGameplayEffectAppliedDelegateToTarget.AddLambda(OnGameplayEffectAppliedDelegateFunction);    
}
void UMyAbilitySystemComponent::BeginPlay()
{
	Super::BeginPlay();

	UScriptableGameplayEffect::RegisterForGameplayEffectEvents(this);
}

It looks contrived, though. Feedback on this solution and how to improve it is very welcome.

Further research seem to suggest that a way to achieve my goal might be for the GameplayEffect to grant a GameplayAbility to the target, that auto-activates and handles whatever logic is required (e.g. spawning the magic shield Actor in my current use case).

I don’t understand the underlying systems of the Gameplay Ability System well enough to tell which is the best solution. Any feedback from someone who has some experience with the system would be welcome.

If anyone stumbles upon this thread and wants to know, further reading seemed to indicate that Gameplay Effects in GAS are intended to be data only. Gameplay logic should only exist in Gameplay Abilities, from what I gather.

Thus, the solution to have some logic being run when a Gameplay Effect is applied is indeed to set up a Grant Gameplay Abilities component:

That Gameplay Ability can then auto-activate upon being granted using this code:

void UMyGameplayAbility::OnAvatarSet(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec)
{
	Super::OnAvatarSet(ActorInfo, Spec);

	if (bActivateAbilityOnGranted)
	{
		ActorInfo->AbilitySystemComponent->TryActivateAbility(Spec.Handle, false);
	}
}

For my specific use case, an auto-activated Gameplay Ability granted by the Gameplay Effect handles the spawning of the “magic shield” Actor, then uses the Wait For Gameplay Effect Removed ability task to know when the Gameplay Effect is removed and handle the destruction of the “magic shield” Actor.

I hope posting this final resolution helps someone in the future!

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.