Guidance / Cookbook
Occluding SFX Through Geometry
SFX voices can be muffled when walls or props stand between the listener and the emitter. The mechanism is the occlusion mixer slot pool (see Manual chapter 14 for the architecture). This section walks the authoring once, then shows how to opt individual containers into the system.
🧱 This is the foundation for two other features too. Per-Zone Reverb (
AllowReverbSend) and container-level Ambient Propagation (UsePropagation) both acquire the same Occlusion Slot at play time. If you skip the slot-pool authoring in One-Time Project Setup below, none of the three features works fully — voices silently fall back to direct-to-bus routing with no low-pass and no sends, and no error message. Only occlusion’s and propagation’s volume drop still applies, because it doesn’t need a slot. Author the layout once; opt individual containers in afterward.
⚠️ Silent failures to know up front
- No
OcclusionLayoutauthored → containers withUse OcclusionorUse Propagationonly get quieter behind walls and doors (no low-pass), andAllow Reverb Senddoes nothing. No error.- Container’s
Mixer Groupnot in the layout’s bus list → slot acquisition skipped silently, with the same result. Re-run the builder after adding new buses.- Occlusion doesn’t check Is3D: a 2D voice is still tested from the listener to wherever its AudioSource sits, and muffled if that line is blocked. The flag also costs a slot — turn it off for UI/2D containers.
- Slot pool exhausted → new voices route direct-to-bus (no low-pass, no sends; the volume drop still applies) and a console warning fires. Bump Slots Per Bus if you see it.
Setup Path
Follow these in order. Steps 1–2 are required; the rest are per-container or per-feature opt-ins.
- One-Time Project Setup — author the slot pool. Do this once per project.
- Occlude a Container — flip
Use Occlusionon each container that should be muffled. - (Optional) Soften the Edge with Multi-Ray — multi-ray smoothing.
- (Optional) Compose with Propagation — let one container respect both walls and doors.
Recipe: One-Time Project Setup (5 minutes)
📋 What: Author the slot layout so any container with Use Occlusion = true can be muffled at runtime.
🎯 Use Case: You’re shipping SFX that should sound muffled behind cover, walls, or interior geometry. This is the prerequisite for every occluded sound in your project — author it once.
Asset Setup
1. Create an OcclusionLayout asset:
- Right-click in your Audio assets folder → Create → Audio System → Occlusion Layout
- Name it
OcclusionLayout(one per project)
2. Open the builder window:
- Menu: Window → Audio System → Occlusion Layout
3. Configure:
- Voice Mixer: your project’s voice mixer (where SFX are routed)
- Occlusion Layout: drag in the asset you just created
- Slots Per Bus: leave at 6 (the default sizes well for most SFX-heavy scenes; bump to 8–12 for weapon-dense gameplay)
- (Optional) Reverb Send Bus Registry: assign if you also use per-zone reverb sends — covered in the Per-Zone Reverb recipes below
4. Click “Scan, Auto-Create, Refresh”:
- The builder walks the project for containers with
Use Occlusion,Allow Reverb SendorUse Propagationturned on (each of the three takes a slot at play time), and for Ambient Sources withUse Occlusionin the open scenes. It collects the unique buses they play through (a layer withUses Parent Outputcounts its Blend’s or Switch’s bus) and authors:- One
<BusName>_OcclusionLayerchild group per bus - Six slot groups under each (
<BusName>_Slot_00through<BusName>_Slot_05) - A Lowpass effect with exposed cutoff parameter on each slot
- One
- The results panel shows which buses got authored and any that failed (typically because the bus doesn’t exist yet on the mixer)
5. Wire AudioManager:
- On your scene’s AudioManager GameObject:
- Voice Mixer: the same mixer asset
- Occlusion Layout: the
OcclusionLayoutasset - Default Occlusion Slots Per Bus: 6 (matches the builder)
- Press Play. The Console should print
Occlusion slot pools ready — N bus(es), M slot(s) total.
✅ Result: The infrastructure is live. Any container with Use Occlusion = true will now acquire a slot on Play, get its cutoff modulated by the raycast occlusion path every frame, and release the slot on completion.
Recipe: Occlude a Weapon Fire Sound
📋 What: Turn on occlusion for a single container.
🎯 Use Case: Bullet impacts, weapon shots, footsteps — any SFX whose perceived loudness and brightness should drop when the listener can’t see the emitter.
Asset Setup
1. Open your container (Random / Routing / Sequence — any type):
- Find the “Use Occlusion” toggle (under Mixer Settings)
- Set it to ✓ (true)
2. Make sure the container is 3D:
- Is3D: ✓
- Min Distance / Max Distance / Rolloff: set as appropriate
- Occlusion only modulates 3D-positioned sounds; 2D voices ignore the raycast
3. Verify the container’s Mixer Group is one of the buses the layout authored. If not, re-run the Occlusion Layout Builder.
What Happens at Runtime
weaponEvent.Post(gameObject, transform.position);
On Play:
AudioManager.GetVoice(container)acquires a free slot from the container’s bus pool.- The voice’s
outputAudioMixerGroupis set to the slot’s mixer group. - The OcclusionUpdate coroutine fires raycasts from listener → source every ~200 ms.
LateUpdatesmooths the resulting cutoff target into the slot’s exposed mixer parameter every frame.
When the listener walks behind a wall:
- The linecast hits geometry → blocked fraction climbs from 0 toward 1
- Gain target lerps from 1.0 → 0.3 (linear amplitude, ≈ -10 dB)
- Cutoff target lerps from 22 kHz → about 7 kHz (6.95 kHz when every ray is blocked). Propagation (
Use Propagation) can take it lower: the most-muffled value wins - Both axes settle in ~60 ms (configurable via
Occlusion Gain/Cutoff Smoothing Secondson AudioManager)
✅ Result: The weapon sound muffles when the listener is behind cover. No code changes — just the Use Occlusion toggle.
Recipe: Soften the Occlusion Edge with Multi-Ray
📋 What: Fan 3–5 rays at the source to produce a continuous gradient instead of a binary in/out step.
🎯 Use Case: You want occlusion to feel natural as the listener walks past a wall edge, not “snap” the moment line-of-sight breaks.
Setup
On the AudioManager component:
- Occlusion Ray Count: 3 (default) or 5
- Occlusion Ray Spread Meters: 0.5 (default), 1–2 for larger emitters
With RayCount = 3 and Spread = 0.5 m, the system fires a center ray plus two side rays at ±0.25 m perpendicular to the listener-source axis. The blocked fraction (0/3, 1/3, 2/3, or 3/3) lerps the gain and cutoff targets accordingly; the per-frame smoother rides over the discrete levels for continuous perception.
Tuning Notes
- Ray Count = 1: identical to the original binary occlusion path. Use when you want hard edges (cover-shooter rooms with tight corner-peeking).
- Ray Count = 3: four-level gradient. Best balance for most outdoor and large-interior scenes.
- Ray Count = 5: six-level gradient. Smoothest, but the cost is 5× raycasts — usually unnecessary above 3.
- Spread Meters: scale with emitter size. A door-sized emitter (1 m) wants 0.5 m spread; a vehicle-sized emitter (3 m) wants 1.5 m.
✅ Result: Walking past a wall edge produces a smooth gradient of muffling and uncovering, not a step at the exact moment line-of-sight breaks.
Recipe: Compose Occlusion with Propagation
📋 What: Make an SFX container respect both raycast occlusion (walls) AND propagation routing (doors).
🎯 Use Case: A door-knock sound that should be both muffled by the door (propagation) and additionally muffled by any clutter between the listener and the door (raycast).
Setup
On the container:
- Use Occlusion: ✓ (raycast path)
- Use Propagation: ✓ (propagation path)
What Happens
Both paths feed LateUpdate independently. The composition is most-muffled-wins:
gainTarget = raycastGain × propagationGain (multiplicative)
cutoffTarget = min(raycastCutoff, propagationCutoff) (min-wins for cutoff)
- If line-of-sight to the emitter is clear but the door is closed → propagation dominates (the cutoff comes from the door).
- If line-of-sight is blocked but the door is open → raycast dominates.
- If both are partly blocked → both contribute; the tightest cutoff wins.
When the listener is outside every registered AudioZone, propagation has no opinion. Default behavior keeps the SFX audible (propGain = 1, propCutoff = 22kHz); flip PropagationManager.Silence Outside Graph if you want a strict “outside means silent” coverage model instead.
✅ Result: A single SFX container that handles both routing-based muffling (doors, openings) and clutter-based occlusion (walls, props) in one place.
Troubleshooting
“My container has Use Occlusion = true but the sound doesn’t muffle.”
- Does it get quieter behind a wall but keep its treble? Then occlusion runs but the voice has no slot: the volume drop doesn’t need one, the low-pass does. Check the next three points.
- Did you assign
Voice MixerANDOcclusion Layouton AudioManager? - Did you re-run the Occlusion Layout Builder after adding this container?
- Is the container’s
Mixer Groupone of the buses in the layout? If you check the OcclusionLayout asset’sBuseslist and the container’s bus isn’t there, the layout is stale. - Is the container 3D? Occlusion casts from the listener to the voice’s AudioSource. It doesn’t skip 2D voices, but their position rarely means anything, so use it on 3D sounds.
“Console warns ‘Occlusion slot pool exhausted on bus X’.”
- More concurrent occluded voices on that bus than slots configured. Bump
Slots Per Buson the Occlusion Layout asset, re-run Generate, and verify the warning stops.
“Console warns ’… on bus X, which has no slots in the Occlusion Layout’.”
- A container with
Use Occlusion,Allow Reverb SendorUse Propagationplayed through a bus the layout has no slots for, so its sounds there get no low-pass and no reverb send. The layout was made before that bus needed slots. Click Scan, Auto-Create, Refresh in the Occlusion Layout window again. The warning comes once per bus, and only when an Occlusion Layout is assigned.
“The first frame of audio clicks.”
- Almost never the slot system — slots are acquired before
Play()precisely to avoid this. Suspect the clip itself: leading sample boundary, encoder pre-roll, or a Stop→Play on a non-pooled AudioSource elsewhere in the project.
“My slot has a Lowpass on it but the cutoff parameter never changes.”
- Check
voiceMixer.GetFloat(slot.CutoffParam, …)in a debug script. If it stays at 22000, the voice never opted in (container’sUse Occlusionis off, container has no slot, orOcclusionPerfis skipping it due to a null container reference). - The exposed-parameter name on the mixer must match
slot.CutoffParamexactly. Re-run the layout builder if you’ve manually renamed mixer groups.
From the Pepper.SFX 3.1 guides (USER_GUIDE.md). Something wrong or unclear? Tell me.