MISSION 14: Sound & Particles

Add audio effects, background music, and particle systems to bring your game to life

Lesson 14 of 22 75 min session 50 XP available Week 9 — Wed
XP EARNED: 0 / 50 XP
1
Audio Basics in Unity
~12 min
+10 XP

Key Vocabulary

AudioSource
A component that plays audio clips — like a speaker attached to a GameObject
AudioClip
An imported sound file (WAV, MP3, OGG) stored in your Assets
AudioListener
The "ear" of the game — usually on the Main Camera. Only one per scene!
SFX
Sound Effects — short sounds triggered by events (jump, collect, hit)
BGM
Background Music — longer tracks that loop continuously during gameplay
1.1
Download free sound effects. Visit one of these free sound sites:
  • freesound.org — huge library of CC0 sounds (requires free account)
  • kenney.nl/assets → search "Audio" — game-ready sound packs, no account needed
  • opengameart.org → browse "Music" and "Sound Effect" categories
  • Teacher resource pack — check Google Classroom for a .zip of curated sounds
Tip: Download at least 4 sounds — a jump sound, a coin/collect sound, a damage/hit sound, and a background music track. WAV or OGG format is best for Unity.
1.2
Create an Audio folder. In your Project window, right-click in Assets → Create → Folder → name it "Audio". Inside Audio, create two sub-folders: "SFX" and "Music".
Folder structure: Assets/Audio/SFX/ (for short sounds) and Assets/Audio/Music/ (for background tracks). Staying organised is a professional habit!
1.3
Import your sounds. Drag your downloaded audio files from File Explorer into the appropriate folder (SFX or Music) in the Project window. Unity will import them as AudioClip assets.
Check import settings: Click on an imported audio file. In the Inspector you'll see options like Load Type (Decompress On Load for SFX, Streaming for music) and Compression Format (Vorbis is a good default). For now, leave these as default.
1.4
Understand the Audio pipeline. Unity audio works like this:
  • AudioClip = the sound file (like a CD)
  • AudioSource = the player/speaker (plays the clip)
  • AudioListener = the ear (on the Camera — already there by default!)
Important: You can have many AudioSources in a scene, but only ONE AudioListener. Unity will warn you if you accidentally add two. The AudioListener is already on your Main Camera — don't add another one.
1.5
Add background music. Create an empty GameObject: Ctrl+Shift+N → name it "BGM". In the Inspector, click Add Component → Audio Source.
  • Drag your music AudioClip into the AudioClip field
  • Tick Play On Awake ✓ (starts when scene loads)
  • Tick Loop ✓ (music repeats forever)
  • Set Volume to about 0.3 (30% — background music should be subtle)
Test it: Press Play. You should hear your background music. If it's too loud, lower the Volume slider. If you hear nothing, check that the AudioClip is assigned and the AudioListener exists on the Camera.
1.6
Preview sounds in the Editor. You can listen to any AudioClip without pressing Play: click the audio file in the Project window, then click the Play button in the Inspector preview at the bottom. The waveform display shows you the sound's shape.
Checkpoint: You have an Audio folder with SFX and Music sub-folders, background music is playing when you press Play, and you know what AudioSource, AudioClip, and AudioListener do.
2
Playing Sound Effects with Code
~15 min
+10 XP
2.1
Add an AudioSource to your Player. Select your Player GameObject in the Hierarchy. Click Add Component → Audio Source. This time:
  • Uncheck Play On Awake (we don't want it playing automatically)
  • Uncheck Loop (SFX should play once)
  • Leave the AudioClip field empty — we'll assign clips through code
2.2
Open your PlayerController script. We need to add variables for our sound clips and a reference to the AudioSource. Add these variables at the top of your class:
// --- Audio --- public AudioClip jumpSound; public AudioClip collectSound; public AudioClip hurtSound; private AudioSource audioSource;
2.3
Get the AudioSource in Start(). Add this line inside your Start() method:
void Start() { // ... your existing Start code ... audioSource = GetComponent<AudioSource>(); }
Why GetComponent? This finds the AudioSource we just added to the Player GameObject and stores a reference so we can use it in our code. It's the same pattern we used for Rigidbody2D!
2.4
Play the jump sound. Find the part of your code where the player jumps (where you apply force/velocity). Add this line right after the jump:
// Inside your jump logic, after applying force: if (jumpSound != null) { audioSource.PlayOneShot(jumpSound); }
PlayOneShot vs Play: PlayOneShot(clip) lets you play a sound without interrupting other sounds on the same AudioSource. Play() stops the current sound first. Always use PlayOneShot for SFX so sounds can overlap!
2.5
Play the collect sound. Find your OnTriggerEnter2D method where you collect coins. Add the sound before or after Destroy(other.gameObject):
void OnTriggerEnter2D(Collider2D other) { if (other.CompareTag("Coin")) { score++; if (collectSound != null) { audioSource.PlayOneShot(collectSound); } Destroy(other.gameObject); } }
2.6
Play the hurt sound. Find where you handle damage (enemy collision or hazard). Add the hurt sound:
// Inside enemy/hazard collision logic: if (hurtSound != null) { audioSource.PlayOneShot(hurtSound); }
2.7
Assign the clips in the Inspector. Save your script (Ctrl+S). Go back to Unity. Select the Player. In the Inspector, you'll see three new fields: Jump Sound, Collect Sound, Hurt Sound. Drag the appropriate AudioClip from your Audio/SFX folder into each field.
2.8
Test all three sounds. Press Ctrl+P to play. Jump — you should hear the jump sound. Collect a coin — collect sound. Touch an enemy — hurt sound. If a sound is too loud/quiet, select the AudioClip and adjust the volume in code: audioSource.PlayOneShot(jumpSound, 0.7f) (the second parameter is volume 0-1).
No sound? Check: (1) AudioClip is assigned in Inspector, (2) AudioSource exists on Player, (3) Play On Awake is unchecked, (4) null check is correct (jumpSound != null), (5) Computer volume is up, (6) AudioListener exists on Camera.
Checkpoint: Your player makes sounds when jumping, collecting coins, and taking damage. You understand PlayOneShot and how AudioClip variables work.
3
SoundManager — Centralised Audio
~15 min
+10 XP
3.1
Why a SoundManager? Right now, only the Player can play sounds. But what if an enemy explodes? Or a coin sparkles? We need a central sound system that any script can access.
Design Pattern — Singleton: A SoundManager uses the Singleton pattern: only one instance exists in the entire game, and any script can access it through SoundManager.Instance. This is the same pattern used in professional games!
3.2
Create the SoundManager script. In Project window: right-click Scripts folder → Create → C# Script → name it "SoundManager". Double-click to open and replace ALL the code with:
using UnityEngine; public class SoundManager : MonoBehaviour { // --- Singleton --- public static SoundManager Instance; // --- Audio Sources --- public AudioSource sfxSource; // for sound effects public AudioSource musicSource; // for background music // --- Sound Clips --- public AudioClip jumpClip; public AudioClip collectClip; public AudioClip hurtClip; public AudioClip gameOverClip; public AudioClip buttonClip; void Awake() { // Singleton: if one already exists, destroy this copy if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; DontDestroyOnLoad(gameObject); } public void PlaySFX(AudioClip clip, float volume = 1f) { if (clip != null) { sfxSource.PlayOneShot(clip, volume); } } public void PlayJump() { PlaySFX(jumpClip); } public void PlayCollect() { PlaySFX(collectClip); } public void PlayHurt() { PlaySFX(hurtClip, 0.8f); } public void PlayGameOver() { PlaySFX(gameOverClip); } public void PlayButton() { PlaySFX(buttonClip, 0.5f); } public void SetMusicVolume(float vol) { musicSource.volume = vol; } public void ToggleMusic() { musicSource.mute = !musicSource.mute; } }
3.3
Set up the SoundManager GameObject. In Hierarchy:
  • Create an empty GameObject → name it "SoundManager"
  • Add the SoundManager script component
  • Add TWO AudioSource components (click Add Component twice)
  • First AudioSource: leave as default (this will be sfxSource)
  • Second AudioSource: set Play On Awake ✓, Loop ✓, assign your music clip, volume 0.3
3.4
Wire up the Inspector. Select the SoundManager GameObject. In the script component:
  • Drag the first AudioSource into Sfx Source
  • Drag the second AudioSource into Music Source
  • Drag each sound clip from Audio/SFX into the matching clip field
Tip: You can drag components from the same GameObject. Click the little circle icon next to the field to see a picker, or drag directly from the Inspector header.
3.5
Use the SoundManager from any script. Now update your PlayerController. Replace the old direct AudioSource calls with:
// OLD way (remove these): // audioSource.PlayOneShot(jumpSound); // NEW way — call the SoundManager from anywhere: SoundManager.Instance.PlayJump(); SoundManager.Instance.PlayCollect(); SoundManager.Instance.PlayHurt();
Why is this better? (1) Any script can play sounds — enemies, pickups, UI buttons. (2) All sound clips are managed in ONE place. (3) You can add volume controls and mute functionality easily. (4) DontDestroyOnLoad means music persists between scenes!
3.6
Delete the old BGM GameObject. Since the SoundManager now handles music, delete the old "BGM" GameObject from Section 1 (right-click → Delete). The SoundManager replaces it.
💡
Pro tip: You can remove the AudioSource and AudioClip variables from your PlayerController now if you want — the SoundManager handles everything. Keep your player script clean!
Checkpoint: SoundManager is set up with Singleton pattern, any script can call SoundManager.Instance.PlayJump() etc, and music plays through the centralised system.
4
Particle Systems — Visual Effects
~20 min
+10 XP

Key Vocabulary

Particle System
A component that emits many small sprites/meshes to simulate effects like fire, smoke, sparks, and explosions
Emission
How many particles are spawned per second — higher = more dense effect
Lifetime
How long each particle exists before disappearing
Start Speed
How fast particles move when they are born
Simulation Space
World (particles stay in place) vs Local (particles move with the parent object)
4.1
Create a Dust particle effect. In the Hierarchy: right-click → Effects → Particle System. Name it "DustParticles". You'll immediately see pink/magenta squares shooting upward — that's the default particle system!
Why pink squares? The default material has no texture assigned. We'll fix this shortly. The magenta colour is Unity's way of saying "missing texture".
4.2
Configure the main module. Select DustParticles. In the Inspector, the Particle System has many expandable modules. Start with the main module (the one at the very top):
  • Duration: 1.0 (how long the system runs in seconds)
  • Looping: Uncheck ✗ (dust puffs should play once, not loop)
  • Start Lifetime: 0.5 (particles vanish after 0.5 seconds)
  • Start Speed: 2 (how fast particles shoot out)
  • Start Size: 0.2 (small particles for dust)
  • Start Color: Click and choose a sandy/brown colour
  • Simulation Space: World (particles stay where emitted, not follow the player)
  • Play On Awake: Uncheck ✗ (we'll trigger it from code)
4.3
Configure Emission. Click the Emission module to expand it:
  • Rate over Time: 0 (we don't want continuous emission)
  • Click the + under Bursts to add a burst
  • Time: 0, Count: 15, Cycles: 1
Rate vs Burst: "Rate over Time" emits particles continuously (good for smoke, fire). "Bursts" emit a bunch of particles all at once at a specific time (good for explosions, dust puffs, impacts).
4.4
Configure Shape. Click the Shape module:
  • Shape: Circle (particles emit from a circle — perfect for landing dust)
  • Radius: 0.3
4.5
Add Size over Lifetime. Check the Size over Lifetime module (click the checkbox to enable it). Click the curve graph. Choose the downward curve preset (top-right of the curve editor). This makes particles shrink as they age — much more natural looking.
4.6
Add Colour over Lifetime. Enable the Color over Lifetime module. Click the gradient bar. Set the right alpha marker (top-right of gradient) to 0. This makes particles fade out as they age — they become transparent before disappearing.
4.7
Fix the material. Expand the Renderer module at the bottom. Change Material to Default-Particle (click the circle icon and search "Default-Particle"). The pink squares should now be soft white circles.
Alternative: For 2D games, you can also use Sprites-Default material, which renders particles as flat sprites instead of 3D billboards.
4.8
Make it a prefab. Drag DustParticles from Hierarchy into your Prefabs folder in the Project window. Now delete it from the Hierarchy — we'll spawn it through code.
4.9
Spawn dust when landing. Open your PlayerController. Add a variable and the spawn logic:
public GameObject dustPrefab; // Call this method when the player lands on the ground void SpawnDust() { if (dustPrefab != null) { GameObject dust = Instantiate(dustPrefab, transform.position, Quaternion.identity); Destroy(dust, 1f); // clean up after 1 second } }
When to call SpawnDust(): Call it in your ground detection code — when the player was in the air (isGrounded was false) and just became grounded (isGrounded is now true). For example, in your ground check logic add: if (!wasGrounded && isGrounded) SpawnDust();
4.10
Assign the prefab. Back in Unity, select your Player. Drag the DustParticles prefab from the Project window into the Dust Prefab field. Press Play and jump — you should see a puff of dust when you land!
Checkpoint: You have a dust particle effect that spawns when the player lands. You understand the main Particle System modules: Emission, Shape, Size over Lifetime, Color over Lifetime, and Renderer.
5
Extension — More Effects & Polish
~13 min
+10 XP
🏆
Challenge time! You've mastered the basics. Now create more particle effects and audio polish to make your game feel professional.
5.1
Coin Collect Sparkle. Create a new Particle System for collecting coins:
  • Looping: Off, Play On Awake: On (it plays immediately when spawned)
  • Start Lifetime: 0.6, Start Speed: 3, Start Size: 0.15
  • Start Color: Gold/Yellow (#FFD700)
  • Emission: Burst of 20 particles
  • Shape: Sphere, Radius: 0.2
  • Size over Lifetime: shrink curve
  • Colour over Lifetime: fade to transparent
  • Renderer: Default-Particle material
Save as prefab "CoinSparkle". Spawn it in your coin collection code: Instantiate(coinSparkle, other.transform.position, Quaternion.identity)
5.2
Enemy Death Explosion. Create a more dramatic particle effect:
  • Start Color: Red/orange gradient
  • Emission: Burst of 30 particles
  • Start Speed: 4-6 (use "Random Between Two Constants")
  • Start Size: 0.1-0.3 (random)
  • Gravity Modifier: 0.5 (particles fall slightly)
Random values: Click the dropdown arrow next to any Particle System value and choose "Random Between Two Constants" to add natural variation. This makes effects look much more organic!
5.3
Ambient particles. Create a subtle continuous particle effect for atmosphere:
  • Looping: On, Play On Awake: On
  • Emission Rate: 5 per second (very sparse)
  • Start Lifetime: 3-5 seconds
  • Start Speed: 0.5, Start Size: 0.05-0.1
  • Shape: Box (wide area, covers the screen)
  • Color: Soft white with low alpha
Place this in your scene as floating dust motes, falling leaves, or drifting snow depending on your game's theme.
5.4
Volume control with a UI slider. Add a Slider to your Canvas (right-click Canvas → UI → Slider). Position it in a corner. Create this method in SoundManager:
// Add to SoundManager.cs public void OnVolumeChanged(float value) { AudioListener.volume = value; // master volume 0-1 }
Then on the Slider's On Value Changed event in the Inspector, drag the SoundManager and select OnVolumeChanged.
5.5
Trail Renderer for movement. Select your Player → Add Component → Trail Renderer. Configure:
  • Width: Curve from 0.2 to 0 (tapers to nothing)
  • Time: 0.3 (trail disappears after 0.3 seconds)
  • Color: Gradient from your accent colour to transparent
  • Material: Sprites-Default
  • Min Vertex Distance: 0.05
Result: Your player leaves a subtle coloured trail when moving. This is a classic "juice" technique used in games like Celeste and Hollow Knight!
🚀
Super Extension: Look up "game juice" or "game feel" on YouTube. Try adding screen shake when the player takes damage (move the camera briefly using Random.insideUnitCircle). Add pitch variation to your SFX: sfxSource.pitch = Random.Range(0.9f, 1.1f) before PlayOneShot to make each sound slightly different.
Checkpoint: You've created at least one additional particle effect (sparkle or explosion) and attempted at least one polish technique (volume slider, trail renderer, or ambient particles).