Skip to content

Music System

The Music System (CrawlerKitMusic module) handles background music across all scenes. A single MusicManager lives in the Main Menu scene and persists through every scene transition via DontDestroyOnLoad. It crossfades between states using dual AudioSources (A/B swap) and automatically switches to Combat music when enemies enter the party's range.


Music states

State When it plays
None Silence — no track playing
Menu Main menu scene
Exploration Dungeon levels, no enemies nearby
Combat At least one enemy is within Combat Radius cells of the party

Combat detection is proximity-based — the manager polls every second and checks whether any alive enemy is within a configurable number of grid cells. Enemies in other rooms don't trigger combat music. When no enemies remain nearby, the system waits a configurable delay before returning to Exploration.


Setup

1. Create MusicStateData assets

For each state you want music in, create a MusicStateData asset:

Assets → Create → Dungeon Crawler Framework → Music State Data

Configure each asset in the Inspector:

Field Description
Tracks Audio clips to play. Add as many as you want.
Play Order Random — picks a clip at random (avoids repeating the same one twice in a row). Sequential — plays in order, wrapping back to the first.
Loop Track If enabled, the current clip loops until the state changes. If disabled, automatically advances to the next clip when it ends.
Volume Target volume for this state (0–1). Multiplied by the manager's master volume.
Fade In Time Seconds to fade in when entering this state.
Fade Out Time Seconds to fade out when leaving this state.

2. Add MusicManager to the Main Menu scene

Create an empty GameObject in your Main Menu scene and add the Music Manager component (Dungeon Crawler Framework → Music Manager).

Assign your MusicStateData assets:

Slot Asset to assign
Menu Music Your menu MusicStateData
Exploration Music Your dungeon ambient MusicStateData
Combat Music Your combat MusicStateData

Inspector settings:

Field Description
Master Volume Global volume multiplier (0–1). All state volumes are multiplied by this.
Auto Combat Detection When enabled, automatically switches to Combat when an enemy enters range and back to Exploration when the area is clear.
Combat Radius Distance in grid cells within which an enemy triggers combat music. Enemies further away are ignored. Default: 6.
Detection Interval How often (in seconds) the manager checks for nearby enemies. Default: 1s.
Combat Exit Delay Seconds to wait after no nearby enemies remain before switching back to Exploration. Prevents music cutting in and out.
Debug Log Logs every state change and detection tick to the Console. Useful during development.

One MusicManager per project

Only place the Music Manager in the Main Menu scene. If the menu scene is loaded again (e.g. "Return to menu"), the duplicate is destroyed automatically. Do not add it to every dungeon scene.

3. Add SceneMusicSetup to every other scene

Add a Scene Music Setup component (Dungeon Crawler Framework → Scene Music Setup) to any GameObject in each scene (e.g. a _GameManager object). Set the State field to whatever music should play in that scene.

Field Description
State Music state to activate when this scene loads.
Immediate Skip the crossfade — cut to the new track instantly on scene load.

Common setups:

Scene type State
Main Menu (MusicManager auto-starts menu music on its own — no SceneMusicSetup needed)
Dungeon level Exploration
Boss arena Combat
Cinematic / cutscene None

Controlling music from code

Access the system through the service locator:

var music = CrawlerServices.Get<IMusicSystem>();

Switch state:

music.SetState(MusicState.Combat);
music.SetState(MusicState.Exploration, immediate: true); // no fade

Stop all music:

music.Stop();
music.Stop(immediate: true);

Adjust master volume at runtime:

music.MasterVolume = 0.5f;

Read current state:

if (music.CurrentState == MusicState.Combat)
    Debug.Log("Fight music playing");


How crossfading works

The manager keeps two AudioSource components (MusicSource_A and MusicSource_B). When a state change occurs:

  1. The outgoing source fades to volume 0 over fadeOutTime seconds.
  2. The incoming source starts at volume 0 and fades up to the target volume over fadeInTime seconds.
  3. Both happen simultaneously — the two fade times are independent.
  4. Once the fade completes, the outgoing source is stopped and the roles swap for the next transition.

This means a transition from Exploration to Combat uses the Combat state's fade times, and the transition back uses Exploration's fade times.


Architecture

CrawlerKitMusic/
└── Runtime/
    ├── IMusicSystem.cs       # Interface + MusicState enum
    ├── MusicStateData.cs     # ScriptableObject — audio clips + settings per state
    ├── MusicManager.cs       # Persistent MonoBehaviour — crossfade, combat detection
    └── SceneMusicSetup.cs    # Per-scene component — declares the base music state

The MusicManager registers itself as IMusicSystem in CrawlerServices during Awake, so any other module can reach it without a direct reference. It also re-binds to IEnemyManager on every scene load, so combat detection works correctly after scene transitions where the enemy manager is recreated.