Week 10 — Micro-Tasks

Solution Scripts

Complete C# scripts for all five micro-tasks, with setup instructions and notes on common mistakes.

1
Player Movement
PlayerMovement.cs
Scene Setup
  1. Create a Plane at (0, 0, 0).
  2. Create a Cube at (0, 0.5, 0).
  3. Add a Rigidbody component to the Cube (Add Component → Physics → Rigidbody).
  4. Create a new Material, pick a colour, and drag it onto the Cube.
  5. Create PlayerMovement.cs and attach it to the Cube.
Script
PlayerMovement.cs
using UnityEngine; public class PlayerMovement : MonoBehaviour { [SerializeField] private float speed = 5f; 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.velocity = movement * speed; } }
Common mistakes Using Update() instead of FixedUpdate() for Rigidbody movement — causes jittery physics. Forgetting to add the Rigidbody component before running — causes NullReferenceException on rb.velocity.
2
Collectible Pickup
PlayerCollector.cs
Scene Setup
  1. Create 4–5 Sphere objects. Scale to (0.5, 0.5, 0.5). Apply a yellow Material.
  2. On each Sphere's Sphere Collider, tick Is Trigger.
  3. Go to Edit → Project Settings → Tags and Layers. Add a new tag: Collectible.
  4. Select all Spheres and set their Tag to Collectible.
  5. Create PlayerCollector.cs and attach it to the player Cube (not the spheres).
Script
PlayerCollector.cs
using UnityEngine; public class PlayerCollector : MonoBehaviour { private int collectCount = 0; void OnTriggerEnter(Collider other) { if (other.CompareTag("Collectible")) { collectCount++; Debug.Log("Collected: " + collectCount); Destroy(other.gameObject); } } }
Common mistakes Writing Destroy(other) instead of Destroy(other.gameObject) — only removes the Collider component, leaving a visible sphere with no collider. Forgetting to tick Is Trigger — fires OnCollisionEnter instead, and the player bounces off. Forgetting to register the tag in Tag Manager — CompareTag throws an exception at runtime.
3
Enemy Spawner
EnemySpawner.cs
Scene Setup
  1. Create a Capsule. Apply a red Material.
  2. Drag the Capsule from Hierarchy into the Project window (Assets) to create a Prefab.
  3. Delete the Capsule from the scene.
  4. Create an empty GameObject named Spawner at (0, 0, 0).
  5. Create EnemySpawner.cs and attach it to Spawner.
  6. Drag the enemy Prefab from the Project window into the enemyPrefab slot in the Inspector.
Script
EnemySpawner.cs
using System.Collections; using UnityEngine; public class EnemySpawner : MonoBehaviour { [SerializeField] private GameObject enemyPrefab; [SerializeField] private float spawnInterval = 2f; [SerializeField] private float spawnRange = 5f; void Start() { StartCoroutine(SpawnLoop()); } IEnumerator SpawnLoop() { while (true) { float x = Random.Range(-spawnRange, spawnRange); float z = Random.Range(-spawnRange, spawnRange); Vector3 pos = new Vector3(x, 1f, z); Instantiate(enemyPrefab, pos, Quaternion.identity); yield return new WaitForSeconds(spawnInterval); } } }
How the coroutine works StartCoroutine() begins running SpawnLoop(). The while(true) loop spawns one enemy, then yield return new WaitForSeconds(2f) pauses only this coroutine for 2 seconds — the rest of the game keeps running. After 2 seconds, the coroutine resumes from where it paused and loops again.
Common mistakes Forgetting using System.Collections; at the top — IEnumerator won't resolve. Forgetting to drag the Prefab into the Inspector slot — enemyPrefab is null and Instantiate throws an error. Using Y = 0 for the spawn position — the capsule spawns halfway through the ground (its pivot is at the centre, so Y should be 1 for a default capsule).
4
UI Score Display
ScoreManager.cs
Scene Setup
  1. Go to GameObject → UI → Text - TextMeshPro. (If prompted, click "Import TMP Essentials".)
  2. Unity auto-creates a Canvas and EventSystem.
  3. Select the Text object. In the RectTransform, click the anchor presets square (hold Alt) and choose top-left.
  4. Set Pos X to 20, Pos Y to -20. Set Width to 300, Height to 50.
  5. In the TextMeshPro component, set the text to Score: 0 and font size to 36.
  6. Create an empty GameObject named GameManager.
  7. Create ScoreManager.cs and attach it to GameManager.
  8. Drag the TextMeshPro object from the Hierarchy into the scoreText slot in the Inspector.
Script
ScoreManager.cs
using TMPro; using UnityEngine; public class ScoreManager : MonoBehaviour { [SerializeField] private TMP_Text scoreText; private int score = 0; void Update() { if (Input.GetKeyDown(KeyCode.Space)) { score++; scoreText.text = "Score: " + score; } } }
Common mistakes Using GetKey instead of GetKeyDown — the score rockets up while Space is held because GetKey returns true every frame. Using Text (legacy) instead of TMP_Text — requires using UnityEngine.UI; instead of using TMPro; and is the older UI component. Forgetting to drag the text object into the Inspector — NullReferenceException on first Space press.
5
Countdown Timer + Game Over
GameTimer.cs
Scene Setup
  1. Create a UI → Text - TextMeshPro for the timer. Anchor to top-right. Set Pos X to -20, Pos Y to -20. Font size 36. Default text: 30.
  2. Create another UI → Text - TextMeshPro for the game over message. Anchor to centre. Font size 64. Set text to Time's Up!. Set alignment to centre.
  3. Disable the Game Over text object by unchecking its checkbox in the Inspector.
  4. Create GameTimer.cs and attach it to the GameManager object (or any object).
  5. Drag the timer text into the timerText slot and the Game Over GameObject into the gameOverUI slot in the Inspector.
Script
GameTimer.cs
using TMPro; using UnityEngine; public class GameTimer : MonoBehaviour { [SerializeField] private TMP_Text timerText; [SerializeField] private GameObject gameOverUI; [SerializeField] private float startTime = 30f; private float timeRemaining; private bool isGameOver = false; void Start() { timeRemaining = startTime; gameOverUI.SetActive(false); } void Update() { if (isGameOver) return; timeRemaining -= Time.deltaTime; if (timeRemaining <= 0f) { timeRemaining = 0f; isGameOver = true; gameOverUI.SetActive(true); Time.timeScale = 0f; } int display = Mathf.CeilToInt(timeRemaining); timerText.text = display.ToString(); } }
Common mistakes Forgetting to set the Game Over text to disabled before pressing Play — it's visible from the start. Using (int)timeRemaining instead of Mathf.CeilToInt() — truncation means the display jumps from 1 to 0 too early. Forgetting that Time.timeScale = 0 persists between Play sessions in the Editor — if you stop and re-enter Play mode, everything may still be frozen. Reset it in Start() to be safe.
Why Time.timeScale? Setting Time.timeScale = 0f freezes all time-dependent systems: Update() still runs but Time.deltaTime becomes 0, physics stop, coroutines using WaitForSeconds pause, and animations freeze. This is the simplest way to implement a global game pause or game-over state.