Reference · Updated Jun 25, 2026

Audio Audit — Spatialization / Play Sound 2D (2026-06-25)

Audio Audit — Spatialization / Play Sound 2D (2026-06-25)

Triggered by a play-test report: "when another player dies and respawns I hear their gun equipping, no matter where I am on the map." This doc is the consolidated audit — the equip fix, the full Play Sound 2D sweep, and the rule for classifying any future sound. Bug record: BugTracker.md BUG-024.


#TL;DR

  • The equip audio is FIXED (content-side, live now — no rebuild needed): both equip cue notifies now
  • attach their sound to the equipping player, so it spatializes from them and falls off with distance.

  • A C++ change (ExecuteEquipCue sets CueParams.Location) backs it up but is **redundant given the attach
  • fix**; it lands on the next rebuild.

  • One unrelated finding from the sweep — BP_GA_SL_Grenade_Throw plays its throw sound 2D — was decided
  • gate-local (thrower-only). Asset cleaned; one manual BP step remains.


#1. The equip bug (BUG-024)

Mechanism. USLGameplayAbility_Equip::ExecuteEquipCue fires ASC->ExecuteGameplayCue(EquipCueTag). GAS multicasts the cue to every client (correct — it's a third-person cosmetic), so a remote player's respawn equip executes on your client.

Root cause (corrected — see BUG-024 for the wrong first guess). The equip cues are data-driven GameplayCueNotify_Burst assets. Their burst_sounds[0] = Rifle_Raise_Cue (which does have WeaponHandling_att attenuation), spawn policy AlwaysPlay, placement DoNotAttach. DoNotAttach → the notify spawns the sound at the cue's world location — and ExecuteEquipCue never set CueParams.Location, so it spawned at world origin (0,0,0). The compact TestMap playspace sits inside the sound's falloff of origin, so it reads as "heard everywhere." (It was never a Play Sound 2D node — a binary scan found no sound node in the cues at all.)

Every equip routes through just two cue notifies (all weapons funnel into these):

Weapon data assetEquipCueTagCue notifyFixed
DA_AssaultRifleGameplayCue.Weapon.AssaultRifle.EquipGC_SL_AssaultRifle_Equip
DA_WeaponPlaceholderGameplayCue.Weapon.AssaultRifle.EquipGC_SL_AssaultRifle_Equip
DA_ShotgunGameplayCue.Weapon.Shotgun.EquipGC_SL_Shotgun_Equip
DA_UnarmedGameplayCue.Weapon.Shotgun.EquipGC_SL_Shotgun_Equip
DA_SL_Pistol(none)— (sidearm uses local DrawSound)n/a

Fix (applied):

  • Cue (the real fix, live): both GC_SL_AssaultRifle_Equip and GC_SL_Shotgun_Equip
  • burst_sounds[0] placement set to AttachToTarget (override_placement_info=True). The sound now anchors to the equipping player's actor regardless of Location. Saved via the MCP bridge.

  • C++ (redundant backup, pending rebuild): ExecuteEquipCue now sets `CueParams.Location =
  • Pawn->GetActorLocation() + Normal. Helps any cue that uses Location`; not strictly needed once the sound attaches.

Bridge note: GameplayCueNotify struct fields are EditDefaultsOnly and can't be set on struct-value
copies via Python ("cannot be edited on instances"). The working approach: rebuild the whole
burst_effects via struct constructors (which bypass the check) and assign it top-level on the CDO.

Observation (not part of the fix): DA_Unarmed reuses the Shotgun equip cue and DA_WeaponPlaceholder reuses the AssaultRifle one — so unarmed plays a shotgun "raise" sound, etc. Content-quality nit, not a spatialization bug. Revisit when those get bespoke audio.


#2. Full Play Sound 2D sweep

Scanned all C++ and all .uasset Blueprints (the PlaySound2D import string appears in the binary when a BP contains the node).

LocationSoundVerdict
C++ SLCharacterBase.cpp — sidearm DrawSound / HolsterSoundper-weaponCorrect. Case A, IsLocallyControlled()-gated FP feedback.
WBP_SL_RespawnOverlaySC_RespawnCountDownCorrect. UI widget; exists only on the local player's HUD — 2D is right.
BP_SL_MasterChiefshield / heartbeat / death / respawn cuesCorrect by design. Uses SC_ (2D local) paired with SC_TP_ siblings that carry WeaponHandling_att (3D, for observers). Verify the 2D calls stay IsLocallyControlled-gated.
BP_GA_SL_Grenade_ThrowSC_FragThrowFinding → gate-local (below).

#3. Grenade throw sound — gate-local (decided 2026-06-25)

SC_FragThrow was played 2D inside a Local-Predicted ability. Local-Predicted runs on the owning client

  • the server, so on a listen server the host heard every player's throw flat; other clients heard nothing
  • (the node never runs for them). Decision: thrower-only feedback.

  • Done (bridge): cleared the now-meaningless SA_Default3D attenuation off SC_FragThrow (it's 2D-only;
  • verified the throw ability is its only referencer). The asset now reflects its 2D-local use.

  • PENDING — manual BP step: in BP_GA_SL_Grenade_Throw, gate the Play Sound 2D node behind an
  • Is Locally Controlled node → BranchTrue → Play Sound 2D. Result: only the thrower hears it; the listen host stops hearing remote throws.


#4. The rule (use for any future sound)

Ask first: should other players hear it, and should distance matter?

Who hears it?NodeAttenuationGate
Only the local player (FP feedback, UI)Play Sound 2Dn/a (2D)IsLocallyControlled() — must only run on the owner
Everyone, with distance (diegetic)Play Sound at Location / Spawn Sound Attached, ideally in a GameplayCuerequired (SA_Default3D or per-sound)none — it's meant for all clients

Catches:

  • Attenuation only applies to 3D playback — Play Sound 2D ignores it. A correctly-attenuated sound
  • still plays map-wide if the play node is 2D.

  • A GameplayCueNotify_Burst sound with DoNotAttach plays at CueParameters.Location — if the caller
  • never sets it, it plays at world origin. Prefer AttachToTarget, or ensure the caller sets Location.

  • GAS cues are multicast to all clients — any cue sound must be spatialized.

Reusable asset: SA_Default3D (/Game/SystemLink/Audio/Attenuation/) — Natural-Sound falloff, 4 m full volume → silence by ~44 m. A sensible default; make a longer-range variant for explosions later.


#5. Outstanding

  • [ ] Rebuild to compile the ExecuteEquipCue Location change (redundant given the attach fix, but
  • keep it consistent).

  • [ ] BP_GA_SL_Grenade_Throw — gate the throw Play Sound 2D behind Is Locally Controlled (§3).
  • [ ] (optional) give DA_Unarmed / DA_WeaponPlaceholder their own equip cues instead of borrowing
  • Shotgun/AR (§1 observation).