URemoveOtherGameplayEffectComponent fails validation for periodic gameplay effects

Hi,

We have two gameplay effects that are mutually exclusive (one GE “charges” an attribute, and the other GE “depletes” it) and the goal is anytime one GE is applied, the other GE should be removed. I tried using `UTargetTagRequirementsGameplayEffectComponent::RemovalTagRequirements` for this behavior (e.g., the “charge” GE uses the “depleting” tag in `RemovalTagRequirements` and the “deplete” GE uses “charging” tag in `RemovalTagRequirements`), but it prevents the GEs from being applied if the other is active (instead of removing the other GE).

I would actually prefer to use `URemoveOtherGameplayEffectComponent` for this functionality, since it’s more self-documenting because GEs refer to each other directly, but when I tried to use this component, it throws a BP compile error due to the GE being periodic (since both the “charging” and “depleting” GE take place over a duration and tick the attribute up and down).

The comment on the error simply says “that you probably instead want the OngoingTagRequirements in TagRequirementsGameplayEffectComponent”:

#if WITH_EDITOR
    /**
     * Warn about periodic Gameplay Effects, that you probably instead want the OngoingTagRequirements in TagRequirementsGameplayEffectComponent
     */
    UE_API virtual EDataValidationResult IsDataValid(class FDataValidationContext& Context) const override;
#endif // WITH_EDITOR

#if WITH_EDITOR
EDataValidationResult URemoveOtherGameplayEffectComponent::IsDataValid(FDataValidationContext& Context) const
{
    EDataValidationResult Result = Super::IsDataValid(Context);

    if (GetOwner()->DurationPolicy != EGameplayEffectDurationType::Instant)
    {
       if (GetOwner()->Period.Value > 0.0f)
       {
          Context.AddError(FText::FormatOrdered(LOCTEXT("PeriodicEffectError", "GE is Periodic. Remove {0} and use TagRequirements (Ongoing) instead."), FText::FromString(GetClass()->GetName())));
          Result = EDataValidationResult::Invalid;
       }
    }

    return Result;
}
#endif // WITH_EDITOR

but is this more of a suggestion, or a hard limitation? It seems to work fine (our game isn’t networked), but before I edit the engine source to remove this restriction, I wanted to check to see if I’m opening a can of worms.

Here’s a screenshot for reference of the set up:

[Image Removed]

[Attachment Removed]

Steps to Reproduce
Hi,

We have two gameplay effects that are mutually exclusive (one GE “charges” an attribute, and the other GE “depletes” it) and the goal is anytime one GE is applied, the other GE should be removed. I tried using `UTargetTagRequirementsGameplayEffectComponent::RemovalTagRequirements` for this behavior (e.g., the “charge” GE uses the “depleting” tag in `RemovalTagRequirements` and the “deplete” GE uses “charging” tag in `RemovalTagRequirements`), but it prevents the GEs from being applied if the other is active (instead of removing the other GE).

I would actually prefer to use `URemoveOtherGameplayEffectComponent` for this functionality, since it’s more self-documenting because GEs refer to each other directly, but when I tried to use this component, it throws a BP compile error due to the GE being periodic (since both the “charging” and “depleting” GE take place over a duration and tick the attribute up and down).

The comment on the error simply says “that you probably instead want the OngoingTagRequirements in TagRequirementsGameplayEffectComponent”:

#if WITH_EDITOR
    /**
     * Warn about periodic Gameplay Effects, that you probably instead want the OngoingTagRequirements in TagRequirementsGameplayEffectComponent
     */
    UE_API virtual EDataValidationResult IsDataValid(class FDataValidationContext& Context) const override;
#endif // WITH_EDITOR

#if WITH_EDITOR
EDataValidationResult URemoveOtherGameplayEffectComponent::IsDataValid(FDataValidationContext& Context) const
{
    EDataValidationResult Result = Super::IsDataValid(Context);

    if (GetOwner()->DurationPolicy != EGameplayEffectDurationType::Instant)
    {
       if (GetOwner()->Period.Value > 0.0f)
       {
          Context.AddError(FText::FormatOrdered(LOCTEXT("PeriodicEffectError", "GE is Periodic. Remove {0} and use TagRequirements (Ongoing) instead."), FText::FromString(GetClass()->GetName())));
          Result = EDataValidationResult::Invalid;
       }
    }

    return Result;
}
#endif // WITH_EDITOR

but is this more of a suggestion, or a hard limitation? It seems to work fine (our game isn’t networked), but before I edit the engine source to remove this restriction, I wanted to check to see if I’m opening a can of worms.

Here’s a screenshot for reference of the set up:

[Image Removed]

[Attachment Removed]

Hello! The reason why we disallow URemoveOtherGameplayEffectComponent with Periodic effects is because it’s only evaluated when the GameplayEffect gets applied, which might not be what people expect from a periodic effect.

If you’re okay with that removal logic being only evaluated once, feel free to comment out that error.

I can confirm that the hint that’s printed, about the same being achievable with Ongoing Tag Requirements, doesn’t apply in this case. Because the second effect is being prevented from application, indeed.

[Attachment Removed]

No problem! And my apologies for us to taking a while to respond this time.

I’ll close this case.

[Attachment Removed]

In this scenario, maybe an extra call to RemoveActiveEffectsWithTags may be a simpler solution. But I understand if you prefer to keep everything data-driven for designer workflows.

[Image Removed]

[Attachment Removed]

Yes! I only care about the check to remove the other effect at application. I don’t need it to be re-evaluated when the period ticks.

Thanks for confirming - I’ll comment out the error in the engine source.

[Attachment Removed]