The Unity script needs the Editor and the setup listed below. The separate C# companion practice runs in SharpRune without UnityEngine.
Decide who owns a cue after the triggering object changes
A short sound often outlives the event that requested it. A pickup can disappear immediately, while its audio should continue on an owner that remains active. This example therefore starts with a dedicated AudioHost instead of attaching playback to a disposable pickup. That choice is a lifetime contract, not an audio-quality optimization. PlayOneShot permits overlapping playback on an AudioSource, so several quick requests may overlap rather than replace one another. A production design may need a limit, priorities or pooled emitters, but first establish one valid route from a configured clip through the source, mixer and listener to audible output.
Read the checks without assuming they configure every audio asset
Awake caches the required source and sets its intended loop and autoplay fields. The Inspector must already have Play On Awake unchecked before entering Play for this baseline; assigning the property in Awake is not offered as a guarantee that previously triggered autoplay never began. PlayCue requires Play mode, an active enabled controller, an assigned clip and an active source before calling PlayOneShot. Clamp01 keeps this tutorial’s volume scale inside its chosen range. OnDisable deliberately calls Stop because the source is dedicated to this controller. Reusing that source for unrelated music would make the same cleanup policy inappropriate.
[ContextMenu("Play Cue (Play Mode)")]
public void PlayCue()
{
if (!Application.isPlaying || !isActiveAndEnabled) return;
if (cue == null || source == null || !source.isActiveAndEnabled)
{ Debug.LogError("Assign a cue and enable this dedicated AudioSource.", this); return; }
source.PlayOneShot(cue, Mathf.Clamp01(volumeScale));
}Use interval arithmetic as a budget illustration, not an audio measurement
The portable data describes one cue beginning at time zero and another at one tenth, each lasting half a time unit. At two tenths both supplied intervals are active. At fifty-five hundredths only the later interval remains, and at one both have ended. Those counts are useful for reasoning about overlap or testing an application budget. They are not measurements of voice virtualization, DSP buffers, clip import latency, speaker output or the engine’s playback flags. The observations are supplied and deterministic. A real audio profile must identify its platform, listener, mixer routing and capture method rather than borrowing certainty from these three arithmetic results.
Audibility requires more than a successful method call
A missing clip, a muted Editor, zero source volume, mixer attenuation, a disabled listener or inappropriate spatial distance can all make a request inaudible. Start with Spatial Blend zero and one intended listener to avoid confusing basic routing with three-dimensional attenuation. Repeated one-shots can raise combined output level even when each scale is below one, so a local clamp is not a complete mixing or clipping guarantee. Deactivating or destroying the audio owner is also different from ending a gameplay event. Keep the owner alive or transfer playback deliberately; do not estimate cleanup from clip.length while ignoring pitch, routing and playback policy.
Practice: separate overlap from source lifetime
Use a short recognisable clip and trigger two requests close together while the stable AudioHost stays active. Confirm whether the expected overlap is audible, then disable this controller in a separate run to observe its explicit Stop policy. Next repeat using a disposable object only as the requester while the AudioHost remains the playback owner. This isolates the difference between the object that raises an event and the object that must survive to produce sound. Record the source, clip, listener and routing used for each observation. A portable overlap count cannot substitute for hearing and measuring the actual configured audio path.
- Hint: uncheck Play On Awake in the Inspector before Play so the first sound is a deliberate request.
- Hint: test source shutdown independently from repeated requests.
Practice: define an overlap limit and its rejection policy
Extend the supplied interval ledger with a maximum number of active cues. Decide whether a new request is rejected, replaces the oldest or replaces a lower-priority cue, and write expected outcomes at the exact ending boundary. Use start-inclusive and end-exclusive intervals so a cue ending at the request time does not occupy a slot. Validate negative durations and nonfinite clock values if the helper becomes reusable. This exercise establishes application scheduling rules only. The Unity adapter would still need to apply that decision to actual playback handles or dedicated emitters and handle disabled owners without assuming one list entry always corresponds to one audible hardware voice.
- Hint: state the time interval convention before checking simultaneous requests.
- Hint: a budget policy chooses requests; Unity and the audio device still own actual playback.
Make the sound experiment reproducible
The lesson’s expected Editor behavior is deliberately modest: a configured short clip plays through one active dedicated source, repeated requests can overlap, and disabling this controller stops that source. The first two claims need a real audible scene observation; only the independent interval calculations were executed locally. Engine API choices were reviewed against pinned Unity6000.0 references and the component was syntax-checked, with no Editor installation or Player audio test claimed. Keep that evidence boundary visible when adding spatial emitters, mixer groups, voice priorities or pooled audio hosts, because those production features introduce additional setup and lifetime decisions.
Unity Editor example
Set up in Unity 6
- Import a short audible audio file as an AudioClip. Create a stable active AudioHost object with AudioSource and OneShotCueProbe.cs; assign that clip.
- Before entering Play, explicitly uncheck Play On Awake on the dedicated AudioSource in the Inspector; the Awake assignment is not offered as a guarantee that already-started autoplay was prevented. Set Spatial Blend to 0, source Volume above zero, and inspect mixer routing.
- Ensure the scene has exactly the intended enabled AudioListener, normally on the active Camera, and that Editor audio is not muted. Enter Play mode and choose Play Cue.
- Invoke Play Cue twice quickly to listen for overlap. Do not immediately deactivate AudioHost; then disable the component separately to test its explicit Stop policy.
- For positional audio, change Spatial Blend and distance attenuation in a separate experiment rather than blaming an inaudible 3D source on the C# call alone.
OneShotCueProbe.cs
Download Unity script ↗using UnityEngine;
[RequireComponent(typeof(AudioSource))]
public sealed class OneShotCueProbe : MonoBehaviour
{
[SerializeField] private AudioClip cue;
[SerializeField, Range(0f, 1f)] private float volumeScale = 0.7f;
private AudioSource source;
private void Awake()
{
source = GetComponent<AudioSource>();
source.playOnAwake = false;
source.loop = false;
}
[ContextMenu("Play Cue (Play Mode)")]
public void PlayCue()
{
if (!Application.isPlaying || !isActiveAndEnabled) return;
if (cue == null || source == null || !source.isActiveAndEnabled)
{ Debug.LogError("Assign a cue and enable this dedicated AudioSource.", this); return; }
source.PlayOneShot(cue, Mathf.Clamp01(volumeScale));
}
private void OnDisable() { if (source != null) source.Stop(); }
}
What to observe in Unity
The assigned cue plays through the dedicated active source and may overlap repeated one-shots. Disabling this controller stops its source by policy; actual audibility requires the listener and audio route setup.
The console practice below isolates the C# logic. It does not run a Unity scene or emulate Unity engine callbacks.
C# companion practice
using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
var suppliedCues = new[] { (Start: 0m, End: 0.5m), (Start: 0.1m, End: 0.6m) };
int ActiveAt(decimal time) => suppliedCues.Count(cue => cue.Start <= time && time < cue.End);
Console.WriteLine(ActiveAt(0.2m));
Console.WriteLine(ActiveAt(0.55m));
Console.WriteLine(ActiveAt(1m));
C# practice output
2 1 0
Keep the audio owner alive; PlayOneShot is not a substitute for a lifetime policy.