# 1. Interactable Audio Patterns
Part of [[Interactable Audio]]. Next: [[2. Pooled AudioSource Service]].
> [!note] Verification status
> `PlayClipAtPoint(AudioClip, Vector3, float)` - reference: "creates an audio source but automatically disposes of it once the clip has finished playing" (no mixer-group parameter). `PlayOneShot(AudioClip, float volumeScale)`, `AudioSource.resource` (`AudioResource`) checked in 6000.5 on 2026-11-07. **Code not compiled in Unity.**
## 1.1 The options
| Pattern | How | Good | Bad |
|---|---|---|---|
| **A. AudioSource on every interactable** | `source.Play()` / `PlayOneShot` | Simple; easy 3D placement; source moves with the object | 500 doors = 500 sources (mostly idle, but each is a component to configure); settings drift between prefabs |
| **B. `PlayOneShot` on a shared source** | one source plays many clips | Overlaps freely; cheap | All one-shots share the source's position, group, pitch; can't stop one individually |
| **C. `AudioSource.PlayClipAtPoint`** | static, spawns a temp GameObject | One line | Creates/destroys a GameObject per call (GC/instantiation cost); no `AudioMixerGroup` parameter, so it isn't routed into your mixer; no pitch, priority or stop control |
| **D. Pooled emitters** (Part 2) | rent a pre-configured source, play, auto-return | Mixer routing, 3D at a point or following a transform, per-sound limits, stop/fade handles, zero runtime allocations after warm-up | More code (once) |
Recommendation: **A** for objects with continuous/looping sound tied to them (a humming generator, a door motor). **D** for everything event-based (impacts, pickups, button clicks, breakables). Avoid **C** once you have a mixer.
## 1.2 The InteractionSound asset
Interactables shouldn't know about clips, groups or randomization - they reference a *sound definition* (Type Object pattern, [[Game Programming Patterns (Review)]]):
```csharp
using UnityEngine;
using UnityEngine.Audio;
[CreateAssetMenu(menuName = "Audio/Interaction Sound", fileName = "Snd_")]
public class InteractionSound : ScriptableObject
{
[Header("Content (use one)")]
[Tooltip("An Audio Random Container or a single clip as an AudioResource.")]
public AudioResource resource;
[Tooltip("Code-randomized clip list (used if resource is empty).")]
public AudioClip[] clips;
[Header("Mix")]
public AudioMixerGroup output; // e.g. SFX/Interactables
[Range(0f, 1f)] public float volume = 1f;
public Vector2 volumeJitter = new Vector2(0.9f, 1f);
public Vector2 pitchRange = new Vector2(0.96f, 1.04f);
[Range(0, 256)] public int priority = 128;
[Header("3D")]
[Range(0f, 1f)] public float spatialBlend = 1f;
public float minDistance = 1f;
public float maxDistance = 25f;
public AudioRolloffMode rolloff = AudioRolloffMode.Logarithmic;
[Header("Limits")]
[Tooltip("Max simultaneous instances of THIS sound. Oldest is stolen when exceeded.")]
public int maxInstances = 4;
[Tooltip("Ignore requests closer together than this (seconds).")]
public float cooldown = 0.05f;
[Header("Loop")]
public bool loop;
// Runtime bookkeeping (not serialized)
[System.NonSerialized] public float lastPlayTime = -999f;
[System.NonSerialized] int _lastIndex = -1;
public AudioClip PickClip()
{
if (clips == null || clips.Length == 0) return null;
if (clips.Length == 1) return clips[0];
int i;
do { i = Random.Range(0, clips.Length); } while (i == _lastIndex);
_lastIndex = i;
return clips[i];
}
}
```
> [!warning] Code not compiled in Unity
> Not compiled. `NonSerialized` runtime fields on a ScriptableObject persist for the session (and in the Editor, across Play sessions until domain reload) - fine for cooldowns, but reset them in a bootstrap if you rely on exact values. See [[ScriptableObject Architecture]] for the runtime-state caveats.
## 1.3 Naming and organization
- `Snd_Door_Open`, `Snd_Door_Locked`, `Snd_Lever_Pull`, `Snd_Pickup_Coin`, `Snd_Impact_Wood_Light`, `Snd_Impact_Wood_Heavy`, `Snd_Hazard_Fan_Windup`.
- One folder per category; one Audio Random Container per sound that needs variation, referenced by the `resource` field.
- Route `output` to `SFX/Interactables` (most) or `UI` (2D UI-like clicks), so the player's SFX slider and the pause/underwater snapshots from [[2. Audio Mixer Snapshots and Exposed Parameters]] apply automatically.
## Docs
- `AudioSource.PlayClipAtPoint`: https://docs.unity3d.com/6000.5/Documentation/ScriptReference/AudioSource.PlayClipAtPoint.html
- `AudioSource.PlayOneShot`: https://docs.unity3d.com/6000.5/Documentation/ScriptReference/AudioSource.PlayOneShot.html
- `AudioResource`: https://docs.unity3d.com/6000.5/Documentation/ScriptReference/Audio.AudioResource.html
- `AudioRolloffMode`: https://docs.unity3d.com/6000.5/Documentation/ScriptReference/AudioRolloffMode.html
- Audio Random Container: https://docs.unity.com/en-us/engine/6000.5/manual/audio/random-container