Summary

AudioSource is the Unity component that plays audio. It is attached to a GameObject and references an AudioClip asset. For sound effects, the primary method is PlayOneShot, which plays a clip once without interrupting other sounds already playing on the same source. For background music, AudioSource.Play() combined with loop = true is standard. For transient world-positioned sounds (a coin collected, an enemy defeated), the static AudioSource.PlayClipAtPoint plays at a location without requiring an AudioSource on the emitting object.


Key ideas

AudioSource.Play() — plays the assigned clip from the beginning. If called again while playing, it restarts. Use for background music, ambient loops, and single persistent sounds.

AudioSource.PlayOneShot(clip) — plays the given clip once without replacing any currently playing sound on that source. Multiple calls layer on top of each other. Use for sound effects that may fire in rapid succession (coins, footsteps, hits).

AudioSource.PlayClipAtPoint(clip, worldPosition) — static method; no AudioSource component needed on the caller. Spawns a temporary GameObject at the given world position, plays the clip, then destroys itself. Use for pickups, explosions, and any transient world sound.

Looping background music: Set clip, loop = true, volume, then call Play(). Use a dedicated AudioSource component for music — separate from the one used for SFX — so volume can be controlled independently.

AudioSource.pitch — playback speed and pitch together (1 is normal; the Manual calls it the “Amount of change in pitch due to slowdown/speed up of the Audio Clip”, Unity Technologies, Unity Manual: Audio Source component reference). Small random values (0.9–1.1) are a cheap way to vary repeated SFX such as footsteps. It is not a tempo control for music: raising it makes the music faster and higher in key at once. To change musical tempo, switch on a bar line to a cue written at the new tempo (see “Changing tempo when combat starts” in unity-adaptive-music-crossfade).

AudioListener.volume — a static property (0 to 1) that scales all audio in the game. Setting it to 0f mutes everything; 1f is full volume.

[Range(0f, 1f)] — an Inspector attribute that displays a float field as a slider constrained to the given range. Useful for volume controls.

Mathf.Clamp01(value) — shorthand for Mathf.Clamp(value, 0f, 1f). Use when accepting a volume value from outside code.


In practice

Looping background music:

[Header("Music")]
[SerializeField] private AudioSource musicSource;
[SerializeField] private AudioClip   mainTheme;
[SerializeField] [Range(0f, 1f)] private float musicVolume = 0.4f;
 
private void StartMusic()
{
    if (musicSource == null || mainTheme == null) return;
 
    musicSource.clip   = mainTheme;
    musicSource.loop   = true;
    musicSource.volume = musicVolume;
    musicSource.Play();
}

SFX with PlayOneShot (layers, does not interrupt):

[Header("SFX")]
[SerializeField] private AudioSource sfxSource;
[SerializeField] private AudioClip   coinClip;
[SerializeField] private AudioClip   hitClip;
 
private void PlaySFX(AudioClip clip)
{
    if (clip == null || sfxSource == null) return;
    sfxSource.PlayOneShot(clip);  // layered — rapid calls don't interrupt
}
 
// Usage
PlaySFX(coinClip);
PlaySFX(hitClip);

PlayClipAtPoint — world-positioned sound, no AudioSource needed:

public void OnCoinCollected(Vector3 worldPosition)
{
    if (coinClip != null)
        AudioSource.PlayClipAtPoint(coinClip, worldPosition);
}

Read this method as an event response, not as an audio loop. Something else in the game detects the coin collection, then passes the coin’s world position into OnCoinCollected. The method checks whether a clip was assigned, plays the sound at that position, and then returns. There is no Update here because sound effects are triggered by game events, not checked every frame.

Global mute toggle:

private bool isMuted = false;
 
public void ToggleMute()
{
    isMuted = !isMuted;
    AudioListener.volume = isMuted ? 0f : 1f;
}

Adjustable music volume:

public void SetMusicVolume(float volume)
{
    musicVolume = Mathf.Clamp01(volume);
    if (musicSource != null)
        musicSource.volume = musicVolume;
}

Crossfading music between game states

To change music when the game state changes (exploring → combat), don’t call Play() with a new clip on the one music source. That cuts the old track off instantly. Use two music AudioSources instead: start the new clip on the idle source at volume 0, then fade one down and the other up over a second or two in a coroutine. An equal-power curve (Mathf.Cos / Mathf.Sin of t * Mathf.PI * 0.5f) avoids a loudness dip halfway through. Use Time.unscaledDeltaTime so the fade still finishes while the game is paused.

For changes that must land on the beat, schedule the new clip with AudioSource.PlayScheduled(AudioSettings.dspTime + …) on the next bar line. For mixes across many sources, use AudioMixer snapshots and AudioMixerSnapshot.TransitionTo(seconds). Code for all three, plus music layers (stems) and a checklist for when FMOD or Wwise is worth it, is in unity-adaptive-music-crossfade.


For a typical 2D game:

  • Music AudioSource — on the FeedbackManager or a dedicated MusicManager. Loop = true, Play On Awake = true optional. Volume set in script.
  • SFX AudioSource — same GameObject as above, or on the player. Used with PlayOneShot only. Clip field left empty.
  • Transient world sounds — handled by AudioSource.PlayClipAtPoint; no component required on pickups or enemies.

There is no need for a separate AudioSource on every pickup or hazard. Centralising sound playback in a FeedbackManager keeps scene setup simple and makes volume control straightforward.

Debugging audio setup

Most beginner audio failures are setup failures:

SymptomLikely causeFirst check
No sound at allMissing AudioListenerCheck the Main Camera has an AudioListener
Music does not startmusicSource or mainTheme is unassignedCheck the FeedbackManager Inspector
Coin sound cuts itself offUsing Play() for repeated SFXUse PlayOneShot
Sound plays at start before eventPlay On Awake enabledUntick it on SFX sources
Volume slider behaves strangelyValue not clampedUse Mathf.Clamp01 before assigning volume

Gotchas

  • AudioSource.Play() restarts the clip from the beginning each call. If a coin collection sound is called multiple times per second, only the last call will be audible. Use PlayOneShot for any effect that can trigger rapidly.
  • PlayClipAtPoint creates a temporary GameObject. It is self-cleaning, but generates a small allocation per call. Acceptable for occasional events; avoid using it every frame.
  • If no AudioListener exists in the scene (usually on the Main Camera), Unity logs a warning and no sound plays. Check the Camera has an AudioListener component.
  • AudioSource.PlayOneShot does not support volume scaling beyond the source’s own volume property. To play a specific clip at a different volume, pass the volume as the second argument: sfxSource.PlayOneShot(clip, 0.5f).

Worked example

The beginner 2D route keeps the sound on the coin itself, which shows the trade-off with the central setup recommended above:

  • CoinPickupFeedback.cs — on pickup, hides the sprite, plays a particle burst and the coin’s own AudioSource, then destroys the coin after 0.5 seconds. Because the AudioSource is destroyed with the coin, any clip longer than that delay is cut off; AudioSource.PlayClipAtPoint or a central SFX source avoids this.

Practice

Build a small AudioManager for a 2D scene:

  • a looping music track on one AudioSource, started from Start, with volume set by a [Range(0f, 1f)] field
  • a second AudioSource for sound effects, played only with PlayOneShot
  • Space plays a coin sound; M toggles global mute; Up/Down raise and lower music volume through a SetMusicVolume method that clamps its input

Success test:

  • the music loops and the SFX source does not play anything on start
  • tapping Space quickly layers the coin sound instead of restarting it
  • M silences music and effects together, and pressing it again restores them
  • holding Up never pushes the music volume above 1

Extension:

  • add a second music source and crossfade from an “explore” track to a “combat” track over two seconds using the approach in “Crossfading music between game states” and unity-adaptive-music-crossfade

Self-test

  1. Which method should play a coin sound that may fire several times a second, Play() or PlayOneShot? Why?
  2. How do you set up looping background music on an AudioSource?
  3. What does AudioSource.PlayClipAtPoint do, and what does it need on the calling object?
  4. How do you mute every sound in the game at once?
  5. A scene plays no sound at all. What is the first thing to check?
  6. Why use two music AudioSources to change track when the game state changes?

Answers

  1. PlayOneShot, because it layers clips without interrupting the one already playing; Play() restarts the clip, so only the last call is heard.
  2. Assign the clip, set loop = true and the volume, then call Play(), ideally on an AudioSource used only for music.
  3. It plays a clip at a world position by creating a temporary GameObject that cleans itself up; the caller needs no AudioSource.
  4. Set AudioListener.volume = 0f (and back to 1f to unmute).
  5. Whether the scene has an AudioListener, usually on the Main Camera.
  6. Calling Play() with a new clip on one source cuts the old track off instantly. With two sources, the new clip starts at volume 0 and one fades down while the other fades up.