SoundEmitter.cs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.Audio;
  5. using QFSW.MOP2;
  6. using UnityEngine.SceneManagement;
  7. using Sirenix.OdinInspector;
  8. namespace KairoEngine.SFX
  9. {
  10. // Todo: Remove playing SFX on scene change
  11. public class SoundEmitter : MonoBehaviour
  12. {
  13. public SphereCollider trigger;
  14. public AudioSource audioSource;
  15. [ReadOnly]
  16. public AudioClip audioClip;
  17. public SFXClip sfx;
  18. public string soundEmitterPrefabPool = "";
  19. private float fadeInTime;
  20. private float fadeOutTime;
  21. private Coroutine fadeOutCoroutine;
  22. private bool done = false;
  23. /// <summary>
  24. /// play a sound at a location in the world using an Audio Clip.
  25. /// </summary>
  26. /// <param name="audioClip">The audio clip to be played</param>
  27. /// <param name="location">The Vector3 position to play the sound in the world</param>
  28. /// <param name="volume">Audio clip volume</param>
  29. /// <param name="radius">Radius for SoundSensors</param>
  30. /// <param name="audioMixerGroup">Audio Mixer Channel</param>
  31. /// <param name="world">Trigger SoundSensors in world?</param>
  32. public void Initialize(AudioClip audioClip, Vector3 location, float volume, float radius, AudioMixerGroup audioMixerGroup, bool world = true)
  33. {
  34. this.audioClip = audioClip;
  35. this.trigger.radius = radius;
  36. transform.position = location;
  37. this.audioSource.volume = volume;
  38. this.audioSource.outputAudioMixerGroup = audioMixerGroup;
  39. this.audioSource.clip = this.audioClip;
  40. this.done = false;
  41. if(world == false)
  42. {
  43. trigger.enabled = false;
  44. }
  45. this.audioSource.Play();
  46. }
  47. /// <summary>
  48. /// Play a sound at a location in the world using a SFX Clip.
  49. /// </summary>
  50. /// <param name="sfx">SFX Clip with audio clip and parameters.</param>
  51. /// <param name="location">Vector3 representing the location to play the sound.</param>
  52. public void Initialize(SFXClip sfx, Vector3 location)
  53. {
  54. this.sfx = sfx;
  55. this.done = false;
  56. // Audio Clip
  57. if(sfx.sfxType == SFXClipType.Single)
  58. {
  59. // Single
  60. this.audioClip = sfx.clip;
  61. }
  62. else if(sfx.sfxType == SFXClipType.RandomList)
  63. {
  64. // Random from list
  65. this.audioClip = sfx.clips[Random.Range(0, sfx.clips.Count)];
  66. }
  67. this.audioSource.clip = this.audioClip;
  68. // Sound Location
  69. transform.position = location;
  70. // Audio Mixer Channel
  71. this.audioSource.outputAudioMixerGroup = sfx.audioMixerChannel;
  72. // Loop
  73. if(sfx.sfxType == SFXClipType.Single) this.audioSource.loop = sfx.loop;
  74. // Volume
  75. this.audioSource.volume = sfx.volume + Random.Range(-sfx.volumeVariation, sfx.volumeVariation);
  76. // Pitch
  77. this.audioSource.pitch = sfx.pitch + Random.Range(-sfx.pitchVariation, sfx.pitchVariation);
  78. // Spatial or stereo audio
  79. if(sfx.soundSpace != SFXSpace.Stereo)
  80. {
  81. this.audioSource.spatialize = true;
  82. }
  83. else
  84. {
  85. this.audioSource.spatialize = false;
  86. this.audioSource.spatialBlend = 0;
  87. }
  88. // Pan
  89. if(sfx.soundSpace != SFXSpace.GameWorld)
  90. {
  91. // Stereo audio Pan
  92. this.audioSource.panStereo = sfx.pan + Random.Range(-sfx.panVariation, sfx.panVariation);
  93. }
  94. else
  95. {
  96. // 3D audio has no Pan
  97. this.audioSource.panStereo = 0;
  98. }
  99. // Spatial Blend
  100. if(sfx.soundSpace == SFXSpace.Mix)
  101. {
  102. this.audioSource.spatialBlend = sfx.spatialBlend + Random.Range(-sfx.spatialBlendVariation, sfx.spatialBlendVariation);
  103. }
  104. // Set Distance
  105. if(sfx.soundSpace == SFXSpace.GameWorld || sfx.soundSpace == SFXSpace.Mix)
  106. {
  107. this.audioSource.minDistance = sfx.minDistance;
  108. this.audioSource.maxDistance = sfx.maxDistance;
  109. }
  110. // Use world sensor
  111. if(sfx.sensorDetection == true)
  112. {
  113. trigger.enabled = true;
  114. this.trigger.radius = sfx.soundRadius;
  115. }
  116. else trigger.enabled = true;
  117. // Set Fade In
  118. if(sfx.fadeIn)
  119. {
  120. this.audioSource.volume = 0f;
  121. this.fadeInTime = 0f;
  122. StartCoroutine(FadeIn());
  123. }
  124. // Set Fade out
  125. if(sfx.fadeOut)
  126. {
  127. fadeOutTime = 0f;
  128. fadeOutCoroutine = StartCoroutine(FadeOut());
  129. }
  130. // Set Remove on scene change
  131. // ! Turn this on when dependencies are solved
  132. //if(sfx.removeOnSceneChange) TransitionEvents.OnChangeScene += OnSceneChanged;
  133. // Trim and Play the audio clip
  134. if(sfx.trim) this.audioSource.time = sfx.trimStartDuration;
  135. this.audioSource.Play();
  136. if(sfx.trim) this.audioSource.SetScheduledEndTime(AudioSettings.dspTime +(sfx.trimEndDuration - sfx.trimStartDuration));
  137. }
  138. void Update()
  139. {
  140. if(done == true) return;
  141. // Destroy this Gameobject when the sound is done playing
  142. if(audioSource.isPlaying == false)
  143. {
  144. if(sfx == null) return;
  145. if(sfx.loop && sfx.sfxType == SFXClipType.RandomList)
  146. {
  147. Initialize(sfx, transform.position);
  148. }
  149. else
  150. {
  151. Remove();
  152. }
  153. }
  154. }
  155. private void Remove()
  156. {
  157. SoundController.soundEmitters.Remove(this);
  158. // ! Enable when dependencies are resolved
  159. //if(sfx.removeOnSceneChange) TransitionEvents.OnChangeScene -= OnSceneChanged;
  160. sfx = null;
  161. if(MasterObjectPooler.Instance != null && soundEmitterPrefabPool != "") MasterObjectPooler.Instance.GetPool(soundEmitterPrefabPool).Release(this.gameObject);
  162. else Destroy(this.gameObject);
  163. }
  164. IEnumerator FadeIn()
  165. {
  166. float start = Time.realtimeSinceStartup;
  167. while(Time.realtimeSinceStartup <= (start + (sfx.fadeInDuration)))
  168. {
  169. fadeInTime = (Time.realtimeSinceStartup - start);
  170. this.audioSource.volume = Mathf.Lerp(0, 1, fadeInTime/sfx.fadeInDuration);
  171. yield return null;
  172. }
  173. this.audioSource.volume = 1;
  174. }
  175. IEnumerator FadeOut(bool wait = true, bool remove = false, float time = 0)
  176. {
  177. fadeOutTime = 0f;
  178. if(time == 0) time = sfx.fadeOutDuration;
  179. if(wait) yield return new WaitForSecondsRealtime((this.audioSource.clip.length - this.audioSource.time) - time);
  180. if(sfx == null) yield return 0;
  181. float start = Time.realtimeSinceStartup;
  182. while(Time.realtimeSinceStartup < (start + (time)))
  183. {
  184. fadeOutTime = (Time.realtimeSinceStartup - start);
  185. this.audioSource.volume = Mathf.Lerp(1, 0, fadeOutTime/time);
  186. yield return null;
  187. }
  188. this.audioSource.volume = 0;
  189. if(remove) Remove();
  190. }
  191. // ! Enable when dependencies are solved
  192. /*
  193. private void OnSceneChanged(SceneChangeData sceneData, float time)
  194. {
  195. StopCoroutine(fadeOutCoroutine);
  196. done = true;
  197. if(sfx == null) return;
  198. if(sfx.fadeOut) StartCoroutine(FadeOut(false, true, time));
  199. else Remove();
  200. }
  201. */
  202. }
  203. }