W1D5: Scenes and 2D animation
Scenes, scene loading (additive vs single, sync vs async), and building a 2D sprite animation in Unity's Animator.
Lucian Lazar · 13 Jul 2024
Last session of week one. It splits cleanly in two: the first half is about scenes — what they actually are, when to make a new one, and how to load and unload them — and the second half is hands-on 2D sprite animation, ending with a character that jumps when you hit space. If you’ve been following along since day one, this is where the editor work starts feeling like game development rather than tooling setup.
What’s covered
Scenes, properly explained
- A scene as a container for everything in a level, a map, a room, a menu screen
- Why an open world can’t live in one scene, and how tiled scenes get loaded around the player
- The one-scene-per-level approach we use for the course project, and why that’s fine here
- The alternative some games use: a single scene where you create and destroy everything by hand. Lucian’s take is that Unity is built around the assumption you won’t do this, and going against that means tracking every object yourself so no ghosts from the last level hang around
- Scenes vs levels: Unity’s own API uses the words interchangeably because that’s historically what scenes were for
A detour into visibility and MonoBehaviour messages
A question from chat about hiding off-screen objects turns into a live experiment with OnBecameVisible and OnBecameInvisible. It half works. The visible callback fires, the invisible one doesn’t, and it fires repeatedly for no obvious reason. Lucian’s verdict: these callbacks are buggy across Unity versions and he avoids relying on anything beyond the common ones. There’s also the reminder that premature optimisation is a trap — get a game that runs before you start hiding cubes.
Along the way you get a tour of the MonoBehaviour message list: OnCollisionEnter / Stay / Exit, OnMouseOver, and the dozens of others in the docs.
World-space UI, briefly
Looking at a demo character’s health bar turns into a live build of a world-space Canvas with a Slider on it. Set the render mode, assign a camera, work with scale rather than tiny width and height values, strip the handle off the slider, recolour the fill. It’s a preview of UI week rather than a full treatment, and Lucian admits he’d done it a harder way in his own project.
Scene management APIs
SceneManager.LoadScene— blocking, everything stops while it loadsSceneManager.LoadSceneAsync— returns anAsyncOperationyou poll each frame forprogress, which is how you drive a loading barLoadSceneMode.SinglevsLoadSceneMode.Additive— replace the current scene or stack another on top, which is how you do pause menus, overlays and chat windows- Scenes must be in File → Build Settings, and load by scene name, not file path. This gets debugged live when the first attempt fails
DontDestroyOnLoadand the phantom scene Unity creates for it, so your scene manager survives a level change
2D sprite animation
- Sprites as a sequence of frames, and the sprite-sheet alternative via Sprite Mode → Multiple
- The split between an Animator Controller (
.controller, a finite state machine) and an Animation (.anim, one clip) - Creating the player object, adding Sprite Renderer and Animator
- Making two clips, dragging them into the Animator to auto-create states, setting the default state
- Trigger parameters and transition conditions
- Has Exit Time explained properly: why you uncheck it going into the jump (you want an instant response) and keep it coming back out (you want the jump to finish)
- Dragging sprite frames onto the animation timeline, spacing keys to control speed, ticking Loop Time
- Record mode: animating the Y position so the jump moves the character, then returning to zero on the last frame
Timestamps
- 00:14:00 — Housekeeping: picking one of the three course projects (magic jewelry, 3D sniper shooter, 2D platformer) and logging it on the spreadsheet
- 00:24:00 — Cloning the course repo with SmartGit and adding it to Unity Hub
- 00:31:00 — What a scene is, and scenes as level tiles in large or open-world games
- 00:36:00 — Single-scene levels, memory, battery and FPS implications for a platformer
- 00:42:00 — Multiple scenes for menus and levels, and why manual object cleanup is the wrong default in Unity
- 00:47:00 — Live test of
OnBecameVisible/OnBecameInvisible, and why it doesn’t behave - 00:55:00 — Premature optimisation, and the MonoBehaviour message list (collision, mouse, lifecycle)
- 01:04:00 — Touring a demo scene: object hierarchies, parent objects for organisation, cinemachine camera
- 01:09:00 — Building a world-space Canvas health bar with a Slider
- 01:20:00 — Scene management: reloading a level by unloading and loading rather than tracking objects
- 01:25:00 — Additive vs single load mode, overlays, pause menus and chat scenes
- 01:31:00 —
LoadScenevsLoadSceneAsync, polling progress, and adding scenes to Build Settings - 01:39:00 —
DontDestroyOnLoadand building a persistent scene manager object - 01:46:00 — Testing the async load live and reading the console output
- 01:51:00 — Animator vs Animation: state machines,
.controllerand.animfiles - 01:58:00 — Importing sprites, setting texture type to Sprite, folder structure for the player
- 02:03:00 — Creating the Animator Controller, states, default state and transitions
- 02:08:00 — Jump trigger parameter, Has Exit Time, normalized exit time and transition blending
- 02:14:00 — Recording the Y position keyframes for the jump, and the space-bar script
What you build
A player GameObject with a Sprite Renderer and an Animator, driven by a two-state controller: idle loops forever, jump fires on a trigger and returns automatically. The jump clip animates both the sprite frames and the transform’s Y position, so the character actually leaves the ground instead of flailing in place. A small script wires the space bar to the trigger.
There’s also a throwaway scene manager built during the scene half — a persistent object that loads a scene asynchronously on a button click and logs progress each frame.
public class PlayerJump : MonoBehaviour
{
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
GetComponent<Animator>().SetTrigger("jump");
}
}
}
The async load pattern, stripped down to what was actually typed:
```csharp
private AsyncOperation loadOp;
void StartLoading()
{
Debug.Log("loading scene");
loadOp = SceneManager.LoadSceneAsync("W2", LoadSceneMode.Single);
}
void Update()
{
if (loadOp != null)
{
Debug.Log("load progress " + loadOp.progress);
if (loadOp.isDone) loadOp = null;
}
}
Note the difference between `GetKeyDown` and `GetKey` — the first fires once on the frame the key goes down, the second fires every frame while it's held. Using the wrong one means triggering your jump sixty times a second.
## Who should watch this
Anyone who's got comfortable with GameObjects and components but hasn't worked out how a game actually moves between menus, levels and overlays. It's also the natural starting point if you've picked the 2D platformer as your course project, since the idle/jump animator you build here is the foundation of that character. Watch with Unity open and the repo cloned — Lucian says it plainly, watching gets you 20 percent and doing gets you 80.
Part of the full curriculum. Course overview.