You'll need a set of audio clips for this tutorial: a background music loop, a collectible pickup sound, a spawn sound, and a game-over jingle. We'll get these from the Unity Asset Store.
- Create a new 3D (Built-in Render Pipeline) project named SoundArena. Alternatively, create a new Scene in an existing project.
- Open the Asset Store in your browser at assetstore.unity.com. Log in with your Unity account.
- Search for a free sound effects pack. Suggested searches:
- "Free Sound Effects Pack" by Olivier Girardot
- "FREE Casual Game SFX Pack" by Dustyroom
- "Universal Sound FX" (free version) by Imphenzia
- Any free pack that includes: pickup/coin, impact/spawn, and a short music loop
- Click Add to My Assets, then open Unity and go to Window → Package Manager. Switch the dropdown to My Assets, find your pack, and click Download then Import.
- After import, browse the audio files in your Project window. Take note of which clips you'll use for:
- 🎵 Music — a looping track (any calm or upbeat loop)
- ✨ Pickup — a short chime or coin sound
- 👾 Spawn — a whoosh, pop, or impact
- 🏁 Game Over — a jingle, fail sound, or descending tone
- Audio pack imported into Unity
- Identified clips for music, pickup, spawn, and game over
Build the gameplay scene — a ground plane with a player, collectibles, and a spawner. This reuses what you built in Week 10's micro-tasks.
- Create a Plane at (0, 0, 0). Scale it to (2, 1, 2) so the arena is larger.
- Create a Cube at (0, 0.5, 0). Name it Player. Give it a blue Material. Add a Rigidbody component.
- Create a Sphere at (3, 0.5, 3). Name it Collectible. Scale to (0.5, 0.5, 0.5). Give it a yellow Material.
- On the Sphere's Collider, tick Is Trigger.
- Add the tag Collectible in Edit → Project Settings → Tags and Layers. Assign it to the Sphere.
- Drag the Sphere from Hierarchy into the Project window to create a Prefab. Delete the original from the scene.
- Create a Capsule. Give it a red Material. Create a Prefab from it. Delete the original. This is the enemy Prefab.
- Create an empty GameObject named Spawner at (0, 0, 0).
- Create an empty GameObject named GameManager.
- Add UI text elements: create GameObject → UI → Text - TextMeshPro twice — one for Score (top-left), one for Timer (top-right). Set default text to "Score: 0" and "30" respectively. If prompted, import TMP Essentials.
- Create a third TextMeshPro text centred on screen with text "Time's Up!", font size 60. Disable this GameObject (uncheck its checkbox).
- Ground plane, Player cube with Rigidbody
- Collectible Prefab (trigger, tagged)
- Enemy Prefab (red capsule)
- UI: Score text, Timer text, Game Over text (disabled)
- Main Camera has AudioListener
Background music plays continuously from the moment the scene loads. It should loop, be 2D (heard at the same volume everywhere), and be quieter than sound effects so it doesn't drown them out.
- Create an empty GameObject named BackgroundMusic.
- Add an AudioSource component to it (Add Component → Audio → Audio Source).
- Drag your music AudioClip from the Project window into the AudioClip field on the AudioSource.
- Configure the AudioSource:
- Play On Awake: ✅ Checked
- Loop: ✅ Checked
- Volume: 0.3 (keep it subtle)
- Spatial Blend: 0 (fully 2D — heard everywhere equally)
- Press Play and verify the music starts immediately and loops continuously.
- Music plays when scene starts
- Music loops without gaps
- Volume is subtle (not overpowering)
The player moves with WASD using physics. We'll also add an AudioSource to the player so it can play pickup and other sounds triggered by gameplay events.
- Add an AudioSource component to the Player object.
- On this AudioSource, uncheck Play On Awake (we'll trigger sounds from scripts). Set Spatial Blend to 0 (2D).
- Create a new C# script called PlayerMovement and attach it to the Player.
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
[SerializeField] private float speed = 6f;
private Rigidbody rb;
void Awake()
{
rb = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(h, 0f, v);
rb.MovePosition(rb.position + movement * speed * Time.fixedDeltaTime);
}
}- Player moves with WASD/arrow keys
- Player has an AudioSource (Play On Awake off)
When the player picks up a collectible, a sound should play. We use PlayOneShot() because it allows multiple pickup sounds to overlap if the player collects rapidly.
- Create a new C# script called PlayerCollector and attach it to the Player.
- In the Inspector, drag your pickup AudioClip into the pickupSound field.
- Drag the Score TextMeshPro object into the scoreText field.
- Manually place 5–6 instances of the Collectible Prefab around the ground plane so you can test.
using TMPro;
using UnityEngine;
public class PlayerCollector : MonoBehaviour
{
[SerializeField] private AudioClip pickupSound;
[SerializeField] private TMP_Text scoreText;
private AudioSource audioSource;
private int score = 0;
void Awake()
{
audioSource = GetComponent<AudioSource>();
}
void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Collectible"))
{
score++;
scoreText.text = "Score: " + score;
// Play the sound on the player's AudioSource
audioSource.PlayOneShot(pickupSound);
// Destroy the collectible object
Destroy(other.gameObject);
}
}
}- Collecting a sphere plays the pickup sound
- Score updates on screen
- Rapid pickups overlap sounds correctly
Enemies spawn every 2 seconds at random positions. Each spawn plays a sound at the spawn location using AudioSource.PlayClipAtPoint() — a static method that creates a temporary AudioSource, plays the clip, then cleans itself up.
- Create a new C# script called EnemySpawner and attach it to the Spawner object.
- Drag the enemy Prefab into the enemyPrefab field in the Inspector.
- Drag your spawn AudioClip into the spawnSound field.
using System.Collections;
using UnityEngine;
public class EnemySpawner : MonoBehaviour
{
[SerializeField] private GameObject enemyPrefab;
[SerializeField] private AudioClip spawnSound;
[SerializeField] private float spawnInterval = 2f;
[SerializeField] private float spawnRange = 8f;
void Start()
{
StartCoroutine(SpawnLoop());
}
IEnumerator SpawnLoop()
{
while (true)
{
float x = Random.Range(-spawnRange, spawnRange);
float z = Random.Range(-spawnRange, spawnRange);
Vector3 spawnPos = new Vector3(x, 1f, z);
Instantiate(enemyPrefab, spawnPos, Quaternion.identity);
// Play sound at the spawn position
AudioSource.PlayClipAtPoint(spawnSound, spawnPos);
yield return new WaitForSeconds(spawnInterval);
}
}
}- Enemies spawn every 2 seconds at random positions
- A sound plays at each spawn location
A 30-second countdown timer ticks down. When it hits zero, the game freezes, background music stops, and a game-over sound plays. This script controls the overall game state.
- Create a new C# script called GameTimer and attach it to GameManager.
- Add an AudioSource component to GameManager. Uncheck Play On Awake.
- In the Inspector, assign:
- timerText — the Timer TMP text object
- gameOverUI — the "Time's Up!" text GameObject
- gameOverSound — your game-over AudioClip
- backgroundMusic — the BackgroundMusic AudioSource (drag the BackgroundMusic object)
using TMPro;
using UnityEngine;
public class GameTimer : MonoBehaviour
{
[SerializeField] private TMP_Text timerText;
[SerializeField] private GameObject gameOverUI;
[SerializeField] private AudioClip gameOverSound;
[SerializeField] private AudioSource backgroundMusic;
[SerializeField] private float startTime = 30f;
private AudioSource audioSource;
private float timeRemaining;
private bool isGameOver = false;
void Awake()
{
audioSource = GetComponent<AudioSource>();
}
void Start()
{
timeRemaining = startTime;
gameOverUI.SetActive(false);
Time.timeScale = 1f; // Reset in case it was frozen
}
void Update()
{
if (isGameOver) return;
timeRemaining -= Time.deltaTime;
if (timeRemaining <= 0f)
{
timeRemaining = 0f;
GameOver();
}
timerText.text = Mathf.CeilToInt(timeRemaining).ToString();
}
void GameOver()
{
isGameOver = true;
gameOverUI.SetActive(true);
// Stop the background music
backgroundMusic.Stop();
// Play the game-over jingle
audioSource.PlayOneShot(gameOverSound);
// Freeze the game after a short delay
// (so the game-over sound can still play)
Invoke("FreezeGame", 0.1f);
}
void FreezeGame()
{
Time.timeScale = 0f;
}
}- Timer counts down from 30
- "Time's Up!" appears at zero
- Background music stops on game over
- Game over sound plays
- Game freezes after the sound starts
Right now all sounds are 2D — they sound the same regardless of where the player is. Let's make the collectibles emit a subtle spatial hum so the player can hear them in 3D space.
- Open the Collectible Prefab (double-click it in the Project window).
- Add an AudioSource component to the Prefab.
- Assign a looping ambient sound (a hum, sparkle, or gentle tone) to the AudioClip field. If your audio pack doesn't have one, any short sound will do.
- Configure the AudioSource:
- Play On Awake: ✅ Checked
- Loop: ✅ Checked
- Volume: 0.5
- Spatial Blend: 1 (fully 3D)
- Expand the 3D Sound Settings section at the bottom of the AudioSource:
- Min Distance: 1 (full volume within 1 unit)
- Max Distance: 15 (silent beyond 15 units)
- Volume Rolloff: Logarithmic (default — sounds natural)
- Save the Prefab and return to the scene.
- Place 5–6 collectibles around the arena at various distances from the player start position.
- Press Play and walk around — you should hear collectibles get louder as you approach and quieter as you move away. The sound should also pan left/right based on the collectible's position relative to the camera.
- Collectibles hum/glow audibly when nearby
- Sound fades with distance
- Sound pans left/right based on position
Run through the complete game and verify every audio element works together.
- Background music starts immediately and loops
- Player moves with WASD
- Collecting a sphere plays a pickup sound and updates the score
- Rapid pickups produce overlapping sounds (not cutting each other off)
- Enemies spawn every 2 seconds with a spawn sound
- Collectible hum is louder when nearby and quieter when far away
- Timer counts down and freezes the game at 0
- Background music stops on game over
- Game-over sound plays before the game freezes
- No console errors or warnings about multiple AudioListeners