Exercise: the arena takes shape
Assets/
Scenes/
Arena.unity
Scripts/
Prefabs/
you already havethis lesson addsthis lesson retires
Build the arena the rest of the course takes place in. One scene called Arena: a floor, a grid of blocks generated by code — the blocks you will be jumping between in tab 02 — three characters you can swap between, and a small on-screen readout.
what you reuse
This is what the finished scene should look and behave like.
What to build, step by step
Part 1 — the stage
A new scene named Arena, saved in Assets/Scenes. Every lesson from here to the end happens in this one file — the only other scene you ever make is the intro screen.
A Plane floor at 0,0,0 scaled to 3,1,3 with your own dark material.
Camera at roughly 0, 14, -16 rotated 38, 0, 0 so the whole floor is in frame.
Directional Light rotated 50, -30, 0 with soft shadows.
Part 2 — the block grid
Write one script that spawns the grid. Do not place a single cube by hand — if you find yourself dragging cubes, go back and fix the loop.
using System.Collections.Generic;
using UnityEngine;
public class ArenaBuilder : MonoBehaviour
{
[Header("Grid")]
[SerializeField] private GameObject blockPrefab;
[SerializeField] private int columns = 4;
[SerializeField] private int rows = 4;
[SerializeField] private float spacing = 1.6f;
[SerializeField] private Transform blockParent;
// every block we made, so we can count and clear them
private readonly List<GameObject> blocks = new List<GameObject>();
public int BlockCount => blocks.Count;
void Start()
{
Build();
}
void Update()
{
// press R to rebuild with fresh random colours
if (Input.GetKeyDown(KeyCode.R))
Build();
}
public void Build()
{
Clear();
float offsetX = (columns - 1) * spacing * 0.5f;
float offsetZ = (rows - 1) * spacing * 0.5f;
for (int x = 0; x < columns; x++)
{
for (int z = 0; z < rows; z++)
{
Vector3 pos = new Vector3(
x * spacing - offsetX,
0.5f,
z * spacing - offsetZ);
GameObject b = Instantiate(blockPrefab, pos, Quaternion.identity, blockParent);
b.name = $"Block_{x}_{z}";
// TODO 1: give it a random colour
// TODO 2: give it a random height between 0.5 and 2.5
blocks.Add(b);
}
}
}
public void Clear()
{
foreach (GameObject b in blocks)
if (b != null) Destroy(b);
blocks.Clear();
}
}Part 3 — three characters
Three capsules at Y = 1, in three different colours, all children of one empty called Characters.
SimpleMover on all three, CharacterSwitcher on a GameManager, keys 1 / 2 / 3 and Tab.
The camera follows whichever character is currently active.
Part 4 — the readout
A tiny HUD proves your scripts can talk to each other. This is the first time two of your own scripts read from a third.
using UnityEngine;
public class ArenaHud : MonoBehaviour
{
[SerializeField] private ArenaBuilder builder;
[SerializeField] private CharacterSwitcher switcher;
// OnGUI is the quick-and-dirty way to draw text. Fine for an exercise,
// never ship it — real UI lives on a Canvas, like the HudPanel from the last lesson.
void OnGUI()
{
GUI.skin.label.fontSize = 20;
GUI.Label(new Rect(20, 20, 400, 30), "Blocks: " + builder.BlockCount);
GUI.Label(new Rect(20, 50, 400, 30), "Active: " + switcher.ActiveCharacter.name);
GUI.Label(new Rect(20, 80, 400, 30), "R = rebuild");
}
}Done when
- ✓Pressing Play shows a floor with 16 blocks of random colour and height, none of them placed by hand.
- ✓Pressing R rebuilds the grid with new random values and the count stays correct.
- ✓Keys 1, 2, 3 hand control to a different character; only one moves at a time.
- ✓The camera follows the active character smoothly, with no jitter.
- ✓The HUD shows the live block count and the active character's name.
- ✓The Console is empty — no errors, no warnings from your own scripts.
- ✓Changing columns, rows or spacing in the Inspector changes the result without touching code.
If you want more
Make the blocks fall in with physics: add a Rigidbody and spawn them 5 m up.
Colour the blocks by distance from the centre instead of randomly.
Add a fourth character and make the switcher handle any list length automatically.
Let the active character push blocks: give the blocks a Rigidbody, move the character with a CharacterController, and push them from OnControllerColliderHit.