Created
July 31, 2021 17:00
-
-
Save baruchadi/3c23caf609fa0f4bd349d9ea432eb9c4 to your computer and use it in GitHub Desktop.
Sound effect scriptable object, allows for multiple sound files, varying pitch & volume configuration, and ability to control pitch by semitones
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Sirenix.OdinInspector; | |
using UnityEditor; | |
using UnityEngine; | |
using Utils; | |
using Random = UnityEngine.Random; | |
namespace ScriptableObjects | |
{ | |
[CreateAssetMenu(fileName = "NewSoundEffect", menuName = "Audio/New Sound Effect")] | |
public class SoundEffectSO : ScriptableObject | |
{ | |
#region config | |
private static readonly float SEMITONES_TO_PITCH_CONVERSION_UNIT = 1.05946f; | |
[Required] public AudioClip[] clips; | |
[MinMaxSlider(0, 1)] [BoxGroup("config")] | |
public Vector2 volume = new Vector2(0.5f, 0.5f); | |
//Pitch / Semitones | |
[LabelWidth(100)] [HorizontalGroup("config/pitch")] | |
public bool useSemitones; | |
[HideLabel] | |
[ShowIf("useSemitones")] | |
[HorizontalGroup("config/pitch")] | |
[MinMaxSlider(-10, 10)] | |
[OnValueChanged("SyncPitchAndSemitones")] | |
public Vector2Int semitones = new Vector2Int(0, 0); | |
[HideLabel] | |
[HideIf("useSemitones")] | |
[MinMaxSlider(0, 3)] | |
[HorizontalGroup("config/pitch")] | |
[OnValueChanged("SyncPitchAndSemitones")] | |
public Vector2 pitch = new Vector2(1, 1); | |
[BoxGroup("config")] [SerializeField] private SoundClipPlayOrder playOrder; | |
[DisplayAsString] [BoxGroup("config")] [SerializeField] | |
private int playIndex = 0; | |
#endregion | |
#region PreviewCode | |
#if UNITY_EDITOR | |
private AudioSource previewer; | |
private void OnEnable() | |
{ | |
previewer = EditorUtility | |
.CreateGameObjectWithHideFlags("AudioPreview", HideFlags.HideAndDontSave, | |
typeof(AudioSource)) | |
.GetComponent<AudioSource>(); | |
} | |
private void OnDisable() | |
{ | |
DestroyImmediate(previewer.gameObject); | |
} | |
[ButtonGroup("previewControls")] | |
[GUIColor(.3f, 1f, .3f)] | |
[Button(ButtonSizes.Gigantic)] | |
private void PlayPreview() | |
{ | |
Play(previewer); | |
} | |
[ButtonGroup("previewControls")] | |
[GUIColor(1, .3f, .3f)] | |
[Button(ButtonSizes.Gigantic)] | |
[EnableIf("@previewer.isPlaying")] | |
private void StopPreview() | |
{ | |
previewer.Stop(); | |
} | |
#endif | |
#endregion | |
public void SyncPitchAndSemitones() | |
{ | |
if (useSemitones) | |
{ | |
pitch.x = Mathf.Pow(SEMITONES_TO_PITCH_CONVERSION_UNIT, semitones.x); | |
pitch.y = Mathf.Pow(SEMITONES_TO_PITCH_CONVERSION_UNIT, semitones.y); | |
} | |
else | |
{ | |
semitones.x = Mathf.RoundToInt(Mathf.Log10(pitch.x) / Mathf.Log10(SEMITONES_TO_PITCH_CONVERSION_UNIT)); | |
semitones.y = Mathf.RoundToInt(Mathf.Log10(pitch.y) / Mathf.Log10(SEMITONES_TO_PITCH_CONVERSION_UNIT)); | |
} | |
} | |
private AudioClip GetAudioClip() | |
{ | |
// get current clip | |
var clip = clips[playIndex >= clips.Length ? 0 : playIndex]; | |
// find next clip | |
switch (playOrder) | |
{ | |
case SoundClipPlayOrder.in_order: | |
playIndex = (playIndex + 1) % clips.Length; | |
break; | |
case SoundClipPlayOrder.random: | |
playIndex = Random.Range(0, clips.Length); | |
break; | |
case SoundClipPlayOrder.reverse: | |
playIndex = (playIndex + clips.Length - 1) % clips.Length; | |
break; | |
} | |
// return clip | |
return clip; | |
} | |
public AudioSource Play(AudioSource audioSourceParam = null) | |
{ | |
if (clips.Length == 0) | |
{ | |
this.LogWarning($"Missing sound clips for {name}"); | |
return null; | |
} | |
var source = audioSourceParam; | |
if (source == null) | |
{ | |
var _obj = new GameObject("Sound", typeof(AudioSource)); | |
source = _obj.GetComponent<AudioSource>(); | |
} | |
// set source config: | |
source.clip = GetAudioClip(); | |
source.volume = Random.Range(volume.x, volume.y); | |
source.pitch = useSemitones | |
? Mathf.Pow(SEMITONES_TO_PITCH_CONVERSION_UNIT, Random.Range(semitones.x, semitones.y)) | |
: Random.Range(pitch.x, pitch.y); | |
source.Play(); | |
#if UNITY_EDITOR | |
if (source != previewer) | |
{ | |
Destroy(source.gameObject, source.clip.length / source.pitch); | |
} | |
#else | |
Destroy(source.gameObject, source.clip.length / source.pitch); | |
#endif | |
return source; | |
} | |
enum SoundClipPlayOrder | |
{ | |
random, | |
in_order, | |
reverse | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment