# Presentation Pipelines: Audio, VFX, Cinematics & UI

V4 has to _sound_, _spark_, _cut_, and _read_ like six games at once: the brassy
bombast of a CoD set-piece, the sparse noir pulse of a Splinter Cell
infiltration, the erhu-and-orchestra swell of a Wukong boss, the diegetic
strings of a Commandos op, the modular sci-fi of an RTS skirmish, and the
synth-rock of a Contra run — all emitted by one shared presentation stack. Four
Unreal C++ modules own that job: **`V4Audio`** (MetaSounds patches, dynamic
music stacks, voice barks, foley, spatialization, the karaoke rhythm mode),
**`V4VFX`** (Niagara muzzle flashes, blood decals, debris, weather, Wukong
combat auras), **`V4Cinematics`** (Sequencer-driven
openers/boss-intros/finishers, the slow-mo "showdown" governor, Movie Render
Queue profiles, director commentary), and **`V4UI`** (the CommonUI/MVVM
front-end shell, six per-cell HUDs, the stealth light-gauge widget, and the full
accessibility contract). Where the combat and netcode layers must be _provably
correct_ ([./networking-determinism.md](./networking-determinism.md)), this
layer has the inverse remit: it is cosmetic, it may be as expensive as the
platform budget allows, and its only hard rule is that it must never feed back
into the integer state the lockstep and rollback paths hash.

What actually lives in the repo is the **decision, policy, and validation
layer** for that stack — built as four `UGameInstanceSubsystem`s plus a handful
of components and view-models, every one of which compiles and is exercised by
automation — sitting _above_ a set of engine plugins that are **linked but, with
two honest exceptions, not yet called**, and _above_ authored content that ships
today as JSON descriptors rather than binary `.uasset`. This page is part of the
**Presentation, Production & Launch** group; the section hub is
[../V4_ARCHITECTURE.md](../V4_ARCHITECTURE.md).

## What ships, honestly

The four modules are **real, deterministic C++ that builds and is tested**, but
they are a specification-and-decision layer, not a wired audiovisual runtime.
Six qualifications so the rest of the page reads at face value:

1. **The modules link the real presentation plugins.** `V4Audio.Build.cs:12-19`
   adds `MetasoundEngine`, `AudioModulation`, and `AudioGameplayVolume`;
   `V4VFX.Build.cs:12-16` adds `Niagara`; `V4Cinematics.Build.cs:12-23` adds
   `MovieScene`, `MovieRenderPipelineCore`, and `Sequencer` (editor-only); and
   `V4UI.Build.cs:12-21` adds `UMG`, `CommonUI`, `ModelViewViewModel`, and
   `FieldNotification`. Linking is not calling.

2. **Only two of the four modules touch an engine runtime at all — and
   modestly.** `V4Audio` calls `IPluginManager::Get().FindPlugin(...)` to
   _probe_ whether a spatialization plugin is installed
   (`V4AudioBootstrapSubsystem.cpp:7`), and `V4UI`'s HUD view-model calls
   `UMVVMViewModelBase::BroadcastFieldValueChanged` (`V4HUDViewModel.cpp:94`).
   Neither `V4VFX` nor `V4Cinematics` invokes its plugin at all: there is **no**
   `UNiagaraFunctionLibrary::SpawnSystem*`, **no** `ULevelSequencePlayer`, and
   **no** `SetGlobalTimeDilation` anywhere in their source. They are bookkeepers
   and state machines, not spawners.

3. **There are zero binary `.uasset` presentation assets.** `V4/ue/Content`
   holds **no** `.uasset` at all; the presentation content directories ship
   **102 `*.uasset.v4asset.json` descriptors** instead — MetaSounds patches,
   Niagara systems, level sequences, loading-screen libraries, data assets —
   none of which a soft-object path resolves to yet. The cue, patch, and
   sequence soft paths the C++ builds (`/Game/V4VFX/Niagara/...`,
   `/Game/V4Audio/MetaSounds/...`, `/Game/V4Cinematics/...`) name content that
   is specified but not authored.

4. **The dispatchers do real policy, not real emission.** The VFX spawner
   enforces per-LOD budgets and cooldowns and tracks active counts; it does not
   put a particle on screen. The cinematic subsystem advances a playhead and
   flips a state enum; it does not drive a Sequencer. The slow-mo subsystem
   computes an `EffectiveTimeDilation` curve; it does not slow the world. The
   _gate_ is the product here — the engine hand-off is the pending integration.

5. **The decision math is genuine and value-tested.** Footstep attenuation,
   spatial occlusion gain, decibel ducking, rhythm-timing judgement, slow-mo
   blends, and MVVM change-notification are real algorithms asserted against
   specific computed numbers across **16 automation specs** under
   `V4Tests/Private/{V4AudioTests,V4VFXTests,V4CinematicsTests,V4UITests,V4AccessibilityTests}`.
   They would fail against a hardcoded return.

6. **Authored data sidecars are real and locked.**
   `V4/ui/launch-experience/launch-experience.json` and the six
   `V4/cinematics/*.json` commentary manifests (2,148 lines) are
   schema-versioned feature contracts with owners and QA-coverage lists — design
   data, not code, and honestly labeled as such.

Everything below is real where it cites a function and a line; where it names an
authored asset or an un-called plugin, that is the specified-but-pending part.

## The shared shape: four subsystems, one discipline

All four modules are built the same way and for the same reason. Each is a
`UGameInstanceSubsystem` (or, for `V4UI`, a small family of subsystems plus a
view-model and widget) that holds registered **catalogs** in `TMap`s and exposes
three kinds of entry point: `Build…`/`Default…` factories that emit launch
manifests, `Validate…` self-checks that fail loud with a `TArray<FString>` of
errors, and `Resolve…`/`Dispatch…`/`Judge…` decisions that are pure functions of
their inputs. That split lets every rule be unit-tested without a live world and
keeps the math side-effect-free, so the same call yields the same answer on
every machine — the same determinism hygiene the gameplay core demands, applied
to the cosmetic layer.

```mermaid
flowchart TD
    subgraph Decision["V4 presentation — real, tested C++ (this page)"]
        AUD["V4Audio<br/>MusicStack · VoiceBark · Foley · Spatial · Bootstrap"]
        VFX["V4VFX<br/>VFXSpawner (budget + cooldown gate)"]
        CIN["V4Cinematics<br/>Cinematic state machine + SlowMo governor"]
        UI["V4UI<br/>MVVM view-model · per-cell HUDs · accessibility"]
    end
    GAS["GAS verbs / combat / stealth"] --> AUD & VFX & CIN
    UI -->|reads, never writes| GAS
    AUD -. "IPluginManager probe (real)" .-> PLUG["SteamAudio / Resonance"]
    UI -. "BroadcastFieldValueChanged (real)" .-> FN["FieldNotification"]
    AUD -. soft path .-> MS["MetaSounds .uasset — not authored"]
    VFX -. soft path .-> NS["Niagara .uasset — not authored"]
    CIN -. soft path .-> LS["LevelSequence .uasset — not authored"]
    UI -. soft class .-> WBP["WBP_* widgets — not authored"]
```

The dashed edges are the boundary: two are real engine-runtime touches, the rest
are soft references awaiting authored content and a spawn/playback call site.

## Audio — `V4Audio`

### Runtime mixers: music, barks, foley, spatial

Four small subsystems and a component do the live audio decisions, each with its
own automation spec.

**Dynamic music stacks.** `UV4MusicStackSubsystem` holds a per-cell stack of
`FV4MusicLayer`s and resolves a blend target by _summing the active layers'
volumes_ and clamping to unity: `GetCellTargetVolume`
(`V4MusicStackSubsystem.cpp:43`) returns
`FMath::Clamp(Σ active TargetVolume, 0, 1)`. Toggling a layer (`SetLayerActive`)
is how a cell transitions combat-to-ambient without restarting the bed
(`V4.Audio.MusicStack.Layering`).

**Voice barks with cooldown.** `UV4VoiceBarkSubsystem::DispatchBark`
(`V4VoiceBarkSubsystem.cpp:14`) keys barks `Operator.Bark`, and refuses a
dispatch when `CurrentTime − LastDispatch < CooldownSeconds` — a genuine
per-line rate limiter, not a coin-flip (`V4.Audio.VoiceBark.DispatchCooldown`).

**Stance-aware foley.** `UV4FoleyComponent::ResolveFootstepVolume`
(`V4FoleyComponent.cpp:21`) multiplies a per-surface volume by a **stance
multiplier** — Prone `0.25`, Crouch `0.55`, Sprint `1.25`, Stand `1.0`
(`:41-55`) — and by a speed ratio clamped to `[0.2, 1.5]`, then clamps the
product to `[0, 2]`. This is the audible side of the stealth sound-footprint
([./ai-perception-stealth.md](./ai-perception-stealth.md)): crouching is quiet,
sprinting is loud (`V4.Audio.Foley.StanceAndSurface`).

**Spatial attenuation.** `UV4SpatialAudioBridge::ResolveAttenuatedGain`
(`V4SpatialAudioBridge.cpp:51`) computes a linear-falloff distance gain
`clamp(1 − dist/radius, 0, 1)` times an occlusion gain `1 − occlusion`. It is a
real, testable attenuation model that runs without any audio device
(`V4.Audio.SpatialBridge.AttenuationAndOcclusion`).

### The bootstrap subsystem: manifests, ducking, providers, rhythm

`UV4AudioBootstrapSubsystem` (788 lines) is the catalog and policy hub. It
builds the launch manifest the audio team commits to — **110 weapon-foley
patches, 40 operators × 120 bark lines = 4,800 bark patches, 4,942 MetaSound
patches total, 6 music-stack cells, 12 ambience maps, 12 vehicle profiles** —
and `ValidateLaunchAudioManifest` fails if any floor is missed
(`V4.Audio.Bootstrap.Patches` pins every number).

Three pieces are worth reading as genuine domain logic:

- **Decibel ducking.** `ResolveDuckedMix` (`:370`) converts each rule's
  `DuckAmountDb` to a linear multiplier with `FMath::Pow(10, min(0, dB)/20)` —
  the correct dB→amplitude law — and applies it to the named target bus,
  accumulating the applied-rule ids. The default rules duck **music −7 dB under
  voice** and **SFX −5 dB under a critical callout** (`:364-367`), exactly the
  "music ducks under VO; SFX ducks under callouts" promise
  (`V4.Audio.Modulation.SnapshotsAndDucking`).

- **Honest provider probing.** `BuildDefaultSpatialProviderProfiles` (`:426`)
  ties each profile's backing-engine flag to whether the plugin is _actually
  enabled_: `V4IsPluginEnabled("SteamAudio")` and
  `V4IsPluginEnabled("ResonanceAudio")` call through `IPluginManager` (`:7-11`).
  The inline comment is explicit — it sets the flag "instead of asserting it
  unconditionally." This is a fail-loud capability seam, the opposite of a
  fabricated `true`.

- **Karaoke rhythm judging.** `JudgeRhythmInput` (`:616`) computes the timing
  offset in milliseconds and returns **Perfect (≤45 ms → 1000 pts, combo
  continues)**, **Good (≤110 ms → 500 pts, combo continues)**, or **Miss**,
  reading the per-note windows. The spec drives an input `0.52 s` against a
  `0.50 s` beat to Perfect, `0.59 s` to Good/500, and `0.70 s` to Miss — three
  outcomes a hardcoded return could not satisfy (`V4.Audio.KaraokeRhythmMode`).
  `BuildKaraokeRhythmCharts` ships three original, streaming-cleared charts, and
  `ValidateKaraokeRhythmMode` rejects any chart with fewer than 8 notes, 4 lyric
  cues, or a missing clearance flag.

## VFX — `V4VFX`

`UV4VFXSpawnerSubsystem` is a **budget-and-cooldown gate**, and that is the
honest description of the whole module: it decides _whether_ a Niagara cue may
play and records that it did, but never calls Niagara. `DispatchCue`
(`V4VFXSpawnerSubsystem.cpp:72`) walks four checks in order and returns an
`EV4VFXDispatchResult`:

1. unknown cue → `UnknownCue`;
2. inside the cue's cooldown → `CoolingDown`;
3. at the cue's per-cue `MaxActiveOverride` → `BudgetExceeded`;
4. at the **LOD-bucket budget** → `BudgetExceeded`;
5. otherwise increment the active count, append a `FV4VFXDispatchRecord`, and
   return `Spawned`.

The LOD buckets carry real defaults — **Hero 32, Medium 96, Background 160**
active systems (`V4VFXSpawnerSubsystem.h:67-74`), with per-bucket emitter caps
and tick intervals in `BuildDefaultLodBucketPolicies` (`:250` — Hero allows
dynamic lights and ticks every frame; Background caps at 3 emitters and ticks at
0.1 s). `ReleaseCueInstance` decrements the count so freeing a slot re-opens the
budget. The launch library is **22 cues across eight Niagara families** (muzzle
flash, blood, debris, weather, environment, Wukong combat, RTS projectiles,
arcade sprites), each with a family-tuned cooldown (muzzle flash `0.04 s`,
weather `1.0 s`). The `V4.VFX.Spawner.DispatchCooldownAndResolve` spec asserts
the exact Spawned→CoolingDown→Spawned sequence across a cooldown boundary, and
`V4.VFX.Spawner.LodBudgets` asserts the 32/96/160 budgets, a `BudgetExceeded`
when a 1-slot hero bucket is full, and recovery after a release. The pending
work is the last inch: turn a `Spawned` result into a `UNiagaraFunctionLibrary`
call against an authored system.

## Cinematics — `V4Cinematics`

`UV4CinematicSubsystem` models Sequencer playback as a **state machine over a
playhead float**, not a live `ULevelSequencePlayer`. `PlayCinematic` (`:159`)
returns `Started`, `UnknownCinematic`, or `AlreadyPlaying`; `Pause`/`Resume`
gate on the current state; and `AdvanceCurrentCinematic` (`:206`) clamps the
playhead to the duration and flips to `Finished` when it lands — so
`Advance(5 s)` on a 5-second opener reports `Finished` with the playhead pinned
at `5.0` (`V4.Cinematics.Playback.SequencerDrivenState`). The module also emits
the authoring contracts: **five Sequencer templates** (CampaignOpener,
BossIntro, ARPGFinisher, Takedown, RivalryPostMatch), each with required story
beats; **two Movie Render Queue profiles** (CoD campaign at 3840×2160 EXR,
marketing trailer at 30 fps ProRes 4444); per-cell camera-language presets; and
**nine director commentary tracks** gated on campaign completion via
`IsDirectorCommentaryUnlocked`, which only unlocks when the completed-campaign
list actually contains the campaign id
(`V4.Cinematics.Assets.TrackCameraAndTemplateValidation`,
`V4.Cinematics.DirectorCommentaryExtended`).

`UV4SlowMoSubsystem` is the **showdown / death-cam time-dilation governor**.
`ResolveBlendedDilation` (`V4SlowMoSubsystem.cpp:63`) lerps from `1.0` down to
the clamped target dilation over `BlendInSeconds`, holds for `DurationSeconds`,
then lerps back to `1.0` over `BlendOutSeconds`; `TickSlowMo` advances the clock
and auto-clears when the total elapses. The dilation is clamped to `[0.05, 1.0]`
on activation. The honest seam: it produces `GetEffectiveTimeDilation()` for a
caller to apply, but never calls `UGameplayStatics::SetGlobalTimeDilation`
itself — the curve is real, the application is the integration point
(`V4.Cinematics.SlowMo.BlendLifecycle`).

## UI / HUD — `V4UI`

### MVVM: the one real engine-runtime binding

`UV4HUDViewModel` is the module's genuine engine touch: it subclasses
`UMVVMViewModelBase` and, in `SetNumericBinding`/`SetTextBinding`, **dedupes**
(a no-op write returns `false` and broadcasts nothing) and otherwise calls
`BroadcastFieldValueChanged(FFieldId(BindingId, 0))`
(`V4HUDViewModel.cpp:92-95`). That is the "all HUD bindings via MVVM; no direct
attribute access from UMG" rule made concrete — a real
ModelViewViewModel/FieldNotification change-notification, not a stub.
`UV4MVVMRegistry` complements it with per-screen binding tables and
`BuildDefaultHUDBindings` (`V4MVVMRegistry.cpp:91`), which encodes the **six
per-cell HUDs** verbatim: Tactical-FPS (Health/Ammo/MiniMap/Killfeed), Stealth
(LightGauge/SuspicionRadius/Disguise/Objective), Tactics
(PartyPortraits/CommandQueue/Showdown), ARPG
(HP/Stamina/Will/Posture/CharmSlots/LockOn), RTS
(ResourceTicker/BuildMenu/MiniMap/GroupHotkeys), and Arcade
(Score/Lives/WeaponIcon/BossHealth) (`V4.UI.MVVMRegistry.Bindings`).

### Per-cell HUDs and the stealth bow-tie

`UV4StealthBowTieWidget` is a real `UUserWidget` subclass whose static
`BuildPresentation` (`V4StealthBowTieWidget.cpp:15`) maps an
`EV4LightExposureState` (shared from `V4Stealth`) to a deterministic
presentation: **Dark** → dim blue-grey tint, 0.2 wing-fill; **Dim** → amber
tint, 0.55 wing-fill; **Lit** → bright tint, full wings, with `bPulseAttention`
set when lux ≥ 1.0. This is the Splinter Cell light-gauge "bow-tie" read
straight off the stealth gauge — logic and presentation data are real even
though the visual `WBP` widget is not yet authored. `UV4UIBootstrapSubsystem`
registers the front-end shell screens and drives focus navigation for
gamepad-vs-mouse routing (`V4.UI.Bootstrap.NavigationAndFocus`).

### Accessibility

`UV4AccessibilitySubsystem` builds and validates the full launch accessibility
contract into a `UV4AccessibilitySettingsAsset`. The substantive parts are real
behavior, not toggles: `ShouldApplyAimAssist`
(`V4AccessibilitySubsystem.cpp:97`) **refuses aim assist in ranked PvP** when
the opt-out flag is set, and `ResolveWukongParryWindowFrames` widens the parry
window by a configurable bonus — a base of 6 frames plus the default 4 yields
**10 frames** (`:167`, asserted exactly). `ValidateAccessibilityLaunchCoverage`
enforces the floors: subtitle size > 1.0 with a speaker indicator, **four
colorblind profiles** (Protanopia/Deuteranopia/Tritanopia/Monochrome) each
covering HUD, minimap, and vision-cone overlays, **six high-contrast HUDs** (one
per cell), audio-cue toggles, two one-handed schemes, and difficulty toggles
that never silently disable achievements (`V4.UI.Accessibility.LaunchCoverage`).
The `V4/ui/launch-experience` manifest layers the loading screens, What's-New
modal, toast queue, and companion push on top.

## How it connects (and where it does not yet)

Presentation sits **downstream of gameplay and reads, never writes**. GAS verbs
and combat events ([./gas-layout.md](./gas-layout.md)) are what _should_ call
`DispatchCue`, `DispatchBark`, `PlayCinematic`, and `ActivateSlowMo`; the HUD
view-models _read_ attributes through MVVM and push nothing back. The poses
these effects dress come from the [animation pipeline](./animation-pipeline.md)
— the MetaHuman facial-performance and lip-sync flags the animation module
declares are realized here on the audio/VFX side — and the weather, fog, and
debris cues are emitted into the streamed sandbox the
[world-streaming & procgen](./world-streaming-procgen.md) layer manages. The
authored content these modules name — MetaSounds patches, Niagara systems, level
sequences, compiled WBP widgets — is tracked as the `*.v4asset.json` descriptor
set and belongs to the build-and-content path
([./build-data-content.md](./build-data-content.md)).

The honest remaining work is uniform across all four modules and identical in
shape to the animation layer's: the policy, math, and validation are real and
tested; the authored `.uasset` and the final engine-API call (Niagara spawn,
LevelSequence play, global time-dilation apply, MetaSound playback) are the
pending integration. These are **shippable units awaiting a call site and
authored content**, not stubs — every decision they make is computed,
deterministic, and pinned to a known-correct value.

## Related

- [Animation Pipeline](./animation-pipeline.md) — the poses, MetaHuman faces,
  and lip-sync this layer dresses with audio and VFX
- [World Streaming & Procgen](./world-streaming-procgen.md) — the streamed
  sandbox weather, fog, and debris cues are emitted into
- [Build, Data & Content](./build-data-content.md) — the `*.v4asset.json`
  descriptor set and cook path the authored presentation assets flow through
- [AI Perception & Stealth](./ai-perception-stealth.md) — the sound-footprint
  and light-exposure state the foley component and bow-tie widget present
- The section hub: [../V4_ARCHITECTURE.md](../V4_ARCHITECTURE.md)
