This is a sub-topic of this topic. Please keep this topic clean and only talk about this one specific issue. Balance will not be changed - it is just an aesthetical (sound-related) change.
This specific topic is about ambient (background) sound. To prevent ambiguity, this topic is not about the following type of sounds:
- Impact sounds
- Gunfire sounds
The problem
When turrets rotate they create an ambient sound. When buildings just exist, they have an ambient sound. A lot of things create ambient sounds.
To give you an idea:
- Almost all extractors and power generators have ambient sounds.
- All units with turrets have (additional) ambient sounds (when the turret moves, not when it is firing).
- Almost all props have ambient sounds (when trees are burning, for example).
- All units create ambient sounds (when moving, or when changing the state of moving).
- ....
To get a better idea, this is the number of loop audio files defined in blueprints:
- ActiveLoop: 100+
- ConstructLoop: 180+
- CaptureLoop: 40+
- ReclaimLoop: 40+
- AmbientMove: 380+
- AmbientMoveSub: 20+
In order to actually experience these audio files you need to turn off your music and set Fx sound to full. Slowly but surely, you'll start hearing them. My problem with these loops is how they are made.
-- as defined in unit.lua
GetSoundEntity = function(self, type)
if not self.Sounds then self.Sounds = {} end
if not self.Sounds[type] then
local sndEnt
if self.SoundEntities[1] then
sndEnt = table.remove(self.SoundEntities, 1)
else
sndEnt = Entity()
Warp(sndEnt, self:GetPosition())
sndEnt:AttachTo(self, -1)
self.Trash:Add(sndEnt)
end
self.Sounds[type] = sndEnt
end
return self.Sounds[type]
end,
PlayUnitAmbientSound = function(self, sound)
local bp = self.Blueprint
if not bp.Audio[sound] then return end
local entity = self:GetSoundEntity('Ambient' .. sound)
entity:SetAmbientSound(bp.Audio[sound], nil)
end,
StopUnitAmbientSound = function(self, sound)
local bp = self.Blueprint
if not bp.Audio[sound] then return end
local type = 'Ambient' .. sound
local entity = self:GetSoundEntity(type)
if entity and not entity:BeenDestroyed() then
self.Sounds[type] = nil
entity:SetAmbientSound(nil, nil)
self.SoundEntities = self.SoundEntities or {}
table.insert(self.SoundEntities, entity)
end
end,
Whenever an ambient sound is made a new entity is made. And that entity plays the sound. When the ambient sound is no longer required the sound is removed but the entity is kept. In turn, every unit can easily carry 2 - 3 additional entities because there are various ambient sounds that can be played at once.
To make it clear: the playing of the sounds is not the issue. Sound is not processed by the sim thread. The calls to the engine hurt because they are engine calls - but that isn't the issue either. Remember that the sim runs at 10 ticks a second, therefore we have 100ms for each tick
To visualize the issue, say we have a 1000 mantis in-game that are:
- All idle and all ambient sound enabled, stabilizes at 10.5 - 11.0 ms.
- All idle and all ambient sound disabled, stabilizes at 8.5 - 9.0 ms.
This means that just having the entities cached for the sound is taking up about 2 ms of the entire sim. As a game can have thousands of units that may have several ambient sounds at once it can easily eat up 5 - 10 ms late game.
To solidify the issue I kept track how often sound is being stopped with the profiler.
That is 363K calls to one function in a game with 4 AIx that lasted 26 minutes. It is by far the most common function called.
A possible solution
The later the game gets, the less the ambient sound of an individual unit matters. As an example, if you have an army of 200 mantis then hearing the ambient sound of 20 of them will be sufficient, for two reasons:
- The sound thread can not 'render' all the 200 sounds anyhow.
- The sound is typically so low in volume that you don't hear it all (try hear the t1 aeon mass extractor with music on).
Therefore I suggest to have units that do not play ambient sounds at all as the game continues. For example, we could say the following:
- For each t1 unit, if there are less than 100 in-game then all sounds remain as is.
- For each t1 unit, if there are more than 100 in-game then every 2nd unit does not make ambient sound.
- For each t1 unit, if there are more than 300 in-game then every 2nd and 3rd unit does not make ambient sound.
- For each ...
This way there will always be some units in your army that can make ambient sounds. But, not all of them will be and that benefits the sim in the long run. We could specialize it on specific units or tech levels. For example, I think experimental units and the ACU should always make ambient sounds.
Solutions considered, but not worth it
One solution the original game used was trashing the entity when it was no longer required. This will introduce garbage that we may not notice immediately, but once the garbage collector kicks in you can get a stutter.
Disclaimer: the times are relative to your processor / RAM. It may be slower or faster on your computer, but the relative time difference (in %) will remain the same.