Advertisement
Game Audio

Unity Heal SFX Setup: AudioSource, Pitch Randomization, and Trigger Timing for Health Pickups

AudioSource settings, pitch randomisation and trigger timing for health pickups in Unity: what to set, what to leave alone, and how to stop a heal from firing twice.

A player crosses a medkit at 18 HP. The heal cue starts, the pickup disappears, and the sound stops about 40 ms in. The health bar fills, so the feature works, and the cue is still broken. There is no console error, because nothing failed in the ordinary sense: the AudioSource was a child of the pickup, and the pickup was destroyed.

Most heal-sound bugs reported as a sound problem are really scene wiring: a default left on, a trigger that fires twice, an object destroyed before its clip ends.

Set Up the AudioSource Before You Touch the Clip

The AudioSource defaults are tuned for ambient emitters that live in the scene for its whole lifetime. A one-shot pickup cue wants almost the opposite, so change these before you audition anything.

  • playOnAwake: off. A pooled pickup that wakes with the scene plays its heal cue during the loading fade, at the wrong position, for no reason.
  • loop: off. Obvious, and still the most common reason a pickup buzzes for ten seconds after a respawn.
  • dopplerLevel: 0. If the player crosses the pickup at sprint speed, a non-zero level bends the pitch downward mid-cue, and a heal that sags sounds like a failure rather than a gain.
  • priority: leave it at 128 unless you have a reason. Lower numbers win when the voice limit is hit. A heal that vanishes mid-boss-fight is usually a priority problem, not a volume problem.
  • spatialBlend: 1 for world pickups, 0 for interface heals. There is no correct middle value here; the cue either belongs to a place in the world or it does not.
  • rolloff and maxDistance: logarithmic, roughly 12 to 15 metres. Past that range the player cannot see the item anyway.
  • outputAudioMixerGroup: assign one. An unassigned source goes straight to the master bus and ignores every duck, snapshot and reverb zone you set up later.

World Pickups Versus Interface Heals

A pickup on the floor is a point in space: spatialBlend 1, and let the listener do the work. A heal triggered from a menu or a consumable slot is a UI event: spatialBlend 0, and switch bypassReverbZones on so the cue does not inherit the dampening of whatever room the camera stands in. Mixing the two conventions produces heals that are loud in corridors and inaudible in halls. How a one-shot source sits inside a spatial mix is covered in 3D positional audio in Unity.

PlayOneShot or Play, and What Each Does to Pitch

PlayOneShot layers an extra voice on the same source: source pitch, mixer routing and spatial settings still apply, and the volume-scale argument multiplies the source volume. Call it twice inside one frame and you get two copies of the same clip, same pitch, starting within a few samples of each other, which is one cue with a comb filter on it and worse than either copy alone.

Play retriggers the source and cuts the instance already playing. For a pickup that can only be collected once, that behaviour is acceptable and slightly cheaper. For anything that can overlap, it is wrong, because the second heal deletes the tail of the first.

Neither call gives per-shot pitch. pitch belongs to the AudioSource, not the voice, so two overlapping heals at different pitches need two sources, which is the argument for a small pool and the reason variation systems for repeated cues tend to own their playback component.

A Pitch Range Small Enough to Stay in Key

Randomisation on a heal does one job: stopping the tenth pickup sounding identical to the first. It is not there to be noticed. Pitch factor converts to cents as 1200 × log2(factor), so the practical ranges look like this.

Pitch factor Deviation Where it works
0.98–1.02 about ±34 cents Tonal scores, healing that repeats often
0.95–1.05 about ±85 cents Loose mixes, arcade and roguelike palettes
0.90–1.10 about ±165 cents Rarely; more than a semitone and a half

Against music, ±34 cents is usually enough: audible as variation in a quiet room, gone under combat, which is the point. Past ±85 cents the cue starts to read as a different note, and above a semitone it may clash with the key the score is in.

Pitch is also a playback rate: at 1.02 a 300 ms cue runs about 294 ms, at 0.95 roughly 316 ms. That matters when the destroy timer is measured from clip length.

The Cooldown Guard, and the Two-Collider Double Fire

The classic double heal: the pickup root carries a BoxCollider, a child carries a larger SphereCollider for a magnet radius, and the player has a CharacterController plus a child hitbox. Every overlapping collider pair raises its own OnTriggerEnter, so two entries in one frame give two cues and 50 HP from a 25 HP item.

Destroy does not save you here. Destruction is deferred to the end of the frame, so a second trigger can arrive before the object is gone. The guard has to be a field you set synchronously, before anything else in the method runs.

public class HealthPickup : MonoBehaviour
{
    [SerializeField] AudioClip healClip;
    [SerializeField] AudioSource source;
    [SerializeField] int healAmount = 25;

    bool consumed;
    float ignoreUntil;

    void OnTriggerEnter(Collider other)
    {
        if (consumed || Time.time < ignoreUntil) return;
        if (!other.CompareTag("Player")) return;
        if (!other.TryGetComponent(out Health health)) return;

        consumed = true;
        ignoreUntil = Time.time + 0.25f;
        GetComponent<Collider>().enabled = false;
        GetComponent<MeshRenderer>().enabled = false;

        health.Heal(healAmount);

        source.pitch = Random.Range(0.98f, 1.02f);
        source.PlayOneShot(healClip, 1f);

        Destroy(gameObject, healClip.length / source.pitch);
    }
}

The order of those lines is the design. The flag goes first, so a second trigger in the same frame exits immediately. The collider goes next, so physics stops generating events before the object dies. The renderer goes off so the pickup looks collected the frame it is heard.

The destroy delay is the fix for the opening failure: dividing clip length by pitch keeps the object alive for the full randomised playback, not the nominal one. Keep the guard on one authoritative collider; a magnet radius is better done as a distance check than as a second trigger volume, since a second volume is what created the double fire.

Do Not Let the Clip Die With the Pickup

Any AudioSource parented to the pickup is destroyed with it. If the cue is longer than the object has left to live, the cue is cut, and no mixer setting will bring it back.

AudioSource.PlayClipAtPoint spawns a throwaway object at a world position and destroys it after the clip ends. Fine for a prototype, limiting past it: it takes a volume scale but no pitch, no mixer group, no priority, so the cue bypasses ducking and cannot be randomised. Once a project has a mixer, route one-shots through a persistent AudioSource on an audio manager, positioned with transform.position before the call.

The order for a consumable pickup is: set the flag, disable the collider, hide the renderer, apply the heal, play the cue, destroy on a delay. Destroying first and playing second produces the 40 ms heal sound, and it is easy to introduce later when someone refactors for pooling.

Pooling, dspTime and Ducking

Four to eight pooled AudioSources on an audio manager cover most pickup traffic. Round-robin the index, set pitch per shot, and never let two voices start on the same frame at the same pitch. If a room drops five heals at once, this is the difference between a shimmer and a flam.

For timing, AudioSettings.dspTime is the clock the audio thread uses, advancing in buffer-sized steps. At 48 kHz with a 1024-sample buffer that step is about 21 ms, longer than a frame on a fast machine. When a heal must line up with an animation event, or three pickups should land in order, schedule ahead:

source.PlayScheduled(AudioSettings.dspTime + 0.02);

Unity has no built-in sidechain compressor, so a heal that should push the music down needs a mixer snapshot or a short volume curve on the music group. A typical shape: minus 4 to 6 dB over 60 to 80 ms, held 250 to 300 ms, released over 400 to 600 ms. A 100 ms release on a cue repeating every two seconds turns the music into a tremolo.

None of this changes the sound itself, and that boundary is worth stating. Wiring decides whether the cue is heard, how often, at what pitch. What the cue says is a design question, covered in health pickup cue design. Get the trigger and the AudioSource right first: a well-designed cue that fires twice and gets cut at 40 ms is judged a bad sound every time.

Advertisement