Add SoundEffect audio system (ScriptableObject-based) with test assets
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
75f5704ab8
commit
0be503c927
22 files changed
+741
No files matched your search
@@ -0,0 +1,177 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Audio;
|
||||
|
||||
namespace Prototype
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Prototype/Sound Effect")]
|
||||
public class SoundEffect : ScriptableObject
|
||||
{
|
||||
[SerializeField] SoundEffectDistanceRange distanceRange;
|
||||
[SerializeField] Vector2 volumeMultiplier = new(1, 1);
|
||||
[SerializeField] Vector2 pitch = new(.95f, 1.05f);
|
||||
|
||||
[Header("Additional Settings")]
|
||||
public AudioMixerGroup audioMixerGroup;
|
||||
public float volumeMultiplierFactor = 1;
|
||||
[SerializeField] bool loop;
|
||||
[SerializeField] Vector2 fade = new();
|
||||
|
||||
|
||||
[Header("Additional Settings")]
|
||||
[SerializeField, Range(0, 1)] float defaultVolume = .8f;
|
||||
[SerializeField, Range(-1, 1)] float additionalGain = 0;
|
||||
[SerializeField] float maxPlayInterval = .1f;
|
||||
|
||||
[Header("Clips")]
|
||||
[SerializeField] List<AudioClip> clips;
|
||||
|
||||
public int ClipCount => clips.Count;
|
||||
|
||||
float lastPlayTime;
|
||||
|
||||
public SoundEffectOptions ToOptions() => new()
|
||||
{
|
||||
audioMixerGroup = audioMixerGroup,
|
||||
volumeMultiplier = Random.Range(volumeMultiplier.x, volumeMultiplier.y) * volumeMultiplierFactor,
|
||||
pitch = Random.Range(pitch.x, pitch.y),
|
||||
loop = loop,
|
||||
distanceRange = distanceRange ? distanceRange.range : null,
|
||||
fade = fade
|
||||
};
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public void ApplyVolumeMultiplierFactor()
|
||||
{
|
||||
volumeMultiplier *= volumeMultiplierFactor;
|
||||
volumeMultiplierFactor = 1;
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
Initialize();
|
||||
UnityEditor.EditorApplication.playModeStateChanged -= PlayModeStateChanged;
|
||||
UnityEditor.EditorApplication.playModeStateChanged += PlayModeStateChanged;
|
||||
}
|
||||
|
||||
void PlayModeStateChanged(UnityEditor.PlayModeStateChange change)
|
||||
{
|
||||
if (change == UnityEditor.PlayModeStateChange.EnteredEditMode)
|
||||
{
|
||||
Initialize();
|
||||
return;
|
||||
}
|
||||
|
||||
if (change == UnityEditor.PlayModeStateChange.ExitingEditMode)
|
||||
{
|
||||
Initialize();
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
lastPlayTime = -1;
|
||||
}
|
||||
|
||||
public AudioClip GetClip(int index)
|
||||
{
|
||||
return clips[index];
|
||||
}
|
||||
|
||||
public SoundEffectInstance Play()
|
||||
{
|
||||
return PlayInternal(Vector3.zero, 0);
|
||||
}
|
||||
|
||||
public SoundEffectInstance Play(Vector3 position)
|
||||
{
|
||||
return PlayInternal(position, 0);
|
||||
}
|
||||
|
||||
public SoundEffectInstance Play(Transform transform)
|
||||
{
|
||||
return PlayInternal(transform.position, 0);
|
||||
}
|
||||
|
||||
public SoundEffectInstance Play(Vector3 position, float delay)
|
||||
{
|
||||
return PlayInternal(position, delay);
|
||||
}
|
||||
|
||||
public SoundEffectInstance Play(Transform transform, float delay)
|
||||
{
|
||||
return PlayInternal(transform.position, delay);
|
||||
}
|
||||
|
||||
public SoundEffectInstance PlayFirstClip()
|
||||
{
|
||||
return PlayInternal(Vector3.zero, 0, true);
|
||||
}
|
||||
|
||||
public SoundEffectInstance PlayFirstClip(bool destroyOnLoad)
|
||||
{
|
||||
return PlayInternal(Vector3.zero, 0, true, destroyOnLoad);
|
||||
}
|
||||
|
||||
public SoundEffectInstance PlayFirstClipAt(Vector3 position)
|
||||
{
|
||||
return PlayInternal(position, 0, true);
|
||||
}
|
||||
|
||||
public SoundEffectInstance PlayFirstClipWithDelayAt(Vector3 position, float delay)
|
||||
{
|
||||
return PlayInternal(position, delay, true);
|
||||
}
|
||||
|
||||
SoundEffectInstance PlayInternal(Vector3 position, float delay, bool firstClip = false, bool destroyOnLoad = true)
|
||||
{
|
||||
var options = ToOptions();
|
||||
options.delay = delay;
|
||||
options.destroyOnLoad = destroyOnLoad;
|
||||
|
||||
AudioClip clip;
|
||||
if (firstClip)
|
||||
{
|
||||
clip = clips[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
int rngIndex = Random.Range(0, clips.Count);
|
||||
clip = clips[rngIndex];
|
||||
}
|
||||
|
||||
var time = Time.time;
|
||||
|
||||
if (!Mathf.Approximately(maxPlayInterval, 0))
|
||||
{
|
||||
if (time - lastPlayTime < maxPlayInterval)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
lastPlayTime = time;
|
||||
|
||||
var gameObject = new GameObject($"{this.name} -- {clip.name}");
|
||||
gameObject.SetActive(false);
|
||||
gameObject.transform.position = position;
|
||||
|
||||
if (!options.volume.HasValue)
|
||||
{
|
||||
options.volume = defaultVolume;
|
||||
}
|
||||
|
||||
options.volume += additionalGain;
|
||||
|
||||
var soundEffectInstance = gameObject.AddComponent<SoundEffectInstance>();
|
||||
soundEffectInstance.clip = clip;
|
||||
soundEffectInstance.options = options;
|
||||
|
||||
gameObject.SetActive(true);
|
||||
|
||||
return soundEffectInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user