unified audio manager that limits the number of simultaneous sounds in certain categories

This commit is contained in:
Ian Woods 2026-06-30 20:12:03 -07:00
parent d53a2a47e2
commit 404c749a43
9 changed files with 154 additions and 110 deletions

View file

@ -27,7 +27,7 @@ MonoBehaviour:
GroundedOnly: 0
Damage: 5
Range: 10
FireRate: 2
AttacksPerSecond: 1.5
SplashRadius: 0
ChainCount: 0
ChainRange: 0

View file

@ -99,7 +99,6 @@ GameObject:
- component: {fileID: 805962841523123163}
- component: {fileID: 8853488620519990682}
- component: {fileID: 4524938955752569428}
- component: {fileID: 42814861218671054}
- component: {fileID: -2590971686077483651}
m_Layer: 0
m_Name: Tower_TeslaCoil
@ -147,7 +146,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
GlobalObjectIdHash: 280718094
GlobalObjectIdHash: 1363936954
InScenePlacedSourceGlobalObjectIdHash: 0
DeferredDespawnTick: 0
Ownership: 1
@ -224,103 +223,6 @@ MonoBehaviour:
arcDuration: 0.12
segmentsPerArc: 6
jitter: 0.2
--- !u!82 &42814861218671054
AudioSource:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6482414459531823157}
m_Enabled: 1
serializedVersion: 4
OutputAudioMixerGroup: {fileID: 0}
m_audioClip: {fileID: 0}
m_Resource: {fileID: 0}
m_PlayOnAwake: 0
m_Volume: 0.5
m_Pitch: 1
Loop: 0
Mute: 0
Spatialize: 0
SpatializePostEffects: 0
Priority: 128
DopplerLevel: 1
MinDistance: 1
MaxDistance: 500
Pan2D: 0
rolloffMode: 0
BypassEffects: 0
BypassListenerEffects: 0
BypassReverbZones: 0
rolloffCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
panLevelCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
spreadCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
reverbZoneMixCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
--- !u!114 &-2590971686077483651
MonoBehaviour:
m_ObjectHideFlags: 0

View file

@ -2222,6 +2222,63 @@ Transform:
m_Children: []
m_Father: {fileID: 1097152085}
m_LocalEulerAnglesHint: {x: 0, y: 90, z: 0}
--- !u!1 &176580460
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 176580462}
- component: {fileID: 176580461}
m_Layer: 0
m_Name: AudioManager
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &176580461
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 176580460}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9ec21c01769ef58d1ac26ba89668eeaa, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::TD.Audio.AudioManager
categories:
- category: 0
maxVoices: 4
volume: 0.5
- category: 1
maxVoices: 4
volume: 0.5
- category: 2
maxVoices: 4
volume: 0.5
- category: 3
maxVoices: 4
volume: 0.5
--- !u!4 &176580462
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 176580460}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!43 &203092944
Mesh:
m_ObjectHideFlags: 0
@ -28764,3 +28821,4 @@ SceneRoots:
- {fileID: 1097152085}
- {fileID: 1889390964}
- {fileID: 2139601601}
- {fileID: 176580462}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d11cb1aeddfee3009879738a45665945
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,5 @@
// Assets/_Project/Scripts/Audio/AudioCategory.cs
namespace TD.Audio
{
public enum AudioCategory { TowerFire, EnemyHitDeath, UI, Music }
}

View file

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2f652b06e3d150798895893892820cc7

View file

@ -0,0 +1,74 @@
// Assets/_Project/Scripts/Audio/AudioManager.cs
using System.Collections.Generic;
using UnityEngine;
namespace TD.Audio
{
public class AudioManager : MonoBehaviour
{
[System.Serializable]
public struct CategoryConfig
{
public AudioCategory category;
public int maxVoices;
[Range(0f, 1f)]
public float volume;
}
public static AudioManager Instance { get; private set; }
[SerializeField] private CategoryConfig[] categories;
private Dictionary<AudioCategory, AudioSource[]> pools;
private Dictionary<AudioCategory, int> indices;
private void Awake()
{
if (Instance != null)
{
Destroy(gameObject);
return;
}
Instance = this;
InitializePools();
}
private void OnDestroy()
{
if (Instance == this) Instance = null;
}
private void InitializePools()
{
pools = new Dictionary<AudioCategory, AudioSource[]>();
indices = new Dictionary<AudioCategory, int>();
foreach (var config in categories)
{
var pool = new AudioSource[config.maxVoices];
for (int i = 0; i < config.maxVoices; i++)
{
var src = gameObject.AddComponent<AudioSource>();
src.playOnAwake = false;
src.volume = config.volume;
pool[i] = src;
}
pools[config.category] = pool;
indices[config.category] = 0;
}
}
public void Play(AudioClip clip, AudioCategory category, float pitch = 1f)
{
if (clip == null) return;
if (!pools.TryGetValue(category, out var pool)) return;
int idx = indices[category];
var src = pool[idx % pool.Length];
src.pitch = pitch;
src.clip = clip;
src.Play();
indices[category] = idx + 1;
}
}
}

View file

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 9ec21c01769ef58d1ac26ba89668eeaa

View file

@ -1,4 +1,5 @@
// Assets/_Project/Scripts/Combat/TeslaTowerFireSound.cs
using TD.Audio;
using UnityEngine;
namespace TD.Combat
@ -14,7 +15,6 @@ namespace TD.Combat
/// as the Tesla Coil gains more audio (idle hum, charge-up, etc.).
/// </remarks>
[RequireComponent(typeof(TowerCombat))]
[RequireComponent(typeof(AudioSource))]
public class TeslaTowerFireSound : MonoBehaviour
{
[Tooltip("Sound played each time the coil discharges.")]
@ -25,12 +25,10 @@ namespace TD.Combat
[SerializeField] private float maxPitch = 1.07f;
private TowerCombat combat;
private AudioSource audioSource;
private void Awake()
{
combat = GetComponent<TowerCombat>();
audioSource = GetComponent<AudioSource>();
}
private void OnEnable()
@ -45,12 +43,7 @@ namespace TD.Combat
private void HandleAreaFired(Vector3[] positions)
{
if (fireClip != null)
{
audioSource.pitch = Random.Range(minPitch, maxPitch);
audioSource.clip = fireClip;
audioSource.Play();
}
AudioManager.Instance?.Play(fireClip, AudioCategory.TowerFire, Random.Range(minPitch, maxPitch));
}
}
}