INSODIMENSIONStudios
FoleyGuides

Character SFX

Breathing loops, effort one-shots, sprint transitions, and attribute-driven body audio

Character SFX

Event-driven body audio — breathing loops that ramp with exertion, effort grunts on jump and land, sprint-recovery wheezes, low-health heartbeat. Zero-tick design where it counts: subscribed to footstep events, attribute change delegates, or AnimInstance polling — your choice per trigger.

Authoring custom Blueprint Events or Triggers? After this overview, jump to BP Authoring — Events & Triggers for the heartbeat case study and the seven gotchas that ship with the territory.


The Three-Layer Architecture

CharacterSFX intentionally separates what plays, when it plays, and how it plays into three classes:

┌──────────────────────────────────────────────────────────────┐
│ UAgenticCharacterSFXConfig  (DA_CharacterSFX_*)              │
│                                                              │
│   EventConfigs[]                ← WHAT plays per tag         │
│     ├─ CharacterSFX.Breathing.Walk → Loop (Breathing)        │
│     ├─ CharacterSFX.Breathing.Run  → Loop (Breathing)        │
│     ├─ CharacterSFX.Effort.Jump    → OneShot (Grunt)         │
│     └─ ...                                                   │
│                                                              │
│   EventTriggers[]               ← WHEN to raise each tag     │
│     ├─ Foley Event (Breathing)  → footstep state machine     │
│     ├─ Locomotion State         → AnimInstance tag poll      │
│     └─ Attribute Threshold      → GAS attribute delegate     │
└──────────────────────────────────────────────────────────────┘
                        ↓ executes via
┌──────────────────────────────────────────────────────────────┐
│ UAgenticFoleyComponent::TriggerCharacterSFX(Tag, Context)    │
│                                                              │
│   1. Look up EventConfigs for Tag                            │
│   2. For each event → Event->ExecuteEvent(Component,Tag,Ctx) │
│   3. Events manage AudioComponent pool + loop state          │
└──────────────────────────────────────────────────────────────┘

The polymorphism (Abstract, Blueprintable, EditInlineNew, Instanced on both base classes) is what makes the system extensible. Authors write new Event types and Trigger types in C++ or Blueprint without touching the config DataAsset or the foley component itself. The DataAsset holds arrays of base-class instances; UE's Instanced reflection serializes them by their runtime subclass; the Details panel's class-picker dropdown lets you pick a subclass when adding an array entry.


A Configured DataAsset

A DA_CharacterSFX_* with event configs and triggers populated:

DA_CharacterSFX_Custom internals — EventConfigs (Breathing Walk/Run/Sprint, Effort Jump/Land, Heartbeat) and EventTriggers (Foley Event, Attribute Threshold)

Top-level fields on the DataAsset:

FieldTypeNotes
EventConfigsTArray<FAgenticCharacterSFXConfigEntry>Tag → events map. Multiple events can layer under the same tag (Grunt + BreathHold under Effort.Jump).
EventTriggersTArray<UAgenticCharacterSFXTrigger*>Auto-fire triggers. Initialize runs at component BeginPlay.
AudioPoolSizeint32 (2–8)Pre-allocated UAudioComponent slots. Default 4 covers concurrent breathing loop + effort one-shot + spare. Bump to 68 if you stack more layers.
GlobalVolumeScalefloat (0–2)Master scalar across every CharacterSFX event from this config. Separate from the surface-foley layer.

Shipped Event Types — What Plays

Two concrete UAgenticCharacterSFXEvent subclasses ship with the plugin.

Loop (Breathing)

Looping sounds with crossfade, fade-in, fade-out, and timeout-driven auto-stop. Use for:

  • Breathing loops that ramp with locomotion (walk → run → sprint)
  • Sustained body audio (heavy armor clank loop while sprinting)
  • Status SFX (on-fire hiss, bleeding gurgle, low-health heartbeat)
FieldRangeNotes
SoundUSoundBase*Looping USoundCue or UMetaSoundSource.
CrossfadeDuration0.05–5 sCrossfade when the active tag changes (walk → run). 0.5 is a good breathing default.
FadeInDuration0.05–5 sFirst-start ramp from silence. 0.15 = barely audible.
FadeOutDuration0.05–5 sStop ramp. 0.15 for snappy, 0.5 for soft.
TimeoutDuration0–30 sSeconds without PulseCharacterSFX calls before auto-stop. 3.0 for responsive fade, 0.0 = persistent.
BaseVolume0–20.10.3 so breathing sits well under footsteps.
bScaleVolumeByMagnitude + Min/MaxVolume lerps with Context.Magnitude. Use for breathing that gets louder as the character sprints harder.
bScalePitchByExertion + Min/MaxPitch lerps with Context.ExertionLevel. Higher exertion = higher pitch = gasping quality.

One-Shot (Grunt/Effort)

Discrete sounds with random pool selection, cooldown, and pitch variation. Use for:

  • Effort grunts (jump pushoff, heavy landing)
  • Pain cries on damage
  • Battle cries
  • Any one-shot character vocalization
FieldRangeNotes
SoundsTArray<USoundBase*>Pool — random pick per fire, avoiding immediate repeats. 4+ entries gives natural variety.
bPlay2DboolPlays 2D (no spatialization). Useful for first-person breathing right in the player's ear.
Cooldown0–10 sMinimum seconds between fires — prevents spam. 0.3 works for jump.
BaseVolume / scale-by-magnitudeSame lerp pattern as Loop.
BasePitch / PitchVariation0.5–2 / 0–0.5PitchVariation = 0.05 = BasePitch ± 5% per fire.

Shipped Trigger Types — When to Raise Tags

Three concrete UAgenticCharacterSFXTrigger subclasses ship. Each one subscribes to a different condition source — pick the right one per use case.

1. Foley Event (Breathing)

UAgenticCharacterSFXTrigger_FoleyEvent. Subscribes to the foley component's OnFoleyEventResolved delegate — fires on every footstep. Classifies steps into Walk / Run / Sprint by the Foley.Event.* tag, accumulates an ExertionScore, and raises the matching breathing tag automatically. On stop it picks StopShort (low exertion) or StopLong (high exertion wheeze). On Jump/Land it fires the matching Effort tag.

Use it when you want breathing that follows footstep gait. No AnimBP dependency, no polling — drop it into EventTriggers and the breathing state machine runs itself. This is the trigger every character should have.

2. Locomotion State

UAgenticCharacterSFXTrigger_LocomotionState. Polls a named FGameplayTag property on the pawn's UAnimInstance every frame via reflection. Configurable match-tag set, optional speed-based magnitude.

Use it when you want breathing tied to a specific AnimBP state rather than footstep classification — e.g., a dedicated "stamina drain" state that drives breathing whether or not footsteps are firing. Works with any AnimInstance that exposes a FGameplayTag property — zero compile-time dependency on AgenticLocomotion.

3. Attribute Threshold

UAgenticCharacterSFXTrigger_Attribute. Binds to GAS's OnGameplayAttributeValueChange — fires only when the attribute changes, no tick. Configurable comparison (<, <=, >, >=), absolute or percentage threshold, hysteresis to prevent flicker, optional magnitude derived from the attribute value.

Use it when SFX should react to gameplay state. Canonical use cases:

SituationAttributeComparisonThresholdHysteresisEventTagToRaise
Heartbeat (low HP panic)Health<100 (abs) or 30%10 / 5CharacterSFX.Breathing.Heartbeat
Injured breathingHealth<25%5CharacterSFX.Breathing.Injured
Exhausted wheezingStamina<10%3CharacterSFX.Breathing.Exhausted
Battle cry on rage spikeRage>=100 (abs)5CharacterSFX.BattleCry
Mana surge whisperMana>=90%5CharacterSFX.ManaSurge
Bleed-out gurgleHealth<5 (abs)1CharacterSFX.Breathing.Dying

The pattern stacks — multiple Attribute triggers in one DataAsset, each raising a different tag, with EventConfigs playing the right Loop or OneShot per tag. No C++ touch.

Hysteresis is mandatory. Attribute values bounce near boundaries — without Hysteresis > 0 you get rapid on/off spam.

Requires GAS on the pawn. The trigger walks the owner for an UAbilitySystemComponent at Initialize. If none is found, it logs a warning and sits dormant.


Configuration

Open DA_CharacterSFX_Default (or duplicate it for a per-character variant) and:

  1. Swap sounds — open each EventConfigs entry, drill into Events[0], replace the Sound / Sounds field.
  2. Tune volumes + timeoutsBaseVolume = 0.10.3 so breathing sits below the surface layer. TimeoutDuration = 3.0 for responsive fade-out.
  3. Configure triggersFoley Event (Breathing) for footstep-driven breathing, Attribute Threshold for health/stamina-driven SFX, Locomotion State for AnimBP-driven breathing.
  4. Assign to character — on the foley component, set CharacterSFXConfig.

PIE test: walk, run, sprint, jump, stop. Take damage past your heartbeat threshold. Listen.

Verbose logging:

Log LogAgenticFoley Verbose

Each trigger fire and stop logs a line — the pipeline tells you exactly which tag fired, which event matched, and the resolved volume / pitch.


Sound Assets

Two complete sound sets ship with the plugin:

DataAssetSourceNotes
DA_CharacterSFX_DefaultSoldier Sound System pack9 EventConfigs (Breathing × 6 + Effort × 2 + Heartbeat)
DA_CharacterSFX_CustomElevenLabs SFX (our-origin, redistributable)8 EventConfigs

Switch by setting CharacterSFXConfig on the foley component. Both ship with a Foley Event (Breathing) trigger preconfigured; Default adds an Attribute Threshold trigger gating heartbeat at Health < 100.

MetaSound presets shipped

PresetPatternWaves
MSS_Breathing_SprintStartOne-shot, RandomGet4
MSS_Breathing_StopLongOne-shot, RandomGet5
MSS_Breathing_StopShortOne-shot, RandomGet5
MSS_Breathing_JumpOne-shot, RandomGet6
MSS_Breathing_LandOne-shot, RandomGet6
MSS_Breathing_WalkLoopLoop1
MSS_Breathing_RunLoopLoop1

Next Steps

  • BP Authoring — Events & Triggers — write your own Loop / OneShot / Trigger subclasses in Blueprint. Heartbeat case study with full lifecycle (idempotent spawn, fade-out, destroy, no stacking) plus the seven gotchas that bit us shipping it.
  • Multiplayer — CharacterSFX replicates through the same GAS GameplayCue pipeline as surface foley. No extra wiring.
  • Data Assets reference — full field list for UAgenticFoleyDataAsset and the broader DataAsset family.