using System.Collections.Generic; using UnityEngine; using System.Collections; #if UNITY_EDITOR using UnityEditorInternal; using UnityEditor; #endif namespace MalbersAnimations.Utilities { [AddComponentMenu("Malbers/Utilities/Effects - Audio/Animator Event Sound")] public class AnimatorEventSounds : MonoBehaviour, IAnimatorListener { public List m_EventSound; public AudioSource _audioSource; protected Animator anim; public bool debug; void Start() { anim = GetComponent(); //Get the reference for the animator if (_audioSource == null) //if there's no audio source add one.. _audioSource = gameObject.AddComponent(); _audioSource.volume = 0; } public virtual void DisableSoundEvent(string SoundName) { EventSound SoundEvent = m_EventSound.Find(item => item.name == SoundName); if (SoundEvent != null) SoundEvent.active = false; } public virtual void EnableSoundEvent(string SoundName) { EventSound SoundEvent = m_EventSound.Find(item => item.name == SoundName); if (SoundEvent != null) SoundEvent.active = true; } public virtual void PlaySound(AnimationEvent e) { if (e.animatorClipInfo.weight < 0.1) return; // if is too small the weight of the animation clip do nothing if (debug) Debug.Log($"Play Audio: Clip - [{e.animatorClipInfo.clip.name}]",e.animatorClipInfo.clip); var SoundEvent = m_EventSound.Find(item => item.name == e.stringParameter && item.active == true); if (SoundEvent != null) { SoundEvent.VolumeWeight = e.animatorClipInfo.weight; if (anim) _audioSource.pitch = anim.speed; //Match the AnimatorSpeed with the Sound Pitch if (_audioSource.isPlaying) //If the Audio is already Playing play the one that has more weight { if (SoundEvent.VolumeWeight * SoundEvent.volume > _audioSource.volume) { SoundEvent.PlayAudio(_audioSource); } } else { SoundEvent.PlayAudio(_audioSource); } } } public virtual void PlaySound(string sound) { var SoundEvent = m_EventSound.Find(item => item.name == sound && item.active == true); if (SoundEvent != null) { SoundEvent.VolumeWeight = 1; if (_audioSource.isPlaying) //If the Audio is already Playing play the one that has more weight { if (SoundEvent.volume > _audioSource.volume) SoundEvent.PlayAudio(_audioSource); } else { SoundEvent.PlayAudio(_audioSource); } } } public virtual void PlaySoundForever(string sound) { var SoundEvent = m_EventSound.Find(item => item.name == sound && item.active == true); if (SoundEvent != null) { SoundEvent.VolumeWeight = 1; StartCoroutine(C_Playforever(SoundEvent)); } } public virtual void StopPlaying(string sound) { var SoundEvent = m_EventSound.Find(item => item.name == sound && item.active == true); if (SoundEvent != null && SoundEvent.source != null) { if (SoundEvent.source.isPlaying) SoundEvent.source.Stop(); } StopAllCoroutines(); } IEnumerator C_Playforever(EventSound E_sound) { if (E_sound.interval <= 0) { yield return null; } else { var timeInterval = new WaitForSeconds(E_sound.interval); while (true) { E_sound.PlayAudio(_audioSource); yield return timeInterval; } } yield return null; } public virtual bool OnAnimatorBehaviourMessage(string message, object value) => this.InvokeWithParams(message, value); } [System.Serializable] public class EventSound { [Tooltip("Name of the Sound you will call on the Animation Event")] public string name = "Name Here"; public float volume = 1; public float pitch = 1; public bool active = true; [Tooltip("Interval Time to play a sound forever. Remember to call [PlaySoundForever]")] public float interval = 0f; public AudioSource source; public AudioClip[] Clips; /// Weight of the sound depeding of the Animation Weight public float VolumeWeight { get; internal set; } public void PlayAudio(AudioSource audio) { if (source == null) source = audio; if (source == null) return; //Do nothing if the audio is empty if (Clips == null || Clips.Length == 0) return; //Do nothing if there's no clips source.spatialBlend = 1; //Set the sound to 3D source.clip = Clips[Random.Range(0, Clips.Length)]; //Set a random clip to the audio Source source.pitch = pitch; //Depending the animator speed modify the pitch source.volume = Mathf.Clamp01(volume * VolumeWeight); //Depending the weight of the animation clip modify the volume source.Play(); //Play the Audio } } #if UNITY_EDITOR [CanEditMultipleObjects, CustomEditor(typeof(AnimatorEventSounds))] public class AnimEventSoundEditor : Editor { private ReorderableList list; private SerializedProperty m_EventSound, debug; private AnimatorEventSounds M; private void OnEnable() { M = ((AnimatorEventSounds)target); m_EventSound = serializedObject.FindProperty("m_EventSound"); debug = serializedObject.FindProperty("debug"); list = new ReorderableList(serializedObject, m_EventSound, true, true, true, true); list.drawElementCallback = DrawElementCallback; list.drawHeaderCallback = HeaderCallbackDelegate; list.onAddCallback = OnAddCallBack; } public override void OnInspectorGUI() { serializedObject.Update(); MalbersEditor.DrawDescription("Receive animations events from the animations clips to play sounds using the method: PlaySound(string Name)"); EditorGUI.BeginChangeCheck(); { EditorGUILayout.BeginVertical(MTools.StyleGray); { list.DoLayoutList(); if (list.index != -1) { EditorGUILayout.BeginVertical(EditorStyles.helpBox); { SerializedProperty Element = m_EventSound.GetArrayElementAtIndex(list.index); EditorGUILayout.LabelField("[" + M.m_EventSound[list.index].name + "]", EditorStyles.boldLabel); EditorGUI.indentLevel++; EditorGUILayout.PropertyField(Element.FindPropertyRelative("interval")); EditorGUILayout.PropertyField(Element.FindPropertyRelative("Clips"), new GUIContent("Clips", "AudioClips"), true); EditorGUILayout.PropertyField(Element.FindPropertyRelative("source"), new GUIContent("Internal Source", "AudioSource")); EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); } EditorGUILayout.BeginVertical(EditorStyles.helpBox); { EditorGUILayout.PropertyField(serializedObject.FindProperty("_audioSource"), new GUIContent("Global Source", "AudioSource"), true); } EditorGUILayout.EndVertical(); } EditorGUILayout.EndVertical(); } if (EditorGUI.EndChangeCheck()) { Undo.RecordObject(target, "Animation Event Sound"); // EditorUtility.SetDirty(target); } serializedObject.ApplyModifiedProperties(); } void HeaderCallbackDelegate(Rect rect) { Rect R_1 = new Rect(rect.x + 28, rect.y, (rect.width) / 3 + 25, EditorGUIUtility.singleLineHeight); EditorGUI.LabelField(R_1, "Name"); Rect R_2 = new Rect(rect.x + (rect.width) / 3 + 65, rect.y, (rect.width) / 3, EditorGUIUtility.singleLineHeight); EditorGUI.LabelField(R_2, "Volume"); Rect R_3 = new Rect(rect.x + ((rect.width) / 3) * 2 + 40, rect.y, ((rect.width) / 3), EditorGUIUtility.singleLineHeight); EditorGUI.LabelField(R_3, "Pitch"); Rect R_4 = new Rect(rect.width, rect.y, 25, EditorGUIUtility.singleLineHeight); MalbersEditor.DrawDebugIcon(R_4, debug); } void DrawElementCallback(Rect rect, int index, bool isActive, bool isFocused) { var element = m_EventSound.GetArrayElementAtIndex(index); var name = element.FindPropertyRelative("name"); var active = element.FindPropertyRelative("active"); var volume = element.FindPropertyRelative("volume"); var pitch = element.FindPropertyRelative("pitch"); rect.y += 2; Rect R_0 = new Rect(rect.x - 3, rect.y, 10, EditorGUIUtility.singleLineHeight); Rect R_1 = new Rect(rect.x + 15, rect.y, (rect.width) / 3 + 55 - 15, EditorGUIUtility.singleLineHeight); Rect R_2 = new Rect(rect.x + (rect.width) / 3 + 61, rect.y, (rect.width) / 3 - 30, EditorGUIUtility.singleLineHeight); Rect R_3 = new Rect(rect.x + ((rect.width) / 3) * 2 + 35, rect.y, ((rect.width) / 3) - 35, EditorGUIUtility.singleLineHeight); EditorGUI.PropertyField(R_0, active, GUIContent.none); EditorGUI.PropertyField(R_1, name, GUIContent.none); EditorGUI.PropertyField(R_2, volume, GUIContent.none); EditorGUI.PropertyField(R_3, pitch, GUIContent.none); } void OnAddCallBack(ReorderableList list) { if (M.m_EventSound == null) { M.m_EventSound = new System.Collections.Generic.List(); } M.m_EventSound.Add(new EventSound()); } } #endif }