using System.Collections; using UnityEngine; namespace Projectiles { [System.Serializable] public class AudioSetup { public AudioClip[] Clips; public float Volume = 1f; public float Pitch = 1f; public float MaxPitchChange; public float Delay; public bool Loop; public float FadeIn; public float FadeOut; [Space] public bool Repeat; public int RepeatPlayCount; public float RepeatDelay; public void CopyFrom(AudioSetup other) { Clips = other.Clips; Volume = other.Volume; MaxPitchChange = other.MaxPitchChange; Delay = other.Delay; Loop = other.Loop; FadeIn = other.FadeIn; FadeOut = other.FadeOut; Repeat = other.Repeat; RepeatPlayCount = other.RepeatPlayCount; RepeatDelay = other.RepeatDelay; } } public enum EForceBehaviour { None, ForceAny, ForceDifferentSetup, ForceSameSetup, } /// /// Utility that helps with playing gameplay sounds and effects. /// [RequireComponent(typeof(AudioSource))] public sealed class AudioEffect : MonoBehaviour { // PUBLIC MEMBERS public AudioSetup DefaultSetup => _defaultSetup; public AudioSetup CurrentSetup => _currentSetup; public AudioSource AudioSource => _audioSource; public bool IsPlaying => _audioSource.isPlaying == true || _delayedPlayRoutine != null; public int LastPlayedClipIndex { get; set; } // PRIVATE MEMBERS [SerializeField] private AudioSetup _defaultSetup; private AudioSource _audioSource; private bool _playOnAwake; private int _playCount; private Coroutine _delayedPlayRoutine; private AudioSetup _currentSetup; // PUBLIC METHODS public void Play(EForceBehaviour force = EForceBehaviour.None) { Play(_defaultSetup, force); } public void Play(AudioSetup setup, EForceBehaviour force = EForceBehaviour.None) { if (IsPlaying == true) { if (force == EForceBehaviour.None) return; if (force == EForceBehaviour.ForceDifferentSetup && setup == _currentSetup) return; if (force == EForceBehaviour.ForceSameSetup && setup != _currentSetup) return; } if (setup.Clips == null || setup.Clips.Length == 0) return; StartPlay(setup); } public void Stop(bool forceImmediateStop = false) { StopDelayedPlay(); if (forceImmediateStop == false && _currentSetup != null && _currentSetup.FadeOut > 0f) { _audioSource.FadeOut(this, _currentSetup.FadeOut); } else { _audioSource.Stop(); } } // MonoBehaviour INTERFACE private void Awake() { _audioSource = GetComponent(); _playOnAwake = _audioSource.playOnAwake; _audioSource.playOnAwake = false; _audioSource.Stop(); _defaultSetup.Loop |= _audioSource.loop; if (_defaultSetup.Clips.Length == 0 && _audioSource.clip != null) { _defaultSetup.Clips = new AudioClip[] { _audioSource.clip }; } } private void OnEnable() { _audioSource.enabled = true; if (_playOnAwake == true) { Play(); } } private void OnDisable() { StopDelayedPlay(); _audioSource.enabled = false; } // PRIVATE METHODS private void StartPlay(AudioSetup setup) { AudioSetup previousSetup = _currentSetup; _currentSetup = setup; LastPlayedClipIndex = NextClipIndex(setup); if (LastPlayedClipIndex < 0) return; if (_currentSetup.Clips[LastPlayedClipIndex] == null) return; // Do not start playing if there will be nothing to play StopDelayedPlay(); _playCount = 0; bool waitForFadeOut = IsPlaying == true && previousSetup != null && previousSetup.FadeOut > 0.01f; if (_currentSetup.Delay < 0.01f && waitForFadeOut == false) { PlayClip(LastPlayedClipIndex); } else { float delay = _currentSetup.Delay; if (waitForFadeOut == true) { delay += previousSetup.FadeOut; _audioSource.FadeOut(this, previousSetup.FadeOut); } _delayedPlayRoutine = StartCoroutine(PlayDelayed_Coroutine(delay, LastPlayedClipIndex)); } } private void PlayClip(int clipIndex) { _audioSource.Stop(); StopAllCoroutines(); // Stop audiosource fadings LastPlayedClipIndex = clipIndex; _audioSource.clip = _currentSetup.Clips[clipIndex]; _audioSource.volume = _currentSetup.Volume; _audioSource.loop = _currentSetup.Loop; _audioSource.pitch = _currentSetup.Pitch + Random.Range(-_currentSetup.MaxPitchChange, _currentSetup.MaxPitchChange); if (_currentSetup.FadeIn > 0f) { _audioSource.FadeIn(this, _currentSetup.FadeIn, volume: _currentSetup.Volume); } else { _audioSource.Play(); } _playCount++; if (_currentSetup.Repeat == true && _playCount < _currentSetup.RepeatPlayCount) { _delayedPlayRoutine = StartCoroutine(PlayDelayed_Coroutine(_audioSource.clip.length + _currentSetup.RepeatDelay, clipIndex)); } } private IEnumerator PlayDelayed_Coroutine(float delay, int clipIndex) { if (delay > 0.01f) { yield return new WaitForSeconds(delay); } _delayedPlayRoutine = null; PlayClip(clipIndex); } private void StopDelayedPlay() { if (_delayedPlayRoutine != null) { StopCoroutine(_delayedPlayRoutine); _delayedPlayRoutine = null; } } private int NextClipIndex(AudioSetup setup) { if (setup.Clips.Length == 0) { Debug.LogWarningFormat("Cannot play sound on {0} - missing audio clip", gameObject.name); return -1; } int clipIndex = Random.Range(0, setup.Clips.Length); if (clipIndex == LastPlayedClipIndex) { clipIndex = (clipIndex + 1) % setup.Clips.Length; } return clipIndex; } } }