SHARE
TWEET

Noisy.cs

mvaganov Feb 8th, 2018 (edited) 306 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4.  
  5. // author: mvaganov@hotmail.com
  6. // license: Copyfree, public domain.
  7. // latest version at: https://pastebin.com/raw/hGU8et8s -- added: bgmusic fix, trigger functions (2019/05/07)
  8. public class Noisy : MonoBehaviour {
  9.  
  10.     public Noise[] noises = new Noise[1];
  11.  
  12.     public static int RandomNumberThatIsnt(int minInclusive, int maxExclusive, int andNot = -1) {
  13.         int index = minInclusive;
  14.         if (maxExclusive - minInclusive > 1) {
  15.             if (andNot >= minInclusive) {
  16.                 index = Random.Range (minInclusive, maxExclusive-1);
  17.                 if (index >= andNot) {
  18.                     index++;
  19.                 }
  20.             }
  21.             else {
  22.                 index = Random.Range (minInclusive, maxExclusive);
  23.             }
  24.         }
  25.         return index;
  26.     }
  27.  
  28.     [System.Serializable]
  29.     public class Noise {
  30.         [Tooltip("you can reference the 'sounds' list of another Noise by using it's name and leaving this Noise's 'sounds' as length zero")]
  31.         public string name;
  32.         [Tooltip("any one of these sounds count as the-sound-to-play (will be randomized at runtime)")]
  33.         public AudioClip[] sounds = new AudioClip[1];
  34.         [Range(0,1),Tooltip("to keep max volume, leave this at 0 (changes during editor runtime work thanks to Noisy.OnValidate())")]
  35.         public float volumeReduce = 0f;
  36.         [Tooltip("set the background music to this? (if this is set, none of the other checkboxes below matter)")]
  37.         public bool backgroundMusic = false;
  38.         [ContextMenuItem("advanced keypress settings... (create component)", "CreateOnKeyPress"), Tooltip("play as soon as object starts? (eg: ambient sound/music or instantiated objects with 'birth' sounds)")]
  39.         public bool playOnStart = false;
  40.         [Tooltip("play at max volume and consistent pitch, regardless of distance? (eg: volume changes by distance, doppler effect)")]
  41.         public bool is2D = false;
  42.         [Tooltip("sound should attach to this object and follow it as it moves? (eg: ambient sound/music/dialog that follows a moving agent)")]
  43.         public bool followsObject = false;
  44.         [Tooltip("start the sound again right after it ends? (eg: ambient sound/music)")]
  45.         public bool loop = false;
  46.         [Tooltip("stop the previous-Noisy-with-the-same-Name before playing another one? (eg: character dialog, UI feedback sounds, background music)")]
  47.         public bool onePlayAtATime = false;
  48.         [ContextMenuItem("advanced collision settings... (create component)", "CreateOnCollision"), Tooltip("play when this object collides with something? (eg: adding audio output to Rigidbody collision)")]
  49.         public bool onCollision = false;
  50.         [ContextMenuItem("advanced trigger settings... (create component)", "CreateOnTrigger"), Tooltip("play when an object enters this trigger? (eg: ambient noise, reactions to movement through space)")]
  51.         public bool onTrigger = false;
  52.         [HideInInspector]
  53.         /// The last noise played, used to prevent duplicate repetition
  54.         public int lastNoisePlayed = -1;
  55.         [HideInInspector]
  56.         public AudioSource activeAudioSource;
  57.  
  58.         /// Plays the sound. Cannot have the sound follow the object because a position is given, not a transform
  59.         /// <returns>The sound.</returns>
  60.         /// <param name="p">where the noise is</param>
  61.         public AudioSource PlaySound(Vector3 p) {
  62.             if(!backgroundMusic) {
  63.                 activeAudioSource = Noisy.PlaySound(GetSoundToPlay(), p, !is2D, loop, onePlayAtATime ? name : null, 1 - volumeReduce);
  64.             } else {
  65.                 activeAudioSource = Noisy.PlayBackgroundMusic(GetSoundToPlay(), 1 - volumeReduce);
  66.             }
  67.             return activeAudioSource;
  68.         }
  69.  
  70.         public AudioSource PlaySound(Transform t) {
  71.             activeAudioSource = PlaySound (t.position);
  72.             if(followsObject) { activeAudioSource.transform.SetParent (t); }
  73.             return activeAudioSource;
  74.         }
  75.  
  76.         public AudioClip GetSoundToPlay() { return GetSoundToPlay (ref lastNoisePlayed); }
  77.  
  78.         public AudioClip GetSoundToPlay(ref int indexNotToPlayNext) {
  79.             if (sounds != null && sounds.Length > 0) {
  80.                 indexNotToPlayNext = RandomNumberThatIsnt(0, sounds.Length, indexNotToPlayNext);
  81.                 return sounds [indexNotToPlayNext];
  82.             }
  83.             return null;
  84.         }
  85.  
  86.         /// <summary>comparer, used to sort Noise objects into the list</summary>
  87.         public class Comparer : IComparer<Noise> {
  88.             public int Compare(Noise x, Noise y) { return x.name.CompareTo(y.name); }
  89.         }
  90.         public static Comparer compare = new Comparer();
  91.     }
  92.  
  93.     void Awake() {
  94.         // sort noises for faster access later
  95.         System.Array.Sort(noises, Noise.compare);
  96.         // add all named noises to a single static (global) listing, for easy scripted access later
  97.         for (int i = 0; i < noises.Length; ++i) {
  98.             if (noises [i].name != null && noises [i].name.Length > 0) {
  99.                 int index = Global.allNoises.BinarySearch (noises [i], Noise.compare);
  100.                 bool isAlreadyKnown = index >= 0;
  101.                 bool hasSoundsFilledOut = noises [i].sounds != null && noises [i].sounds.Length > 0;
  102.                 if (!isAlreadyKnown && hasSoundsFilledOut) {
  103.                     Global.allNoises.Insert (~index, noises [i]);
  104.                 }
  105.             }
  106.         }
  107.     }
  108.  
  109.     /// plays the first sound in the noises list
  110.     public void DoActivateTrigger() {
  111.         if(noises.Length > 0 && noises[0] != null) { noises[0].PlaySound(transform.position); }
  112.     }
  113.  
  114.     /// plays the first sound in the noises list
  115.     public void DoDeactivateTrigger() {
  116.         if(noises.Length > 0 && noises[0] != null && noises[0].activeAudioSource != null) {
  117.             noises[0].activeAudioSource.Stop();
  118.         }
  119.     }
  120.  
  121.     /// returns the Noise that was created someplace in the scene with the given name
  122.     /// <returns>The sound.</returns>
  123.     /// <param name="name">Name.</param>
  124.     public static Noise GetSound(string name) {
  125.         searched.name = name;
  126.         int i = Global.allNoises.BinarySearch (searched, Noise.compare);
  127.         if (i >= 0) { return Global.allNoises [i]; }
  128.         return null;
  129.     }
  130.  
  131.     void Start() {
  132.         Noise n;
  133.         for (int i = 0; i < noises.Length; ++i) {
  134.             n = noises [i];
  135.             // use the global noise catalog if this Noisy hasn't filled in it's named noise.
  136.             if (n.sounds == null || n.sounds.Length == 0) {
  137.                 Noise existing = GetSound(n.name);
  138.                 if(existing != null) { n.sounds = existing.sounds; }
  139.             }
  140.             if (n.playOnStart || n.backgroundMusic) {
  141.                 n.PlaySound (transform.position);
  142.             }
  143.             if (n.onCollision) {
  144.                 Noisy.OnCollisionAdvancedSettings oc = CreateHandler<OnCollisionAdvancedSettings> (n.name);
  145.                 oc.noise = n;
  146.             }
  147.             if (n.onTrigger) {
  148.                 Noisy.OnTriggerAdvancedSettings oc = CreateHandler<OnTriggerAdvancedSettings> (n.name);
  149.                 oc.noise = n;
  150.             }
  151.         }
  152.     }
  153.  
  154.     private static Noise searched = new Noise();
  155.     /// Plays the named sound (as a 2D sound, full volume)
  156.     public static AudioSource PlaySound(string name) {
  157.         Noise n = GetSound(name);
  158.         return (n != null) ? Noisy.PlaySound (n.GetSoundToPlay()) : null;
  159.     }
  160.  
  161.     public static AudioSource PlaySound(string name, Vector3 p) {
  162.         Noise n = GetSound(name);
  163.         return (n != null) ? n.PlaySound (p) : null;
  164.     }
  165.  
  166.     public static AudioSource PlaySound(string name, Transform t) {
  167.         Noise n = GetSound(name);
  168.         return (n != null) ? n.PlaySound (t) : null;
  169.     }
  170.  
  171.     private static Dictionary<string, AudioSource> s_soundsByCategory = new Dictionary<string, AudioSource>();
  172.     /// Plays the sound.
  173.     /// <returns>Component where the sound is playing from.</returns>
  174.     /// <param name="noise">Noise. returns early if <c>null</c></param>
  175.     /// <param name="p">P. the location to play from. If is3D is false, this parameter is pretty useless.</param>
  176.     /// <param name="is3D">If set to false, sound plays without considering 3D-ness (full volume from anywhere).</param>
  177.     /// <param name="isLooped">If set to <c>true</c> is looped.</param>
  178.     /// <param name="soundCategory">If non-null, prevents multiple sounds with the same soundCategory from playing simultaneously. If null, each instance of the sound will be independent.</param>
  179.     /// <param name="volume"></param>
  180.     public static AudioSource PlaySound(AudioClip noise,  Vector3 p = default(Vector3), bool is3D = false, bool isLooped = false, string soundCategory = null, float volume = 1f) {
  181.         if (noise == null) return null;
  182.         AudioSource asrc = null;
  183.         if (soundCategory != null && soundCategory.Length > 0) {
  184.             s_soundsByCategory.TryGetValue(soundCategory, out asrc);
  185.         }
  186.         if (asrc == null) {
  187.             string noiseName = (soundCategory != null) ? "("+soundCategory+")" : "<Noise: "+noise.name+">";
  188.             GameObject go = new GameObject (noiseName);
  189.             asrc = go.AddComponent<AudioSource> ();
  190.             if (soundCategory != null) {
  191.                 s_soundsByCategory[soundCategory] = asrc;
  192.             }
  193.             asrc.transform.SetParent (Global.Instance ().transform);
  194.         }
  195.         else {
  196.             asrc.Stop ();
  197.         }
  198.         asrc.clip = noise;
  199.         asrc.spatialBlend = is3D ? 1 : 0;
  200.         asrc.transform.position = p;
  201.         if (soundCategory == null && !isLooped) {
  202.             Destroy (asrc.gameObject, noise.length); // destroy the noise after it is done playing if not looped
  203.         }
  204.         asrc.loop = isLooped;
  205.         if(volume != asrc.volume) { asrc.volume = volume; }
  206.         asrc.Play ();
  207.         return asrc;
  208.     }
  209.  
  210.     /// convenience method to play background music
  211.     /// <returns>The background music's AudioSource.</returns>
  212.     /// <param name="song">Song.</param>
  213.     /// <param name="volume">Volume.</param>
  214.     public static AudioSource PlayBackgroundMusic(AudioClip song, float volume) {
  215.         AudioSource bgMusicPlayer = PlaySound (song, Vector3.zero, false, true, "{background music}", volume);
  216.         return bgMusicPlayer;
  217.     }
  218.  
  219.     /// creates an accessible listing to all sounds being used by Noisy, visible in the hierarchy & inspector. also handles some static logic.
  220.     public class Global : MonoBehaviour {
  221.         /// All Noise objects with unique names and actual data in the 'sounds' array.
  222.         public static List<Noise> allNoises = new List<Noise> ();
  223.  
  224.         private static Noisy.Global instance;
  225.         public static Noisy.Global Instance() {
  226.             if(instance == null) {
  227.                 if((instance = FindObjectOfType(typeof(Noisy.Global)) as Noisy.Global) == null) {
  228.                     GameObject g = new GameObject("<" + typeof(Noisy.Global).Name + ">");
  229.                     instance = g.AddComponent<Noisy.Global>();
  230.                 }
  231.             }
  232.             return instance;
  233.         }
  234.         public List<Noise> allTheNoises;
  235.  
  236.         void Start() { allTheNoises = allNoises; }
  237.     }
  238.  
  239.     TYPE CreateHandler<TYPE>(string nameOfNoise) where TYPE : NoisyHandler {
  240.         if (name != null) {
  241.             TYPE[] triggers = GetComponents<TYPE> ();
  242.             for (int i = 0; i < triggers.Length; ++i) {
  243.                 if (triggers [i].advancedNoiseOverride == nameOfNoise)
  244.                     return triggers[i];
  245.             }
  246.         }
  247.         return gameObject.AddComponent<TYPE> ();
  248.     }
  249.  
  250.     void CreateOnTrigger() { CreateHandler<OnTriggerAdvancedSettings> (null); }
  251.     void CreateOnCollision() { CreateHandler<OnCollisionAdvancedSettings> (null); }
  252.     void CreateOnKeyPress() { CreateHandler<OnKeyPressAdvancedSettings> (null); }
  253.  
  254.     public class NoisyHandler : MonoBehaviour {
  255.         [Tooltip("if this is set, Noise (below) will be overwritten at runtime by a Noise with this name")]
  256.         public string advancedNoiseOverride;
  257.         public Noise noise;
  258.         [Tooltip("remove this handler after playing the sound once. (eg: one-time acknowledgement)")]
  259.         public bool justOnce;
  260.         protected void NoisyHandlerStart() {
  261.             if (advancedNoiseOverride != null && advancedNoiseOverride.Length > 0) {
  262.                 noise = Noisy.GetSound (advancedNoiseOverride);
  263.             }
  264.         }
  265.         void Start(){ NoisyHandlerStart (); }
  266.     }
  267.  
  268.     public class NoisyObjectInteractHandler : NoisyHandler {
  269.         [Tooltip("identify which GameObjects can trigger this. (eg: only player, only certain item)")]
  270.         public string triggersOnlyByTag;
  271.         public bool IsValidTrigger(GameObject go) {
  272.             return triggersOnlyByTag == null || triggersOnlyByTag.Length == 0 || go.tag == triggersOnlyByTag;
  273.         }
  274.     }
  275.  
  276.     public class OnTriggerAdvancedSettings : NoisyObjectInteractHandler {
  277.         void OnTriggerEnter(Collider c) {
  278.             if (!IsValidTrigger (c.gameObject)) return;
  279.             if (noise.followsObject) {
  280.                 noise.PlaySound (transform);
  281.                 if(justOnce) Destroy (this);
  282.             }
  283.             else {
  284.                 noise.PlaySound (c.transform.position);
  285.             }
  286.         }
  287.     }
  288.  
  289.     public class OnCollisionAdvancedSettings : NoisyObjectInteractHandler {
  290.         void OnCollisionEnter(Collision c) {
  291.             if (!IsValidTrigger (c.gameObject)) return;
  292.             if (noise.followsObject) {
  293.                 noise.PlaySound (transform);
  294.                 if(justOnce) Destroy (this);
  295.             }
  296.             else {
  297.                 noise.PlaySound (c.contacts[0].point);
  298.             }
  299.         }
  300.     }
  301.  
  302.     public class OnKeyPressAdvancedSettings : NoisyHandler {
  303.         public KeyCode key = KeyCode.None;
  304.         public enum KeyEvent {press, release, hold};
  305.         public KeyEvent eventType = KeyEvent.press;
  306.         public bool IsTriggered() {
  307.             switch (eventType) {
  308.             case KeyEvent.press:   return Input.GetKeyDown (key);
  309.             case KeyEvent.release: return Input.GetKeyUp (key);
  310.             case KeyEvent.hold:    return Input.GetKey (key);
  311.             }
  312.             return false;
  313.         }
  314.         void Start() {
  315.             NoisyHandlerStart ();
  316.             if (noise.sounds == null || noise.sounds.Length == 0) {
  317.                 Noisy n = GetComponent<Noisy> ();
  318.                 if (n != null && n.noises != null && n.noises.Length > 0) {
  319.                     this.noise = n.noises [0];
  320.                 }
  321.             }
  322.         }
  323.         void Update() {
  324.             if (IsTriggered()) {
  325.                 noise.PlaySound (transform);
  326.                 if(justOnce) Destroy (this);
  327.             }
  328.         }
  329.     }
  330.  
  331. #if UNITY_EDITOR
  332.     private void OnValidate() {
  333.         if(noises == null) return;
  334.         for(int i = 0; i < noises.Length; ++i) {
  335.             Noise n = noises[i];
  336.             if(n != null && n.activeAudioSource != null) { n.activeAudioSource.volume = 1 - n.volumeReduce; }
  337.         }
  338.     }
  339. #endif
  340. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top