using System; using System.Collections; using System.Collections.Generic; using UnityEditor; using UnityEngine; namespace Gaia { [ExecuteInEditMode] [Serializable] public class GaiaAudioManager : MonoBehaviour { /// /// Master volume of everything /// public float m_masterVolume = 1f; /// /// Volume for weather system. /// public float m_rainVolume = .5f; public float m_snowVolume = .5f; public float m_thunderVolume = .5f; /// /// Target player /// public GameObject m_player; /// /// Active audio zones that should be in process of playing something /// [NonSerialized] public List m_activeAudioZones = new List(); /// /// Audiozones slated for deactivation /// [NonSerialized] public List m_becomingInActiveAudioZones = new List(); /// /// Currently inactive audiozones /// [NonSerialized] public List m_inactiveAudioZones = new List(); /// /// Show zone gizmos /// public bool m_showGizmos = false; /// /// Single instance of audio manager in the scene /// private static GaiaAudioManager m_instance = null; /// /// Current audio manager instance /// public static GaiaAudioManager Instance { get { return m_instance; } } /// /// When scene loads set up our singleton instance /// private void Awake() { if (m_instance == null) { m_instance = this; } } /// /// Set up our singleton instance /// private void OnEnable() { if (m_instance == null) { m_instance = this; } } /// /// Start up our audio zone state change management system when scene starts playing /// private void Start() { StartCoroutine("UpdateAudioZoneStates"); } /// /// Process active audio zone updates /// private void Update() { //Only valid in play mode if (!Application.isPlaying) { return; } //Only valid if we have a player if (m_player == null) { return; } // Process active zones int zonesUpdated = 0; for (int currentZone = 0; currentZone < m_activeAudioZones.Count; currentZone++) { GaiaAudioZone az = m_activeAudioZones[currentZone]; if (az != null) { az.ProcessActiveUpdate(m_masterVolume, m_showGizmos); zonesUpdated++; } } #if UNITY_EDITOR if (zonesUpdated > 0) { if (Selection.activeGameObject == this.gameObject) { EditorUtility.SetDirty(this); } } #endif } /// /// Set the gizmo state on the audio zones - would only ever be called in editor so it can be less efficient /// public void UpdateSceneGizmos() { GaiaAudioZone[] audioZones = GameObject.FindObjectsOfType(); foreach(GaiaAudioZone audioZone in audioZones) { if (audioZone != null) { audioZone.m_showGizmos = m_showGizmos; } } } /// /// Register an audiozone. Always added to inactive queue first. /// /// Audiozone to register public void RegisterAudioZone(GaiaAudioZone az) { m_activeAudioZones.Remove(az); m_becomingInActiveAudioZones.Remove(az); if (!m_inactiveAudioZones.Contains(az)) { m_inactiveAudioZones.Add(az); } } /// /// DeRegister an audiozone. /// /// Audiozone to deregister public void DeRegisterAudioZone(GaiaAudioZone az) { //DeRegister if not there already az.Stop(); m_inactiveAudioZones.Remove(az); m_activeAudioZones.Remove(az); m_becomingInActiveAudioZones.Remove(az); } /// /// Activate an audiozone. Will check so that a zone is not activated twice /// /// Audiozone to activate protected void ActivateAudioZone(GaiaAudioZone az) { if (!m_activeAudioZones.Contains(az)) { az.Activate(); m_activeAudioZones.Add(az); m_becomingInActiveAudioZones.Remove(az); m_inactiveAudioZones.Remove(az); } } /// /// Signal an audiozone for deactivation /// /// Audiozone to deactivate protected void FlagAudioZoneForDeactivation(GaiaAudioZone az) { if (!m_becomingInActiveAudioZones.Contains(az)) { az.FlagForDeactivation(); m_becomingInActiveAudioZones.Add(az); m_activeAudioZones.Remove(az); m_inactiveAudioZones.Remove(az); } } /// /// Queue an audiozone for deactivation at the end of its current loop /// /// Audiozone to activate protected void DeActivateAudioZone(GaiaAudioZone az) { if (!m_inactiveAudioZones.Contains(az)) { az.Deactivate(); m_inactiveAudioZones.Add(az); m_activeAudioZones.Remove(az); m_becomingInActiveAudioZones.Remove(az); } } /// /// Process state changes - relatively expensive so it is limited to periodic co-routine /// private IEnumerator UpdateAudioZoneStates() { int maxUpdatesPerIteration = 20; while (true) { //Only valid if we have a player if (m_player != null) { int currentZone = 0; int currentIteration = 0; Vector3 playerLocation = m_player.transform.position; //Process active zones currentZone = 0; while (currentZone < m_activeAudioZones.Count) { GaiaAudioZone zone = m_activeAudioZones[currentZone]; //Handle deleted / null zones if (zone == null) { m_activeAudioZones.RemoveAt(currentZone); } //Check / update zone state else { //Always update its gizmos zone.m_showGizmos = m_showGizmos; //Are we not in range - then flag it for deactivation - results in removal from list if (!zone.InRange(playerLocation)) { FlagAudioZoneForDeactivation(zone); } else { //Do nothing and step to next currentZone++; } //Take a little break if necessary currentIteration++; if (currentIteration > maxUpdatesPerIteration) { currentIteration = 0; yield return new WaitForSeconds(0.2f); } } } //Process zones flagged for deactivation currentZone = 0; while (currentZone < m_becomingInActiveAudioZones.Count) { GaiaAudioZone zone = m_becomingInActiveAudioZones[currentZone]; //Handle deleted / null zones if (zone == null) { m_becomingInActiveAudioZones.RemoveAt(currentZone); } //Check / update zone state else { //Always update its gizmos zone.m_showGizmos = m_showGizmos; //Are we in range - then make it active - results in removal from list if (zone.InRange(playerLocation)) { ActivateAudioZone(zone); } else { //See if it can deactivate - results in removal from list if (zone.CanDeactivateNow()) { DeActivateAudioZone(zone); } //Do nothing and step to next else { currentZone++; } } //Take a little break if necessary currentIteration++; if (currentIteration > maxUpdatesPerIteration) { currentIteration = 0; yield return new WaitForSeconds(0.2f); } } } //Process inactive zones currentZone = 0; while (currentZone < m_inactiveAudioZones.Count) { GaiaAudioZone zone = m_inactiveAudioZones[currentZone]; //Handle deleted / null zones if (zone == null) { m_inactiveAudioZones.RemoveAt(currentZone); } //Check / update zone state else { //Always update its gizmos zone.m_showGizmos = m_showGizmos; //Are we in range - then make it active - results in removal from list if (zone.InRange(playerLocation)) { ActivateAudioZone(zone); } else { currentZone++; } //Take a little break if necessary currentIteration++; if (currentIteration > maxUpdatesPerIteration) { currentIteration = 0; yield return new WaitForSeconds(0.2f); } } } } //Always take a break at the end of each major loop yield return new WaitForSeconds(0.2f); } } } }