2023.2: Game Immersion — Sound FX
Compelling music will only get you so far. Imagine a Lightsaber that doesn’t Wooosh when swung or footsteps crushing leaves made no sound at all? Perfectly timed sound FX are absolutely necessary to complete the immersion toolbox for a game developer.
In this second part we’re in for a long haul as we finish adding sound FX. But how do we play a sound programmatically? Your job as a software engineer is usually a task of going from one unknown thing to the next, so the single best skillset after being a good thinker is to be a good question asker. Hit Google with a well thought-out query and your a link away from finding your answer 98% of the time. Always favor documentation over a tutorial and absolutely steer away from videos — they take to long.
My search on “how to play sounds in Unity” lead me to the offical docs: https://docs.unity3d.com/ScriptReference/AudioSource.Play.html. Let me take a quick second to explain documentation. Rarely is it ever enough, usually be design so as not to complicate the idea trying to be communicated, and this documentation is a prime example. So don’t take documentation as “ready for production”!
This documentation absolutely shows us how to play a sound, however it’s not ready to ship as is. You’ve always got to “Null check” your game references — what would happen if a game designer didn’t assign an audio clip with the code above? You’d get a runtime error and a fustrated game player.
I’ve set my AudioSource as a private variable in my Player.cs since the only person (or Game Object) that plays this sound is the player when they fire. In the Unity Editor I assign my audio clip source:
I first created a new Empty Game Object by right-clicking (on windows) on the Audio_Manager
GameObject and named the new item Laser_Audio
, then “Add Component” and finally I clicked and dragged my audio source for the “laser_shot” audio file to the AudioClip
property.
Back in the Player.cs file in the void Start()
method I wire up my reference to the _laserAudio
Here you’ll notice that I’m doing nearly the exact same thing as the Unity Doc outlines but I’m doing that “Null Check” with the:if(_laserAudio == null)
And if it is then I’m writing an error log message. For production you could (should) have a sensible default to fall back on — or if in your CI/CD process you have rules that new error logs are to be written to then you can hault the rollout of the new version of the game since it contains a bug that needs to be fixed — we’ll talk about publishing our game soon!
We want to play the _laserAudio
when the user fires their ship’s laser and we want this to play after the laser GameObject is instantiated in game, so on line #160
we have our _laserAudio.Play()
call to play our sound file. Pretty simple! Player laser sounds — ✔!
For the other sound FX in the game, we’ve got power-up sounds and explosions that are shared with the Enemy
and Asteroid
GameObjects. For those I went with the idea of an AudioManager
so those GameObjects could play the appropriate sounds without having to duplicate code — a principle known as DRY: Don’t Repeate Yourself.
First, we need to add this to the GameObject hierachy by right-clicking (on windows) the Audio_Manager
and adding two new Empty GameObjects: Explosion_Audio
, PowerUp_Audio
and then add Audio Source component and finally clicking and dragging the appropriate sound file to each of the Audio_Clip
properties. With these GameObjects all set, lets get to what the AudioManager
is going to look like.
Under the “Scripts” Assets for my game, I right-clicked (on windows) and created a new C# Script and named it AudioManager
. With that named, I clicked and dragged it to my Audio_Manager
in the game Hierarchy. With that setup out of the way, lets look at the code in AudioManager.cs
:
First, I setup my AudioSource
script references that we’ll use later to Play()
our sounds from. Within the Start()
is where we get the GameObject references and do our duty to “Null Check” them. Now we’re in a position to write some public
methods that other GameObjects can call, namely the public void PlayExplosion()
and public void PlayPowerUp()
.
Let’s check out how we wire up the explosions in the Enemy.cs
script. We’re getting into some pretty much rinse and repeat code to get a GameObject reference and “Null Check” it within the Start()
method:
The real action is within the Colision Detection to see if the Enemy is destroyed or not and if so, then run through the DestructionSequence()
:
Here we see that if the Enemy is _destroyed
then we execute a series of steps and one of the last steps is our call to our _audioManager.PlayExplosion()
method. And now, we have enemies who not only die in a fireball but we have an explosion with it!
The steps to add the explosion sound FX for the Asteroids is nearly identical so we’ll not run through all that again. But what about the collecting of power-ups and playing their sounds? It’s nearly all identical code too:
Define the variable for the AudioManager
, “Null Check” it and then call the _audioManager.PlayPowerUp()
when the player’s ship triggers a collision.
As you can see, it’s pretty easy to add sound FX in game — makes sense that it would be since without sounds games would just lack the immersion needed to keep things interesting and Unity (as well as pretty much every other Game Engine out there) has you covered!
I hope you enjoyed this write-up on how to add sounds to your game, how to add them with reusable components to keep things DRY for better software engineering. Let me know what you think and links to your code on how you might go about this in your own games. As always, if you want to play my game, you can do so on the web here: https://joseph-youngquist.github.io/space-shooter-pro/
Coming up Next
I’m going to get back to something I touched on at the very beginning of my learning Game Development with Unity but this time focusing on the settings to Build your game with Unity.