Buff Debuff UI Icons
By CrunchyNut & AngelMapper, BGP Project 2020, UE 4.24.3
Summary
GIF of effect:
Buff Debuff UI Icons
This was quite a journey to make. Initially tried to use the GameplayEffectUIData class in totality, but we couldn't manage things like stacks and timers easily with it. So we're using a combination of the GameplayEffectUIData and numbered tags to trigger local timers.
We also ran into a problem where a Gameplay Effect C++ delegate that is supposed to fire on any tag change wasn't firing on the client for any tag change above 1. Not sure if this is a UE4/GAS bug or intended behaviour. Nothing in C++ indicated this was intended behaviour. The delegate was still firing however for the server.
We did a manual workaround for that by using BP implemented dictionary variable that maps tags to stacks and we manually check for all stacks and tags remaining on the server (where this was still firing) and calling a multicast
The flow
C++ Code picks up Tag changes (assigned to every GE that we want using this system) and fires off BP events that dynamically query the current tag statuses and update the UI with locally predicted timers
- A Gameplay Effect (GE) is applied to a Character (that has GAS enabled)
- The GE applies a tag that starts with 'ActiveEffect' (e.g. ActiveEffect.Debuff.Fire)
- The C++ code of the Character picks up the tag (by searching for the 'ActiveEffect' bit) and triggers a BP implemented bound delegate
- The event fires off the BP implemented event which does the following:
- Updates a blueprint-variable that contains the tags and stack counts (using blueprint implementable C++ functions)
- Initiates the local UI buff./debuff timers
- Forces a refresh of the latest buff/debuff data
- Finally, the UI Widget BP receives all the info it needs to locally start (or update) the UI Buff/Debuff Timer. Icons and tooltip descriptions are taken from the GameplayEffectUIData class
The code
C++
Character Header File:
/**
* Called when a GameplayEffect is applied to this character
* Happens on client and server */
UFUNCTION(BlueprintImplementableEvent, Category = "GAS", meta = (DisplayName = "On Active Gameplay Effect Added"))
void OnActiveGameplayEffectApplied(UAbilitySystemComponent * Target, const FGameplayEffectSpec & SpecApplied, FActiveGameplayEffectHandle ActiveHandle);
/** Tags to look for to call OnBuffDebuffTagChange event */
UPROPERTY(EditDefaultsOnly, Category = "GAS")
FGameplayTag BuffDebuffTag;
void InternalBuffDebuffTagChange(const FGameplayTag Tag, int32 NewCount);
UFUNCTION(BlueprintImplementableEvent, Category = "GAS", meta = (DisplayName = "On Buff/Debuff Tag Change"))
void OnBuffDebuffTagChange(const FGameplayTag Tag, int32 NewCount);
Character CPP file:
Binds delegate on Construct
// Construction. Sets default values
ABGPCharacterGAS::ABGPCharacterGAS()
{
// Sets the type of replication suitable for a more multiplayer game
AbilitySystem->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
// Bind delegate to blueprint event that activates when a gameplay effect is applied (mainly so we can add buffs/debuffs to the UI).
AbilitySystem->OnActiveGameplayEffectAddedDelegateToSelf.AddUObject(this, &ABGPCharacterGAS::OnActiveGameplayEffectApplied);
}
Begin play, register gameplay tag event bind
// Called when the game starts or when spawned
void ABGPCharacterGAS::BeginPlay()
{
Super::BeginPlay();
AbilitySystem->RegisterGameplayTagEvent(BuffDebuffTag, EGameplayTagEventType::AnyCountChange).AddUObject(this, &ABGPCharacterGAS::OnBuffDebuffTagChange);
}
Blueprints
{TBA - need to check with Dart how to add images for screenshots}