Pepper.SFX
All Guidance pages

Guidance / Manual

State System

7.1 AudioState Overview

Location: Events/AudioState.cs

Purpose: Change multiple audio parameters based on game mode.

Properties

string StateName { get; }                         // Unique identifier
string StateGroup { get; }                        // Mutually exclusive group
IReadOnlyList<BusVolumeProperty> BusVolumes { get; }      // Bus volume changes
IReadOnlyList<SwitchProperty> SwitchValues { get; }       // Switch changes
IReadOnlyList<RTPCProperty> RtpcValues { get; }           // RTPC changes
IReadOnlyList<EffectProperty> EffectProperties { get; }   // Mixer effect changes

7.2 State Groups

Concept: States in the same group are mutually exclusive.

Example:

State Group: "Location"
├── State: "Normal"
├── State: "Underwater"
└── State: "Cave"

State Group: "GameplayState"
├── State: "Menu"
├── State: "Playing"
└── State: "Paused"

Behavior:

  • Only one state per group is active at a time
  • Setting “Underwater” makes it the group’s active state in place of “Normal” or “Cave”
  • Different groups can have active states simultaneously
  • Setting a state applies its own values only. Nothing from the previous state is undone, so give each state in a group a value for every bus, switch and RTPC its siblings change (“Normal” sets the SFX bus back to 0 dB, for example). The exception is a state whose Bus Volume Mode is Offset (below): its bus offsets go back when it ends.
  • State names are unique across all groups
  • Setting the state that is already active does nothing
  • AudioManager.ClearState(group, transitionTime) leaves a group’s state without setting another: an Offset state’s offsets go back; nothing else is undone

Bus Volume Mode: Set Level or Offset

Bus Volume Mode The dB values mean Leaving the state
Set Level (default, as before 3.1) Each bus’s level: written into its Volume, the same value the bus inspector and SetBusVolume set (so in the editor it stays in the asset after Play) Nothing is put back
Offset How far each bus moves, in a slot of its own on top of its Volume and the player’s volume (§6.8). The asset is never changed. The offsets go back, over the next state’s (or ClearState’s) transition time

Use Offset for mix states, so they never undo a Settings page:

State Group "Mode":
  Mix_Single      (Offset, no bus volumes)
  Mix_LocalSplit  (Offset: Hero -3 dB, Speed -3 dB)
AudioManager.Instance.SetState("Mix_LocalSplit", 0.5f);   // Hero moves -3 dB over 0.5 s
AudioManager.Instance.SetState("Mix_Single", 0.5f);       // and back
  • Offset states in different groups add up on a bus (Split −3 dB and Nitro +2 dB: −1 dB); leaving one takes only its part away.
  • The offset moves in a straight line in dB over the transition time, on unscaled time, in Pepper’s per-frame step. A bus shows it as StateOffsetDb (and in its inspector in Play mode).
  • A state’s switches, RTPCs and effect properties work the same in both modes.

7.3 State Properties

BusVolumeProperty

[Serializable]
public class BusVolumeProperty
{
    public AudioBus bus;
    public float volumeDb;
}

Example:

State: "Underwater"
BusVolumes:
  - SFX Bus: -9 dB (muffle sounds)
  - Music Bus: -12 dB (distant music)
  - Dialogue Bus: -6 dB (slightly muffled)

SwitchProperty

[Serializable]
public class SwitchProperty
{
    public string switchGroup;
    public string switchValue;
}

Example:

State: "Underwater"
SwitchValues:
  - switchGroup: "Location", switchValue: "Underwater"
  - switchGroup: "Reverb", switchValue: "LargeHall"

RTPCProperty

[Serializable]
public class RTPCProperty
{
    public string parameterName;
    public float value;
}

Example:

State: "Combat"
RtpcValues:
  - "MusicIntensity": 1.0
  - "HeartbeatIntensity": 0.8

EffectProperty

[Serializable]
public class EffectProperty
{
    public AudioBus bus;
    public string propertyName;
    public float value;
}

Example:

State: "Underwater"
EffectProperties:
  - Bus: Master, Property: "ReverbMix", Value: 0.8
  - Bus: SFX, Property: "LowpassCutoff", Value: 1000.0

7.4 State Transitions

Transition Time: Controls how smoothly parameters change.

public void SetState(string stateName, float transitionTime = 0.5f)
{
    if (!states.TryGetValue(stateName, out var nextState)) return;   // logs a warning
    if (activeStatesByGroup.TryGetValue(nextState.StateGroup, out var current) && current == nextState)
        return;                                                       // already active

    activeStatesByGroup[nextState.StateGroup] = nextState;
    nextState.Apply(transitionTime);
}

Apply Implementation (simplified):

public void Apply(float transitionTime)
{
    var manager = AudioManager.Instance;

    // Bus volumes (Set Level): transition over transitionTime, or set at once when it is 0.
    // (An Offset state's bus volumes are moved by SetState and ClearState, which know the group.)
    foreach (var busVol in BusVolumes)
    {
        if (transitionTime > 0f) manager.TransitionBusVolume(busVol.bus, busVol.volumeDb, transitionTime);
        else busVol.bus.SetVolume(busVol.volumeDb);
    }

    // Switches: always immediate (switches don't transition)
    foreach (var sw in SwitchValues)
        manager.SetSwitch(sw.switchGroup, sw.switchValue);

    // RTPCs: transition over transitionTime too, or set at once when it is 0
    foreach (var rtpc in RtpcValues)
    {
        if (transitionTime > 0f) manager.TransitionRTPC(rtpc.parameterName, rtpc.value, transitionTime);
        else manager.SetRTPC(rtpc.parameterName, rtpc.value);
    }

    // Mixer effect properties: transition over transitionTime
    foreach (var effect in EffectProperties)
        manager.SetBusEffectProperty(effect.bus, effect.propertyName, effect.value, transitionTime);
}

Two quick state changes and RTPCs: a newer RTPC transition on the same parameter replaces the one running (TransitionRTPC, §3.4), so changing state again before the first transition ends moves the value from where it is to the new state’s value. Bus volume transitions replace each other the same way.

Effect properties are found by name: the mixer must expose a parameter called <BusName>_<Property>, for example SFX_LowpassCutoff.

7.5 Common State Patterns

Underwater State:

StateGroup: "Location"
StateName: "Underwater"

BusVolumes:
  - SFX: -9dB
  - Music: -12dB
  - Ambience: -6dB

SwitchValues:
  - "Location": "Underwater"

RtpcValues:
  - "Underwater": 1.0

TransitionTime: 1.5s

Pause State:

StateGroup: "GameplayState"
StateName: "Paused"

BusVolumes:
  - SFX: -20dB
  - Music: -12dB
  - UI: 0dB (unchanged)

TransitionTime: 0.3s

Combat State:

StateGroup: "CombatState"
StateName: "InCombat"

RtpcValues:
  - "MusicIntensity": 1.0
  - "HeartbeatIntensity": 0.7

TransitionTime: 2.0s

From the Pepper.SFX 3.1 guides (USER_GUIDE.md). Something wrong or unclear? Tell me.