Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 4

Mastering the Unity Game Loop

Recommended resources

Welcome back! In our last lesson, we dove into object-oriented programming by creating your own custom C# classes like Stat and Item. You learned how to use [System.Serializable] to see them in the Inspector and how to create instances of them using new. We even used the Awake() method to initialize our CharacterStats script, which hinted at a specific, ordered sequence of events.

Today, we will explore that sequence in detail. We're going to uncover the "heartbeat" of every Unity game: the Game Loop. Understanding this loop is fundamental to game development, as it dictates when your code runs. By the end of this lesson, you will be able to correctly use the Awake(), Start(), and Update() methods to manage the lifecycle of your game objects, ensuring your code runs reliably and without bugs.

The Heartbeat of Your Game

Unlike a simple script that runs from top to bottom and then stops, a game is a continuous process. Every fraction of a second, it needs to update object positions, check for player input, run game logic, and redraw the screen. This cycle is known as the Game Loop.

Unity manages this loop for you by automatically calling specific functions in your MonoBehaviour scripts at predefined moments. These are often called "event functions" or "magic methods" because they work as long as you name them correctly. The case and spelling must be exact for Unity to recognize them.

Unity Magic Functions / Script Lifecycle Methods

This video from BMo provides an excellent demonstration of what these "magic functions" are and why their names are so important.

Watch the first minute of the video, where the presenter shows what happens when Update is misspelled. This clearly illustrates that Unity is actively looking for these specific function names to execute.


The Order of Events

Now that you know these special functions exist, the next critical piece of information is the order in which they are called. Understanding this sequence is the key to preventing many common bugs.

This diagram provides a comprehensive overview of the execution order for the most common event functions.

A detailed flowchart showing the order of Unity's main event functions, from initialization (`Awake`, `Start`) through the per-frame loops (`FixedUpdate`, `Update`) to destruction (`OnDestroy`).

As you can see, the process can be broken down into distinct phases. For today's lesson, we will focus on the first two major parts of this cycle: Initialization and Game Logic.


The Initialization Phase: Awake() vs. Start()

Both Awake() and Start() are used for one-time setup code when an object first enters the scene. However, their execution timing is different, and this difference is crucial for making scripts work together.

Here are the two golden rules of initialization:

  1. Awake() is always called before Start().
  2. All Awake() methods on all active objects in the scene are called and completed before any Start() methods are called.

This makes Awake() the ideal place for a script to set up its own internal state or get references to its own components, without worrying about other objects. Start() is then used for any setup that relies on other objects having already been initialized.

  • Use Awake() for: Setting up references within the script itself. For example, grabbing a component on the same GameObject using GetComponent<Rigidbody2D>(), or, as we did last lesson, creating new instances of your custom classes (strength = new Stat();).
  • Use Start() for: Communicating with other objects. For example, a GameManager might use Start() to find the Player object in the scene, knowing that the Player script has already finished its own Awake() setup.

Getting this wrong can lead to one of the most common errors in Unity: the NullReferenceException. This happens when you try to use a variable that hasn't been initialized yet.

Start vs Awake vs OnEnable (Unity Tutorial)

The video "Start vs Awake vs OnEnable" by Ketra Games demonstrates this exact problem and its solution perfectly.

Watch the segment from "let's look at some examples" up to the fix. You will see how an incorrect execution order causes an error, and how moving the initialization code from Start() to Awake() solves it.

To solidify this concept, let's turn to a textual guide that lays out these differences clearly.

【Unity】Unity Lifecycle Complete Guide - Awake, Start, and ...

This article provides a well-structured breakdown of the Unity lifecycle. We'll focus on the section that directly compares Awake and Start.

First, quickly review the high-level overview to see where these functions fit. Then, carefully read the section titled Initialization: Awake() vs Start(). Pay close attention to the table comparing the two and the bolded text that reinforces the execution order rule.


The Game Logic Phase: Update()

After all the Awake() and Start() methods have run, Unity begins the main loop. The most important function in this phase is Update(), which is called once every single frame. This is where the majority of your ongoing game logic will live.

Anything that needs to be checked continuously or changed smoothly over time belongs in Update(). Common uses include:

  • Reading player input from the keyboard, mouse, or gamepad.
  • Moving characters or objects that don't rely on Unity's physics engine.
  • Managing timers or cooldowns.
  • Updating animations or UI elements.

A crucial concept when working with Update() is frame rate independence. The time it takes to render one frame can vary depending on the complexity of the scene and the power of the computer. If you move an object by a fixed amount each frame, it will move faster on a powerful computer and slower on a less powerful one.

To solve this, Unity provides Time.deltaTime. This variable holds the amount of time in seconds that has passed since the last frame. By multiplying your movement values by Time.deltaTime, you ensure that the movement is based on time, not frame rate, making it consistent across all hardware.

// In Update()
// This moves the object 10 units per second, regardless of frame rate.
transform.Translate(Vector3.forward * 10 * Time.deltaTime); 

The following resource contains several small game examples. Skimming the code snippets will give you a great feel for what kind of logic typically goes inside an Update method.

[PDF] Unity game examples - M2H.nl

This document provides practical code snippets from various mini-games, showing Update() in action.

Browse through the following examples. You don't need to understand every line, but focus on what is being done inside the Update() function. In the "Catch eggs" game, look at the EggScript.cs snippet to see simple falling movement using Time.deltaTime. In the same game, the Playerscript.cs snippet shows how to handle player input and movement. In the "Breakout" game, the Paddle.cs snippet demonstrates clamped horizontal movement based on player input.


Putting It All Together

Let's apply what you've learned to the CharacterStats script from our previous lesson. Create a new script named LifecycleTest and attach it to a GameObject in your scene.

using UnityEngine;

public class LifecycleTest : MonoBehaviour
{
    // Awake is called when the script instance is being loaded.
    void Awake()
    {
        Debug.Log("Awake: The script instance is being loaded. This is called first.");
    }

    // Start is called before the first frame update.
    void Start()
    {
        Debug.Log("Start: All Awakes are done. Now we can initialize things that depend on others.");
    }

    // Update is called once per frame.
    void Update()
    {
        // This message will appear in your console every frame, which can be a lot!
        // We'll use a key press to show it's always listening.
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Debug.Log("Update: Spacebar pressed at frame: " + Time.frameCount);
        }
    }
}

Run your scene and observe the Console window. You will see the "Awake" message, followed by the "Start" message. The "Update" message will only appear when you press the spacebar, demonstrating that it is constantly checking for input every frame.


Conclusion

In this lesson, you've learned about the fundamental execution order of scripts in Unity. This structure, the Game Loop, is the foundation upon which all gameplay is built.

Here are the key takeaways:

  • The Unity Game Loop is an ordered sequence of event functions that Unity calls automatically.
  • Awake() is called first, even before the game officially starts. It's used for a script to initialize itself. All Awake() calls in a scene finish before any Start() methods begin.
  • Start() is called after Awake() but before the first Update(). It's used for initialization that may rely on other scripts having completed their own Awake() setup.
  • Update() is called once per frame. It's the home for most of your continuous game logic, like checking for input or updating object states.
  • Using Time.deltaTime in Update() is essential for creating frame rate-independent movement and logic.

Next Steps

Now that you have a solid grasp of when your code runs, you're ready to make things happen. The Update method is the perfect place to listen for player actions. In our next lesson, we will implement basic player input handling, allowing you to control a GameObject with the keyboard. This is the first exciting step towards creating your playable character.