Summary
[GameplayTags] AnyCountChange delegate does not fire for intermediate count decreases when removing loose gameplay tags
What Type of Bug are you experiencing?
Gameplay
Steps to Reproduce
-
Obtain a UAbilitySystemComponent instance.
-
Register a gameplay tag event delegate using EGameplayTagEventType::AnyCountChange:
ASC->RegisterGameplayTagEvent(
Tag,
EGameplayTagEventType::AnyCountChange
); -
Call AddLooseGameplayTag(Tag) three times.
-
Call RemoveLooseGameplayTag(Tag) three times.
-
Observe the delegate callback invocations and the reported tag counts.
The issue is consistently reproducible with the original implementation.
Expected Result
The AnyCountChange delegate should be triggered every time the gameplay tag count changes, including both increases and decreases.
Expected callback sequence:
Adding:
- Count: 1
- Count: 2
- Count: 3
Removing:
- Count: 2
- Count: 1
- Count: 0
Every count change should trigger the registered delegate.
Observed Result
The delegate fires correctly when adding loose gameplay tags:
- Count: 1
- Count: 2
- Count: 3
However, when removing loose gameplay tags using RemoveLooseGameplayTag(), the delegate only fires when the count reaches zero.
Intermediate count decreases (3 → 2 and 2 → 1) do not trigger the AnyCountChange delegate.
This causes listeners relying on AnyCountChange to receive incomplete tag count change notifications.
Affects Versions
5.8
Platform(s)
Windows
Additional Notes
A fix for this issue has already been submitted as a pull request to the official Unreal Engine repository.
GitHub Pull Request:
https://github.com/EpicGames/UnrealEngine/pull/14880
Commit:
1c4a967
Proposed Fix:
Restructure the scope of the conditional block in UpdateTagMapSingle_Internal() by moving the closing brace before the DeferredTagChangeDelegates execution loop.
This ensures that deferred tag change delegates are executed regardless of whether the tag count change is significant, allowing AnyCountChange delegates to fire for intermediate count decreases.
This follows the existing implementation pattern in UpdateTagMap_Internal(), where delegate execution is already handled outside the corresponding conditional block.
Testing:
- Manually tested loose gameplay tag add/remove cycles.
- Verified that intermediate count decreases correctly trigger
AnyCountChangecallbacks with the proposed fix. - Verified that the container-based
AddLooseGameplayTags/RemoveLooseGameplayTagspath remains unaffected.
The proposed change is a minimal control-flow restructuring without introducing additional gameplay logic.
I am submitting this report to ensure the underlying engine issue is tracked and can be reviewed by the Unreal Engine team.