Skip to content

Instantly share code, notes, and snippets.

@Ddemon26
Created September 26, 2024 05:11
Show Gist options
  • Save Ddemon26/cc596fbc816baf934db4e361ddcdee97 to your computer and use it in GitHub Desktop.
Save Ddemon26/cc596fbc816baf934db4e361ddcdee97 to your computer and use it in GitHub Desktop.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Pool;
namespace TCS.TestSystems.AudioManager {
// AudioEvent class
public class AudioEvent {
public AudioClip Clip { get; }
public float Volume { get; set; } = 1f;
public float Pitch { get; set; } = 1f;
public bool Loop { get; set; } = false;
public float StereoPan { get; set; } = 0f;
public float SpatialBlend { get; set; } = 0f;
public float FadeInDuration { get; set; } = 0f;
public float FadeOutDuration { get; set; } = 0f;
public Vector3? Position { get; set; } = null;
public AudioEvent(AudioClip clip) {
Clip = clip ?? throw new ArgumentNullException(nameof(clip));
}
}
// SoundInstance class
public class SoundInstance {
public AudioEvent AudioEvent { get; }
public AudioSource AudioSource { get; }
public bool IsPlaying => AudioSource != null && AudioSource.isPlaying;
public event Action<SoundInstance> OnFinished;
private Coroutine fadeCoroutine;
private float fadeInDuration;
private float fadeOutDuration;
public SoundInstance(AudioEvent audioEvent, AudioSource audioSource) {
AudioEvent = audioEvent;
AudioSource = audioSource;
fadeInDuration = AudioEvent.FadeInDuration;
fadeOutDuration = AudioEvent.FadeOutDuration;
ConfigureAudioSource();
}
private void ConfigureAudioSource() {
if (AudioEvent.Position.HasValue) {
AudioSource.transform.position = AudioEvent.Position.Value;
AudioSource.spatialBlend = 1f;
} else {
AudioSource.spatialBlend = AudioEvent.SpatialBlend;
}
AudioSource.clip = AudioEvent.Clip;
AudioSource.volume = AudioEvent.Volume;
AudioSource.pitch = AudioEvent.Pitch;
AudioSource.loop = AudioEvent.Loop;
AudioSource.panStereo = AudioEvent.StereoPan;
// Additional configurations
if (fadeInDuration > 0f) {
AudioSource.volume = 0f;
fadeCoroutine = AudioManager.Instance.StartCoroutine(FadeIn());
}
AudioSource.Play();
if (!AudioEvent.Loop) {
AudioManager.Instance.StartCoroutine(CheckIfFinished());
}
}
private IEnumerator FadeIn() {
float time = 0f;
while (time < fadeInDuration) {
time += Time.deltaTime;
AudioSource.volume = Mathf.Lerp(0f, AudioEvent.Volume, time / fadeInDuration);
yield return null;
}
AudioSource.volume = AudioEvent.Volume;
}
private IEnumerator FadeOutAndStop() {
float startVolume = AudioSource.volume;
float time = 0f;
while (time < fadeOutDuration) {
time += Time.deltaTime;
AudioSource.volume = Mathf.Lerp(startVolume, 0f, time / fadeOutDuration);
yield return null;
}
AudioSource.volume = 0f;
StopImmediate();
}
private void StopImmediate() {
if (fadeCoroutine != null) {
AudioManager.Instance.StopCoroutine(fadeCoroutine);
}
AudioSource.Stop();
OnFinished?.Invoke(this);
}
public void Stop() {
if (fadeOutDuration > 0f) {
fadeCoroutine = AudioManager.Instance.StartCoroutine(FadeOutAndStop());
} else {
StopImmediate();
}
}
private IEnumerator CheckIfFinished() {
yield return new WaitUntil(() => !AudioSource.isPlaying);
OnFinished?.Invoke(this);
}
public void FadeOutAndStop(float duration) {
fadeOutDuration = duration;
fadeCoroutine = AudioManager.Instance.StartCoroutine(FadeOutAndStop());
}
}
// AudioManager class
public class AudioManager : MonoBehaviour {
private static AudioManager s_instance;
public static AudioManager Instance {
get {
if (s_instance == null) {
var obj = new GameObject("AudioManager");
s_instance = obj.AddComponent<AudioManager>();
}
return s_instance;
}
}
private AudioPooler m_audioSourcePool;
private AudioSource m_musicSource;
private List<SoundInstance> m_activeSounds = new List<SoundInstance>();
void Awake() {
if (s_instance == null) {
s_instance = this;
DontDestroyOnLoad(gameObject);
m_audioSourcePool = new AudioPooler(this);
InitializeMusicSource();
} else if (s_instance != this) {
Destroy(gameObject);
}
}
void InitializeMusicSource() {
m_musicSource = gameObject.AddComponent<AudioSource>();
m_musicSource.loop = true;
}
public SoundInstance PlaySound(AudioEvent audioEvent) {
if (audioEvent == null || audioEvent.Clip == null) {
Debug.LogError("Invalid AudioEvent or AudioClip.");
return null;
}
var audioSource = m_audioSourcePool.Get();
var soundInstance = new SoundInstance(audioEvent, audioSource);
soundInstance.OnFinished += HandleSoundFinished;
m_activeSounds.Add(soundInstance);
return soundInstance;
}
public void StopSound(SoundInstance soundInstance) {
if (soundInstance != null) {
soundInstance.Stop();
}
}
private void HandleSoundFinished(SoundInstance soundInstance) {
soundInstance.OnFinished -= HandleSoundFinished;
m_activeSounds.Remove(soundInstance);
m_audioSourcePool.Release(soundInstance.AudioSource);
}
public void StopSoundByClip(AudioClip clip) {
var soundsToStop = m_activeSounds.FindAll(s => s.AudioEvent.Clip == clip);
foreach (var sound in soundsToStop) {
sound.Stop();
}
}
public void PlayMusic(AudioEvent audioEvent) {
if (audioEvent == null || audioEvent.Clip == null) {
Debug.LogError("Invalid AudioEvent or AudioClip.");
return;
}
m_musicSource.clip = audioEvent.Clip;
m_musicSource.volume = audioEvent.Volume;
m_musicSource.pitch = audioEvent.Pitch;
m_musicSource.loop = audioEvent.Loop;
m_musicSource.panStereo = audioEvent.StereoPan;
// Additional configurations
m_musicSource.Play();
}
public void StopMusic() {
m_musicSource.Stop();
}
public void StopAllSounds() {
foreach (var soundInstance in m_activeSounds) {
soundInstance.Stop();
}
m_activeSounds.Clear();
m_musicSource.Stop();
}
}
// AudioPooler class
public class AudioPooler {
private ObjectPool<AudioSource> m_audioSourcePool;
private readonly int m_initialPoolSize = 10;
private readonly int m_maxPoolSize = 100;
private readonly AudioManager m_audioManager;
public AudioPooler(AudioManager audioManager, int initialPoolSize = 10, int maxPoolSize = 100) {
m_audioManager = audioManager;
m_initialPoolSize = initialPoolSize;
m_maxPoolSize = maxPoolSize;
InitializePool();
}
void InitializePool() {
m_audioSourcePool = new ObjectPool<AudioSource>(
CreatePooledItem,
OnTakeFromPool,
OnReturnedToPool,
OnDestroyPoolObject,
true,
m_initialPoolSize,
m_maxPoolSize
);
}
AudioSource CreatePooledItem() {
var go = new GameObject("PooledAudioSource");
go.transform.SetParent(m_audioManager.transform);
return go.AddComponent<AudioSource>();
}
void OnReturnedToPool(AudioSource audioSource) {
audioSource.Stop();
audioSource.clip = null;
audioSource.gameObject.SetActive(false);
}
void OnTakeFromPool(AudioSource audioSource) {
audioSource.gameObject.SetActive(true);
}
void OnDestroyPoolObject(AudioSource audioSource) {
GameObject.Destroy(audioSource.gameObject);
}
public AudioSource Get() {
return m_audioSourcePool.Get();
}
public void Release(AudioSource source) {
m_audioSourcePool.Release(source);
}
}
// Extension methods for easier API usage
public static class AudioClipExtensions {
public static SoundInstance PlaySound(this AudioClip clip, float volume = 1f, float pitch = 1f, bool loop = false, float fadeInDuration = 0f, float fadeOutDuration = 0f) {
var audioEvent = new AudioEvent(clip) {
Volume = volume,
Pitch = pitch,
Loop = loop,
FadeInDuration = fadeInDuration,
FadeOutDuration = fadeOutDuration
};
return AudioManager.Instance.PlaySound(audioEvent);
}
public static void StopSound(this AudioClip clip) {
AudioManager.Instance.StopSoundByClip(clip);
}
public static SoundInstance PlaySpatialSound(this AudioClip clip, Vector3 position, float volume = 1f, float pitch = 1f, bool loop = false, float fadeInDuration = 0f, float fadeOutDuration = 0f) {
var audioEvent = new AudioEvent(clip) {
Volume = volume,
Pitch = pitch,
Loop = loop,
FadeInDuration = fadeInDuration,
FadeOutDuration = fadeOutDuration,
Position = position,
SpatialBlend = 1f
};
return AudioManager.Instance.PlaySound(audioEvent);
}
public static void PlayMusic(this AudioClip clip, float volume = 1f, float pitch = 1f, bool loop = true) {
var audioEvent = new AudioEvent(clip) {
Volume = volume,
Pitch = pitch,
Loop = loop
};
AudioManager.Instance.PlayMusic(audioEvent);
}
}
// PlayerController class
public class PlayerController : MonoBehaviour {
public AudioClip m_jumpClip;
public AudioClip m_walkClip;
public AudioClip m_footstepClip;
public AudioClip m_backgroundMusicClip;
bool m_isWalking;
SoundInstance m_walkSoundInstance;
void Update() {
HandleJump();
HandleWalk();
HandleFootsteps();
HandleBackgroundMusic();
HandleSpatialSounds();
}
void HandleJump() {
if (Input.GetKeyDown(KeyCode.Space)) {
Debug.Log("Player jumped");
m_jumpClip.PlaySound(volume: 0.8f, pitch: 1.2f);
}
if (Input.GetKeyDown(KeyCode.J)) {
Debug.Log("Stop jump sound");
m_jumpClip.StopSound();
}
}
void HandleWalk() {
if (IsWalking()) {
if (!m_isWalking) {
Debug.Log("Player started walking");
m_walkSoundInstance = m_walkClip.PlaySound(volume: CalculateVolumeBasedOnSpeed(), loop: true);
m_isWalking = true;
}
} else {
if (m_isWalking) {
Debug.Log("Player stopped walking");
if (m_walkSoundInstance != null) {
m_walkSoundInstance.Stop();
}
m_isWalking = false;
}
}
}
void HandleFootsteps() {
if (Input.GetKeyDown(KeyCode.F)) {
Debug.Log("Playing footstep sound with random pitch");
m_footstepClip.PlaySound(pitch: UnityEngine.Random.Range(0.9f, 1.1f));
}
if (Input.GetKeyDown(KeyCode.S)) {
Debug.Log("Stop footstep sound");
m_footstepClip.StopSound();
}
}
void HandleBackgroundMusic() {
if (Input.GetKeyDown(KeyCode.M)) {
Debug.Log("Playing background music");
m_backgroundMusicClip.PlayMusic(volume: 0.6f, loop: true);
}
if (Input.GetKeyDown(KeyCode.N)) {
Debug.Log("Stopping background music");
AudioManager.Instance.StopMusic();
}
}
void HandleSpatialSounds() {
if (Input.GetKeyDown(KeyCode.G)) {
Debug.Log("Playing spatial sound at a specific position");
var soundPosition = transform.position;
m_footstepClip.PlaySpatialSound(soundPosition, volume: 1f);
}
}
bool IsWalking() {
return Input.GetKey(KeyCode.W);
}
float CalculateVolumeBasedOnSpeed() {
// Placeholder: replace with actual logic
return 1f;
}
void OnGUI() {
GUI.Label(new Rect(10, 10, 200, 20), "Press Space to jump");
GUI.Label(new Rect(10, 30, 200, 20), "Press J to stop jump sound");
GUI.Label(new Rect(10, 50, 200, 20), "Press W to walk");
GUI.Label(new Rect(10, 70, 200, 20), "Press F to play footstep sound");
GUI.Label(new Rect(10, 90, 200, 20), "Press S to stop footstep sound");
GUI.Label(new Rect(10, 110, 200, 20), "Press G to play spatial sound");
GUI.Label(new Rect(10, 130, 200, 20), "Press M to play background music");
GUI.Label(new Rect(10, 150, 200, 20), "Press N to stop background music");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment