Gameplay Cue Visuals¶
Summary: Persistent cosmetic visuals — buff auras and elemental weapon effects — are driven entirely by GameplayCues that ride a GameplayEffect's lifecycle (no custom RPCs). Data assets describe the visual layers, thin Blueprint children bind cue tags to those configs, and a small set of
AGameplayCueNotify_Actorhandlers spawn/tear down the layers on every client. This document explains that framework and how it differs from one-shot impact FX.
Table of Contents¶
- Architecture Overview
- Core Concepts (Why)
- Aura Visuals
- Elemental Weapon Visuals
- Replication & Lifecycle
- Public Contracts
- Source References
- Related Systems
- Recent Changes
Architecture Overview¶
Two families of persistent cosmetic VFX share one pattern: a GameplayEffect (a buff for auras, an
equip state for weapons) declares a GameplayCue.* tag; the cue notify spawns visual layers
WhileActive and removes them OnRemove. Because cue replication is built into GAS, every client
sees the effect with no bespoke networking.
AURAS WEAPON EFFECTS
+-----------------------------+ +-----------------------------------+
| Buff GameplayEffect | | Server: weapon equipped |
| (e.g. Haste) has cue tag | | EquipmentComponent derives identity|
| GameplayCue.Aura.* | | from granted UOnHitAbility |
+--------------+--------------+ | StatusTag |
| +----------------+------------------+
| GE active/removed | StatusTag -> registry
v v
+-----------------------------+ +-----------------------------------+
| AGameplayCueNotify_Aura | | Fires GameplayCue.Weapon.* on ASC |
| (one BP child per aura tag) | | cue params carry the equipment slot|
+--------------+--------------+ +----------------+------------------+
| | (replicated to all clients)
| reads UAuraVisualConfig v
v +-----------------------------------+
+-----------------------------+ | AGameplayCueNotify_Weapon |
| Spawn enabled layers: | | (single notify at PARENT tag) |
| 1. ground deferred decal | | resolves child tag + slot -> |
| 2. body overlay material | | UWeaponEffectsConfiguration entry |
| 3. flourish Niagara | | -> spawn at FX_Weapon socket |
+-----------------------------+ +-----------------------------------+
Core Concepts (Why)¶
-
Cue-driven, not RPC-driven. Visual state is a function of GAS state. Spawning on
WhileActive/OnActiveand cleaning up onOnRemovemeans replication, late-join, and teardown all come for free from the GameplayEffect/cue lifecycle. No replicated visual booleans, no multicast RPCs. -
Data-driven, no per-effect C++. Adding an aura or a weapon element is content work: a data asset (the visual layers) plus a Material Instance / Niagara plus one tag mapping. The C++ notify classes are generic.
-
Aura visuals are deliberately power-agnostic. Color, pulse, and radius are baked into the chosen Material Instance, not attribute-driven. A stronger Haste does not glow brighter. This keeps auras readable and avoids coupling cosmetics to balance numbers — see
UAuraVisualConfig, which intentionally exposes no color/intensity fields. -
Distinct from one-shot impact FX. This layer is for persistent cosmetics. One-shot hit reactions (
FCombatEffectConfigvia the combat-impact cue) and animation-driven swing trails are separate systems. The damage-number cue (AGameplayCueNotify_DamageNumber, see Damage Execution) is the closest sibling pattern: sameGameplayCueNotify_Actorbase, but transient and execute-once rather than active/removed.
Aura Visuals¶
An aura is described by a UAuraVisualConfig data asset exposing three independent, optional layers.
Each aura enables any subset; a new aura is one config asset + one Material Instance + one thin
AGameplayCueNotify_Aura Blueprint child that sets GameplayCueTag (a GameplayCue.Aura.* tag) and
points at the config.
| Layer | What it is | Notes |
|---|---|---|
| Ground decal | Deferred decal projected downward (the M_Aura_Decal MI family) |
Box conforms to terrain; offset below the feet so it projects onto the ground, not the head. Receiver-normal angle-fade prevents the decal climbing walls. The notify disables decal receipt on the owner's own meshes so the aura stays on the floor. |
| Body overlay | Overlay material applied to every visible mesh on the owner | "This unit is buffed" rim-glow. GatherVisibleBodyMeshes skips bHiddenInGame / !IsVisible() components — notably the hidden ALS driver mesh — and skips meshes socketed under another mesh (weapons, props), since the aura is a body effect. Each affected mesh's prior overlay is saved and restored (see below). |
| Flourish | Persistent Niagara attached to the owner | Motes / runes; optional attach socket. |
AGameplayCueNotify_Aura spawns only the enabled layers in OnActive and tears them all down in
OnRemove. Spawned handles are tracked transiently because cue notify instances may be pooled and
reused.
The Overlay Slot Is Shared and Single-Valued¶
SetOverlayMaterial is one slot per mesh — writing it discards whatever was there. That slot is not the
aura's to own: enemy meshes already carry a rim-light overlay for dark-zone readability, and a naive aura
would delete it permanently the first time it ran.
So the notify treats the slot as borrowed. On spawn, each mesh's existing overlay is recorded in an
FAuraOverlayTarget (mesh + previous material) before SetOverlayMaterial; teardown walks that list and
writes each previous material back. An aura therefore suspends a pre-existing overlay for its duration and
never clobbers it.
Two consequences worth knowing: - Overlapping auras don't compose. The last one applied wins visually, and unwinding is LIFO-correct only if they tear down in reverse order. Design auras so two rarely stack on the same unit. - Anything else writing the overlay slot mid-aura is lost on teardown, since the notify restores the value it captured at spawn.
See Character Rim Overlay for the other occupant of this slot.
Elemental Weapon Visuals¶
A weapon's cosmetic identity is derived from its gameplay identity rather than authored directly: the element a weapon visually expresses is the on-hit status it grants (a bleed weapon drips blood).
Server derivation. When a weapon is equipped, UEquipmentComponent inspects the weapon's granted
UOnHitAbilitys for their StatusTag, looks each up in the UWeaponEffectsConfiguration
registry to find the matching GameplayCue.Weapon.* tag and priority, and fires the top-N cues (N =
MaxSimultaneousEffects, by priority) on the wielder's ASC. The fired cue's parameters carry the
equipment slot tag so the right weapon mesh can be found. On unequip, the previously fired cues for
that slot are removed.
Client handling. A single AGameplayCueNotify_Weapon is registered at the parent
GameplayCue.Weapon tag (one thin BP child), so it handles every GameplayCue.Weapon.* child. On
the cue it resolves the specific child tag and the slot tag from the cue params, looks up the
registry entry, finds the wielder's weapon mesh for that slot via the PlayerState's equipment
component, and spawns the configured Niagara/overlay at the entry's socket (default FX_Weapon).
Visible to all players.
Registry. UWeaponEffectsConfiguration maps GameplayCue.Weapon.* → FWeaponEffectConfig
(Niagara, optional overlay material, socket, the StatusTag it represents, and a priority). The
stateless cue notify has no owning component, so it reaches the registry through the
UWeaponEffectsSettings developer-settings singleton (Project Settings → Game → Weapon Effects) —
the same singleton pattern the combat-impact cue uses.
Replication & Lifecycle¶
GE applied (server) ──► cue replicated ──► OnActive on each client ──► spawn layers
GE removed (server) ──► cue replicated ──► OnRemove on each client ──► teardown layers
| Concern | How it's handled |
|---|---|
| Networking | Inherent in GAS cue replication — no custom RPCs. |
| Cue-vs-equipment race | On clients the GameplayCue.Weapon.* cue can arrive before the equipment meshes finish spawning (separate replication path). AGameplayCueNotify_Weapon retries the mesh lookup for a bounded window. |
| Pooled notifies | Cue notify instances may be pooled/reused, so spawned handles are tracked transiently and the weapon notify also tears down in EndPlay as a safety net (a recycled instance may never receive OnRemove). |
Public Contracts¶
UAuraVisualConfig (data asset)¶
| Field group | Purpose |
|---|---|
Decal (bUseDecal, DecalMaterial, DecalSize, DecalOffset) |
Ground deferred-decal layer; size/offset are fixed per-aura geometry, not attribute-driven. |
Overlay (bUseOverlay, OverlayMaterial) |
Body overlay layer. |
Flourish (bUseFlourish, FlourishNiagara, FlourishSocketName) |
Persistent Niagara layer. |
No color/pulse/radius fields by design — those are baked into the Material Instance.
UWeaponEffectsConfiguration (registry data asset)¶
| Method | Parameters | Purpose |
|---|---|---|
GetEffectForCueTag |
CueTag |
Client hot path: config for a GameplayCue.Weapon.* tag (nullptr if unmapped). |
FindCueForStatus |
StatusTag, out CueTag, out Priority |
Server derivation: cue tag + priority whose entry represents an OnHit status. |
UEquipmentComponent (weapon-identity driving)¶
| Method | Parameters | Purpose |
|---|---|---|
TriggerWeaponIdentityCues |
Fragment, SlotTag, ASC |
Server: derive identities, map to cues, fire top-N on the wielder's ASC. |
RemoveWeaponIdentityCues |
SlotTag, ASC |
Server: remove the cues previously fired for a slot. |
Cue notify handlers¶
| Class | Registered at | Role |
|---|---|---|
AGameplayCueNotify_Aura |
One BP child per GameplayCue.Aura.* |
Spawns enabled UAuraVisualConfig layers OnActive, tears down OnRemove. |
AGameplayCueNotify_Weapon |
Parent GameplayCue.Weapon (one BP child) |
Resolves child tag + slot, looks up registry, spawns at weapon socket; bounded retry for the mesh race. |
Source References¶
| Component | Location |
|---|---|
UAuraVisualConfig |
Source/ProjectEternal/Public/AbilitySystem/Auras/AuraVisualConfig.h |
AGameplayCueNotify_Aura |
Source/ProjectEternal/Public/AbilitySystem/GameplayCues/GameplayCueNotify_Aura.h |
AGameplayCueNotify_Weapon |
Source/ProjectEternal/Public/AbilitySystem/GameplayCues/GameplayCueNotify_Weapon.h |
UWeaponEffectsConfiguration / FWeaponEffectConfig |
Source/ProjectEternal/Public/Combat/Data/WeaponEffectsConfiguration.h |
UWeaponEffectsSettings |
Source/ProjectEternal/Public/Combat/Data/WeaponEffectsSettings.h |
UEquipmentComponent::TriggerWeaponIdentityCues |
Source/ProjectEternal/Public/EquipmentManagement/Components/EquipmentComponent.h |
UOnHitAbility (StatusTag source) |
Source/ProjectEternal/Public/Abilities/OnHitAbility.h |
Related Systems¶
- Damage Execution —
AGameplayCueNotify_DamageNumber, the sibling transient cue pattern; on-hit abilityStatusTag. - Ability Classes —
UOnHitAbilitythat defines a weapon's gameplay identity. - GAS Overview — GameplayEffect/cue lifecycle these visuals ride on.
- Character Rim Overlay — the enemy-readability material sharing the same single-valued mesh overlay slot.
Recent Changes¶
| Date | Change | Impact |
|---|---|---|
| 2026-08-06 | Carnage aura shipped content-only | DA_Aura_Carnage + MI_Overlay_Aura_Carnage + a GC_Aura_Carnage notify child — no C++ changes, confirming the "adding an aura is content work" claim above |
| 2026-08-06 | Overlay applies to all visible meshes; prior overlay saved/restored | GatherVisibleBodyMeshes covers every visible body mesh instead of one, skipping the hidden ALS driver mesh and socketed props; FAuraOverlayTarget records each mesh's previous overlay and teardown restores it, so an aura no longer permanently destroys the enemy rim-light overlay |
| 2026-05-25 | Aura & elemental weapon VFX framework | New GameplayCue-driven persistent cosmetics: UAuraVisualConfig + AGameplayCueNotify_Aura (data-driven 3-layer auras), AGameplayCueNotify_Weapon + UWeaponEffectsConfiguration/UWeaponEffectsSettings (weapon element derived from OnHit StatusTag). No custom RPCs; equipment component drives weapon identity cues. |