# 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