Licensee reported problem via UDN.
Context
GameplayCues ('cue') are events that can be fired by game code, that are identified via a GameplayTag. Cues can be instantaneous or can be a state on any actor with an AbilitySystemComponent. GameplayCueNotifies (GCN) are (blueprint) actor classes that respond to fired cues. The GameplayCueManager knows which GCN responds to which tag. It can preload GCN blueprint classes, or load them on-demand if the class isn't loaded yet. Loading them on-demand can happen synchronously or async, depending on how the GameplayCueManager subclass overrides certain virtual functions.
Problem
When game code adds and removes a stateful cue on an actor, but the GCN class isn't loaded yet and is async loaded, the cue events can be executed out of order on the GCN depending on timing. Specifically if the game code calls AddGameplayCue and then RemoveGameplayCue before the GCN class has finished loading, then the removal and add events executing out of order can lead to GCNs staying active indefinitely. In practice this can have unintended effects like particles or looping sounds staying active.
When AddGameplayCue followed by RemoveGameplayCue is called during the async loading of a GCN class, one of two scenarios can happen timing wise:
The second case happens because GameplayCueSet performs a just-in-time class lookup to decide whether to (a) async load and defer the event or (b) execute immediately. The problem happens because the class can be found before a prior cue's async load callback has been executed. The likelihood of this problem is increased by the fact that async load callbacks are deferred until the end of the frame: see
/** Helper class that defers streamable manager delegates until the next frame */ class FStreamableDelegateDelayHelper : public FTickableGameObject
There are many opportunities for game code to execute between a GCN class being findable, but before the related async load callback is executed, so scenario (2) must be addressed to execute events in order.
Suggested Fix
When async loaded GCNs finish loading and multiple events (such as add-remove) have been requested in the meantime, there are multiple ways to deal with this:
Which one is preferred by developers will be use-case specific based on what the GCN is intended to do. For example: if it's to activate/deactivate particle systems or looping sounds then having nothing happen may be better. If it's to play one-shot sounds, particles or spawn combat text then executing the events may be better. Because GCN can contain any game code, I'm opting to execute all events in order. This results in consistency: executing cues will result in the associated GCN being played regardless of preloaded/sync loaded/async loaded. If immediate playback is important, then Epic's recommendation will be to update your configuration to preload the GCN classes. See Lyra for examples.
Repro steps in Lyra:
Non-fatal, callstack for reference for when deferred gameplay cues are executed:
UnrealEditor-GameplayAbilities.dll!UGameplayCueManager::OnMissingCueAsyncLoadComplete(FSoftObjectPath LoadedNotifyClass) Line 380 C++ [External Code] [Inline Frame] UnrealEditor-GameplayAbilities.dll!TDelegate<void __cdecl(void),FDefaultDelegateUserPolicy>::ExecuteIfBound() Line 635 C++ UnrealEditor-GameplayAbilities.dll!UE::StreamableManager::Private::WrapDelegate::__l5::<lambda_1>::operator()(TSharedPtr<FStreamableHandle,1> __formal) Line 99 C++ [External Code] [Inline Frame] UnrealEditor-Engine.dll!TDelegate<void __cdecl(TSharedPtr<FStreamableHandle,1>),FDefaultDelegateUserPolicy>::ExecuteIfBound(TSharedPtr<FStreamableHandle,1>) Line 635 C++ > UnrealEditor-Engine.dll!FStreamableDelegateDelayHelper::Tick(float DeltaTime) Line 214 C++ UnrealEditor-Engine.dll!FTickableGameObject::TickObjects(UWorld * World, ELevelTick LevelTickType, bool bIsPaused, float DeltaSeconds) Line 196 C++ UnrealEditor-UnrealEd.dll!UEditorEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 2199 C++ UnrealEditor-UnrealEd.dll!UUnrealEdEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 533 C++ UnrealEditor-LyraEditor.dll!ULyraEditorEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 39 C++ UnrealEditor.exe!FEngineLoop::Tick() Line 5933 C++
There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-259543 in the post.
0 |
Component | UE - Gameplay - Gameplay Ability System |
---|---|
Affects Versions | 5.4, 5.3, 5.5 |
Target Fix | 5.6 |
Fix Commit | 40973307 |
---|
Created | Mar 22, 2025 |
---|---|
Resolved | Mar 22, 2025 |
Updated | Mar 24, 2025 |