Good to see you again. Previously, you turned values such as health and damage into small gameplay rules using methods, parameters, return values, and conditionals. This lesson adds the Unity-specific execution model: Unity can call certain correctly named methods for you, and the Console tells you when your code cannot compile or fails while the game is running.
By the end, you will be able to choose between Awake, OnEnable, Start, Update, and FixedUpdate at a beginner-appropriate level; distinguish a compile-time syntax error from a runtime null-reference error; and use the Console’s file and line information to fix both.
Unity event methods: code Unity calls for you
A script that inherits from MonoBehaviour becomes a Unity component when attached to a GameObject. Unlike the helper methods you wrote last lesson, some methods in a MonoBehaviour have special names. Unity recognizes them and calls them at specific moments.
For example, you call your own damage method explicitly:
ApplyDamage(damagePerHit);
But you do not call Start yourself. Unity calls it when the component reaches its first active frame.
private void Start()
{
Debug.Log("Unity called Start.");
}
The capitalization and signature matter. Write Start, not start; write void, not Void. For the event methods in this lesson, use the standard form private void MethodName().
Here are the event methods you will use most often in the early stages of your arena game:
| Event method | When Unity calls it | Good early use |
|---|---|---|
Awake | When Unity loads this component instance | Get and store references required by this component |
OnEnable | Whenever the component becomes enabled and active | Reset state that should refresh each time the object is enabled |
Start | Before the first Update for an enabled component | Begin gameplay setup that depends on the object being active |
Update | Once per rendered frame while active and enabled | Read input, update non-physics timers, and run frame-based logic |
FixedUpdate | At regular physics intervals, independently of rendered frames | Work with Rigidbody2D physics later in the course |
For a GameObject that begins active, a useful simplified order is:
AwakeOnEnableStartUpdate, repeatedly while the game runs
Awake is especially useful when one component needs another component on the same GameObject. For example, a future player controller will need a Rigidbody2D. It can obtain and store that reference in Awake, before attempting movement.
private Rigidbody2D body;
private void Awake()
{
body = GetComponent<Rigidbody2D>();
}
This line asks the current GameObject for its Rigidbody2D component and stores the result in body. If the GameObject does not actually have a Rigidbody2D, GetComponent<Rigidbody2D>() returns null; you will learn how to catch that situation shortly.
Use Start when the work is setup that should happen once, just before normal frame updates begin. Use Update for logic that must be reconsidered as the game runs. Avoid placing a one-time setup action in Update: it would run dozens of times each second.
FixedUpdate is not simply “another update.” It is Unity’s physics-timed callback. When you implement top-down movement with Rigidbody2D in a later module, the actual physics movement belongs there. For now, remember the division: ordinary frame logic in Update; Rigidbody2D-related physics work in FixedUpdate.
MonoBehaviour Lifecycle Basics - Awake OnEnable Start Update FixedUpdate | Unity C# Basics Part 3
Watch MonoBehaviour Lifecycle Basics from LlamAcademy for a visual explanation of the callbacks Unity invokes automatically and why their timing matters.
Start with the overview to place the main callbacks in the lifecycle. Then watch Awake and Update, focusing on the example of obtaining a component reference in Awake and the fact that Update runs once per frame. Finish with FixedUpdate for the distinction between frame rate and physics timing; this will prepare you for Rigidbody2D movement later.
One caution: this is the usual lifecycle within one component, not a guarantee that every GameObject in a scene completes all of its Awake calls before every other GameObject begins Start. Do not build important gameplay rules around an assumed order between unrelated objects. For now, keep each component responsible for preparing its own references and state.
The Console is your first debugging tool
The Unity Console records messages generated by Unity and by your scripts. Open it from Window > General > Console. Keep it visible when you write code.

The Console has three kinds of output that matter immediately:
- Messages from
Debug.Log, used to confirm that code ran or inspect a value. - Warnings from
Debug.LogWarningor Unity, indicating something may be wrong but the game can often continue. - Errors from
Debug.LogError, compiler failures, and runtime exceptions. Treat these as work to investigate, not background noise.
The image shows several errors in Assets/Scripts/game.cs, each with a line and column such as (17,7). Selecting an entry reveals its full message. Usually, double-clicking a script reference opens the relevant file and line in your code editor.
Unity - Manual: Console window reference
Read Unity Manual’s Console window reference to learn how to interpret, filter, and manage the messages your project produces.
Read the opening explanation in “Console window reference,” especially what the Console reports. In “Console window interface,” focus on the message list and detail area: select an entry, then use the script link in its detail area. In “Console toolbar options,” read the Collapse option. Then read “Searching and filtering Console output,” from searching messages, and note how the message, warning, and error buttons let you isolate one kind of output.
Two Console controls are particularly helpful:
- Clear removes old logs, warnings, and runtime errors. Clear before a focused test so that new output belongs to the change you just made. Compiler errors remain until the code is corrected.
- Collapse groups repeated copies of the same message. A null-reference exception inside
Updatecan otherwise flood the Console every frame, hiding the first useful error among hundreds of duplicates.
Use the three type buttons beside the search bar to hide messages or warnings temporarily when you need to focus on errors. This is not a substitute for fixing problems; it is a way to make the evidence readable.
Two different kinds of failure
The most important debugging distinction is when the error occurs.
Compile-time errors: Unity cannot build your scripts
A compile-time error happens when C# cannot understand or validate the code after you save it. Common beginner causes include:
int score = 0 // Missing semicolon.
if (currentHealth <= 0
{
Debug.Log("Eliminated");
}
The second example has a missing closing parenthesis.
Debug.Log(playerHeath); // Misspelled variable name.
If the declared field is playerHealth, then playerHeath does not exist. This is not punctuation, but it is still a compilation failure: the compiler cannot resolve the name.
When scripts do not compile, Unity cannot safely enter Play mode with the new code. One small syntax mistake can produce several Console errors because the compiler loses track of the intended structure after the first failure. Therefore, fix the first relevant error, save, let Unity compile again, and only then assess what remains.
A systematic response is:
- Read the Console’s error message, script name, and line number.
- Double-click the entry to open that line.
- Inspect that line and the few lines above it for a missing semicolon, parenthesis, brace, quote, or misspelled name.
- Fix one clear cause, save the script, and return to Unity.
- Repeat until the red error count is zero.
Your editor may underline the problem before Unity does, which is useful. Still, Unity’s Console is the final authority on whether the current project compiled.
Runtime errors: code compiled, but failed during play
A runtime error occurs after the game begins running. A null-reference exception is one of the most common.
A reference variable is meant to point to a Unity object, such as a Transform, Rigidbody2D, GameObject, or another component. If it has no object assigned, its value is null.
This fails at runtime:
[SerializeField] private Transform target;
private void Start()
{
Debug.Log(target.name);
}
If nothing was assigned to target in the Inspector, target is null. C# cannot read .name from “no object,” so Unity logs a NullReferenceException.
The error is not that target.name is an invalid expression in general. It is valid only when target refers to a real Transform.
Unity - Manual: Null references
Read Unity Manual’s Null references for the precise meaning of a null-reference exception and Unity’s basic null-check pattern.
Read from the first paragraph of “Null references” through the example that uses GameObject.Find. Pay particular attention to how the Console identifies a source location: the error location. Then read the “Null checks” subsection, especially the null-check strategy. Notice that the script checks the reference before trying to use its members.
The straightforward preventive pattern is:
if (target == null)
{
Debug.LogError("Target has not been assigned.");
return;
}
Debug.Log(target.name);
The if condition tests the reference before accessing .name. If it is missing, Debug.LogError produces a useful, intentional message and return; prevents the unsafe line from running.
A null check prevents the crash, but it is not always the complete gameplay fix. You must still decide why the reference is missing:
- Was a serialized field left empty in the Inspector?
- Does the GameObject lack a required component?
- Did
GetComponentsearch the wrong object? - Did a search such as
GameObject.Findfail to locate an object? - Was an object destroyed before the script tried to use it?
For the simple target field, the intended solution is usually to assign a Transform in the Inspector. For a required same-GameObject component, Awake plus GetComponent is usually cleaner, followed by a check that produces a specific error if the component is missing.
A controlled debugging lab
This short lab lets you produce both types of issue safely, read the Console, and then restore working code. Work outside Play mode when assigning references in the Inspector; changes made while in Play mode are normally discarded when you stop.
1. Build a small lifecycle and Console probe
Create an empty GameObject named ConsoleDebugDemo. Create a script with the same name, attach it to the GameObject, and replace its contents with the following:
using UnityEngine;
public class ConsoleDebugDemo : MonoBehaviour
{
[SerializeField] private Transform target;
private bool wroteFirstUpdateMessage;
private void Awake()
{
Debug.Log($"{name}: Awake");
}
private void OnEnable()
{
Debug.Log($"{name}: OnEnable");
}
private void Start()
{
if (target == null)
{
Debug.LogError($"{name}: Assign a Target in the Inspector.");
return;
}
Debug.Log($"{name}: Target is {target.name}");
}
private void Update()
{
if (wroteFirstUpdateMessage)
{
return;
}
wroteFirstUpdateMessage = true;
Debug.Log($"{name}: first Update");
}
}
Create another empty GameObject named TargetMarker. Select ConsoleDebugDemo, then drag TargetMarker from the Hierarchy into the Target field in the Inspector.
Open the Console, select Clear, and enter Play mode. You should see messages in this order:
ConsoleDebugDemo: Awake
ConsoleDebugDemo: OnEnable
ConsoleDebugDemo: Target is TargetMarker
ConsoleDebugDemo: first Update
The Update message appears only once because the boolean prevents repeated logging. This restraint matters: a plain Debug.Log in Update can create a stream of messages that makes real errors hard to notice.
2. Create and repair a syntax error
Stop Play mode. In the field declaration, temporarily remove the semicolon:
private bool wroteFirstUpdateMessage
Save the script. Unity should show one or more red compiler errors in the Console. The exact wording can vary by editor version, but it will commonly mention an expected semicolon or invalid syntax near the following line.
Click the first error that refers to ConsoleDebugDemo.cs. Confirm that the editor takes you close to the missing punctuation. Restore the semicolon:
private bool wroteFirstUpdateMessage;
Save again and wait until the Console’s red error count is zero. This is the complete compile-error loop: make the smallest likely correction, save, and verify compilation rather than assuming it worked.
3. Create, locate, and repair a null-reference error
With the correct script saved, select ConsoleDebugDemo and clear its Target field in the Inspector. Then temporarily replace the entire Start method with this unsafe version:
private void Start()
{
Debug.Log($"{name}: Target is {target.name}");
}
Clear the Console and enter Play mode. This time, the scripts compile and the game starts, but Unity logs a NullReferenceException. Select the error and inspect its detail area.
Find the first stack-trace entry that names your method, ConsoleDebugDemo.Start, rather than an internal Unity method. Click it to open the line that accessed target.name. The line tells you where it failed; the unassigned Target field explains why it failed.
Restore the safe version of Start:
private void Start()
{
if (target == null)
{
Debug.LogError($"{name}: Assign a Target in the Inspector.");
return;
}
Debug.Log($"{name}: Target is {target.name}");
}
Now test both intended cases:
| Target field | Expected Console result |
|---|---|
| Empty | Your specific Assign a Target in the Inspector error, with no null-reference exception |
TargetMarker assigned | Target is TargetMarker, with no errors |
This is a useful distinction. A NullReferenceException is evidence that the program attempted something unsafe. Your deliberate Debug.LogError message is evidence that your code detected the missing setup early and described exactly how to correct it.
How to Debug Errors in Unity | Beginners Must Learn This
Watch How to Debug Errors in Unity from Game Dev Experiments to see the Console-to-code workflow used on a null reference and a compile-time error.
Watch null reference debugging. Focus on selecting the Console error, locating the relevant line in the stack trace, and checking which reference was not assigned in the Inspector. Then watch compile errors for the contrast: compilation failures are typically located immediately by the Console and code editor before the game can run.
A reusable debugging routine
When something fails in your game, avoid changing many lines at random. Use a narrow evidence-based routine.
- Classify the failure. If Unity cannot enter Play mode because of red compiler errors, solve compilation first. If it runs and then reports an exception, investigate it as a runtime error.
- Start at the Console location. Read the file, line, and method. Click the entry.
- Read the complete statement. If a null exception happens on a long line, identify every reference before a dot. Any one could be null.
- Check the setup route. For a serialized field, inspect the GameObject’s Inspector. For
GetComponent, verify the required component exists on the object being searched. - Make one targeted fix. Add a missing assignment, correct the code, or add a clear guard where absence is possible.
- Test the original failure again. A Console that is merely quiet after you changed the test is not proof that the problem is fixed. Reproduce the same situation deliberately.
For example, imagine a future player controller contains:
private Rigidbody2D body;
private void Awake()
{
body = GetComponent<Rigidbody2D>();
}
private void FixedUpdate()
{
body.linearVelocity = Vector2.zero;
}
If the player GameObject has no Rigidbody2D, the problem may not become visible until FixedUpdate. The Console points to the line using body; the actual cause is a missing component. A precise development-time guard makes that clear:
private void Awake()
{
body = GetComponent<Rigidbody2D>();
if (body == null)
{
Debug.LogError("PlayerController requires a Rigidbody2D component.");
}
}
You will attach that component correctly when building the arena. The broader habit is already valuable: initialize references early, validate required setup, and use the Console location as the first clue rather than the final explanation.
Key takeaways
MonoBehaviour event methods are Unity-controlled entry points. Use Awake for early component setup, Start for one-time active setup, Update for frame-based logic, and FixedUpdate for later Rigidbody2D physics work.
The Console is the main workspace for interpreting failures:
- Compile-time errors prevent the current scripts from compiling. Read and fix the first relevant error, save, and recheck.
- A null-reference exception occurs at runtime when code accesses a member of an unassigned or missing reference.
- The file, line, method name, stack trace, and Inspector setup together reveal both where a problem appeared and why it appeared.
- Use
Collapseto control repeated runtime errors, and use clearDebug.LogErrormessages to report missing required setup before an exception occurs.
You have now completed the C# and Unity foundations module. Next, you will begin building the top-down arena by importing 2D assets responsibly, checking their license, and configuring sprites so Unity draws them correctly.
Can't find a good explanation? Sign up and we'll make it for you
Sign up