- Home /
Play many sounds (60 to 200) without Unity cutting sound
Hello,
I am building a game whereby many enemies on the screen die in quick succession. This can be upwards of 50 to 200 enemies all dying at once. When an enemy dies, it spawns a GameObject
(from a pool) that has an AudioSource
and plays the enemy's death sound.
Once the game hits ~60 deaths, the sound completely cuts out for about 2 seconds. This is super irritating and I have to fix it ASAP. Obviously, it is inefficient to play this many sounds at once so I need a solution, or a work around, to play a fraction of the sounds without the 'sound buffer' overloading.
I.e. is there a way to tell how many sounds are playing, and restrict any further sounds from playing? Or setting a threshold between which sounds of the same type can be played?
Thanks so much!
Answer by Eno-Khaon · Jun 08, 2020 at 06:13 PM
This seems like a scenario where you could make good use of Update() vs. LateUpdate() timing.
You can keep a global controller around to listen for enemies to report their deaths, then play pre-mixed sounds accordingly based on the quantity.
First, an example script for the controller to listen for enemies dying:
public class DeathWatch : MonoBehaviour
{
// Sets up a singleton that can be called via a static variable
// It will create itself when requested if it doesn't already exist
// And will assign itself if it already does
private static DeathWatch sdInstance;
public static DeathWatch instance
{
get
{
if(sdInstance == null)
{
// Expensive, but only has to be performed only once
DeathWatch verify = FindObjectOfType<Deathwatch>();
if(verify == null)
{
GameObject newObj = new GameObject();
sdInstance = newObj.AddComponent<T>();
}
else
{
sdInstance = verify;
}
}
return sdInstance;
}
}
void Awake()
{
sdInstance = this;
deathReported = false;
currentDeathCount = new Dictionary<string, int>();
}
// DeathWatch-specific from here down
bool deathReported; // improves efficiency
Dictionary<string, int> currentDeathCount;
public void ReportDeath(string enemyType)
{
if(currentDeathCount.ContainsKey(enemyType))
{
currentDeathCount[enemyType]++;
}
else
{
currentDeathCount[enemyType] = 1;
}
deathReported = true;
}
void LateUpdate()
{
if(deathReported)
{
foreach(KeyValuePair<string, int> kvp in currentDeathCount)
{
// Play sound based on the enemy type kvp.Key
// and death count kvp.Value
}
currentDeathCount.Clear();
deathReported = false;
}
}
}
Next, we have the simple adjustment to the enemy script to support this:
void Update()
{
// Example death
if(health <= 0)
{
Die();
}
}
void Die()
{
DeathWatch.instance.ReportDeath(enemyTypeIdentifier);
PlayDeathSequence();
}
... Yep, it's basically two lines for the enemy units. One to provide an identifier (a string is just an example. You could use any type of value to identify them and adjust the Dictionary accordingly) and one to report their death for the current frame.
That said, if you intend to batch them within a small time window rather than same-frame only, you'll need a little bit more information (for instance, the integer portion of the Dictionary could be replaced with a class/struct for more detailed and grouped information as desired).
Edit: (Whoops, messed up a copy/paste and forgot to change a variable name)
Edit 2: (Fixed another simple script error)