One of the few breaking API changes between Game Studio 3.0 and 3.1
concerns the SoundEffect.Play method.
There are two different
scenarios for playing sound effects:
- Fire &
forget is when you want to trigger a sound, then not have to
worry about it any more, and have the system automatically clean up
after it finishes playing.
- Create
& configure is when you want to get back some sort of
object that lets you alter the sound (changing volume or pitch, applying
3D settings, pausing or resuming) while it plays. This is obviously
more powerful, but also more work because you have to keep track of
which sounds are playing and clean up the relevant objects when you are
done with them.
We think it is important for the
SoundEffect API to support both scenarios, and in Game Studio 3.0, we
tried to make the SoundEffect.Play method handle them both. It returned a
SoundEffectInstance object, which you could use for create &
configure scenarios, but you could also just ignore this return value if
you were doing fire & forget, in which case we did some magic to
make sure the garbage collector would not try to reclaim the
SoundEffectInstance while the sound was still playing.
Turns
out, this didn't work as well as we expected. There were two problems:
- Because the Play method did not know whether you were going
to store the return value, it had to allocate a new SoundEffectInstance
each time, so fire & forget sounds created garbage.
- The only way we could tell the difference between fire &
forget vs. create & configure usage was by noticing if the garbage
collector tried to reclaim a SoundEffectInstance that was still playing.
But .NET garbage collection is not deterministic! This worked ok as
long as GC occurred regularly, but the better optimized your game was,
the less frequently GC would run, in which case fire & forget sounds
might not be reclaimed often enough, so the system would run out of
voices.
In Game Studio 3.1, we changed the API to make
these two usage scenarios explicit:
- SoundEffect.Play is
now only for fire & forget, and it no longer returns a
SoundEffectInstance. Instead, you get back a bool indicating whether the
call succeeded. This no longer generates any garbage, and no longer
supports looping (because if you started a looping sound this way, it
would be impossible to ever stop it, which doesn't seem particularly
useful :-)
- For create & configure
scenarios, we added a new SoundEffect.CreateInstance method. This
returns a paused SoundEffectInstance, so you can set properties such as
Volume, Pitch, and Pan, then call SoundEffectInstance.Play when you are
ready to trigger it.
We removed the SoundEffect.Play3D
method. To play a 3D sound, call SoundEffect.CreateInstance,
SoundEffectInstance.Apply3D, and then SoundEffectInstance.Play. You
cannot use fire & forget with 3D sounds.
We also changed
what happens if you try to play too many sounds at the same time. In GS
3.0, the Play call would throw InstancePlayLimitException. In 3.1,
CreateInstance still throws this exception if it runs out of voices, but
Play just returns false. This is convenient for the common case where
you are playing fire & forget sounds and want to silently ignore
voice overflows, as you can simply ignore the Play return value and no
longer have to write special error handling code.