◀ 1.21.31.4 ▶

1.3 — Your First Script: Variables & Update

Today you write your first C#: one throwaway script, PlayerLogger.cs, that prints to the Console once a second. It teaches the three things every later lesson leans on — how a script attaches to a GameObject, what Start() and Update() do, and where a value has to live to be remembered.

In this lesson you will be able to answer“how do I print text to the console?”Full walkthrough
Break It Down~10 minFull walkthrough

Get a script of my own printing to the Console, and keep printing once a second instead of sixty times, so the Console is still readable a minute later.

First, before any code: cut that into parts small enough that you could look each one up on its own.

  1. Make the file something Unity will runOne class in the file, its name matching the filename exactly, inheriting MonoBehaviour so Unity can attach it as a component. Get this wrong and the file never runs.Read: the class Boss : MonoBehaviour row in the quick reference.
  2. Put the two values that have to be remembered in the class bodyA reportDelay tuning knob and a nextReportTime deadline, both declared outside every method: created once and kept for the whole game, where the same lines inside a method would be rebuilt sixty times a second.Read: the declare-vs-assign split in the scope rule below, and the [SerializeField] row in the quick reference.
  3. Give the deadline its first value in the once-only setupThe once-only setup runs before the first frame. It sets nextReportTime to the current game time plus the delay, and prints one line so you know the script ran.Read: the once-only setup and Time.time rows in the quick reference.
  4. Gate the print and re-arm the deadlineThe every-frame method runs far too often to print. The if asks whether the deadline has passed; if it has, print, then push the deadline a second into the future so the gate closes again.Read: the Time.time row in the quick reference.

Only the last part does anything you can see. The first three exist because of one question — does anything next frame need to know about this? — asked three times.

Could you look this part up on its own, in one search? "Print to the Console" cannot be looked up; "the seconds since Play" can.

Your turn

Which of the four parts would you have written first if nobody had shown you the split — and what would have gone wrong?

In the Editor~15 min
  1. Project window → Assets/Scripts, right-click → Create → C# Script, name it PlayerLogger. Unity fills the new file with a class of the same name.
  2. Hierarchy → select the Player, then Inspector → Add Component → search PlayerLogger and add it. Dragging the script file onto the object in the Hierarchy does the same thing.
  3. Console window → Window → General → Console. Every log line lands here. Turn on Clear on Play so each run starts empty.
  4. Game view → press Play and watch the Console count the seconds, then press Play again to stop — stopping wipes the Console too.
  5. Inspector → with the Player selected, find Player Logger (Script) and change the Report Delay box while the game runs. That box is the private field, tuned without touching code.
    Inspector showing PlayerInspectorPlayerTransformPlayer Controller (Script)Player Logger (Script)5Report Delay1
    The Player's Inspector once step 2 is done. Player Logger (Script) is a component like any other, and Report Delay is the only box it has — [SerializeField] is what put a box on a private field.
A script on no object never runs. Skip step 2 and the Console stays empty, with no error to tell you why — so check the Inspector's component list before hunting a bug in the code.
In the Code~20 min

The scope rule: a variable declared inside a method is created when the method starts and destroyed when it ends, so it cannot remember anything between calls. One declared in the class body, outside every method, belongs to the object and lives as long as the object does. Declaring says the name exists, assigning gives it a value, and one line may do both only when nothing next frame needs the value.

APIWhat it isWhat you getWhat it costs
Start()Lifecycle hook Unity calls once, before the first frame.One-time setup: a field's starting value, one line proving the script ran.It runs once, so it cannot watch anything that changes while the game runs.
Update()Lifecycle hook Unity calls every frame — roughly 60 times a second.Continuous work: polling input, checking a timer, moving something.Not a fixed rate — a heavy frame changes how often it runs, so nothing inside may assume one.
Debug.Log(object message)Prints a value into the Console.Developer eyes: with no UI yet, the only way to see what the code is doing.A real side effect — one log inside Update() prints sixty messages a second.
Time.timeA float: seconds elapsed since you pressed Play.A clock to compare against: "has my deadline passed?"It resets when you stop, and keeps counting while paused.
[SerializeField] private float reportDelay = 1.0f;A field declaration plus the attribute that serializes it.A private field with an Inspector box, tunable without touching code.The box beats the code default once set, and exists only on the object carrying the script.

Socratic: Lifecycle and Timing

  1. If your computer runs the game at 120 FPS, how many times will Update() execute in one second? On a battery-saving laptop at 30 FPS?
  2. Write Debug.Log("hello") directly inside Update() with no check around it: what happens to the Console in 5 seconds?
  3. Ask the scope question about your own script — does anything next frame need to know about this? Which parts of the Break It Down block answer yes, and which answer no?
In the CodeFull walkthrough

The question this answers“how do I print text to the console?”

You are editing Assets/Scripts/PlayerLogger.cs. Type these in order — the whole file is under twenty lines.

Step 1Name the file and the class

A C# file holds one class, and the class name has to match the filename exactly — Unity uses the filename to find your class.

// Assets/Scripts/PlayerLogger.cs — a new, empty file you create in the Project window.
using UnityEngine;

public class PlayerLogger : MonoBehaviour
{
  1. 2Tells C# where to find everything Unity provides — MonoBehaviour, Debug, Time. Without it, those names do not exist.
  2. 4Declares the class. The : MonoBehaviour part makes it a component — a script Unity can attach to a GameObject.The name after class must match the filename exactly, capitals included, or Unity throws an error the moment you press Play.

Step 2Put the values that have to be remembered in the class body

Both lines sit inside the class braces but outside any method — the placement the scope rule above is about.

// PlayerLogger.cs — class body: inside the class braces, outside every method.
    [SerializeField] private float reportDelay = 1.0f;
    private float nextReportTime;
  1. 2Seconds between reports. [SerializeField] makes a private field editable in the Inspector; the f suffix marks it as a float, not a double.
  2. 3When the next report is allowed. private: no other script may touch it.It has no value yet. Declaring and assigning are two separate jobs, and Start() is about to do the second one.

Step 3Write Start() — the once-only setup

Start() is a lifecycle method: Unity calls it for you, exactly once, before the first frame.

// PlayerLogger.cs — below the fields, above Update().
    void Start()
    {
        nextReportTime = Time.time + reportDelay;
        Debug.Log("PlayerLogger is alive.");
    }
  1. 2The method Unity runs once when the object appears. void means it hands nothing back.
  2. 4The deadline for the first report: current game time plus one second. This is the assignment the declaration above left undone.
  3. 5Prints one line to the Console so you know the script attached and ran.

Step 4Write Update() — the every-frame gate

Update() runs once per frame — roughly 60 times a second, far too often to print. The if is what keeps the Console readable.

// PlayerLogger.cs — below Start(). Unity calls this once per frame.
    void Update()
    {
        if (Time.time >= nextReportTime)
        {
            Debug.Log("Player has been running for " + Time.time + " seconds.");
            nextReportTime = Time.time + reportDelay;
        }
    }
  1. 2The method Unity runs every frame, automatically.
  2. 4The gate: "has enough time passed to report again?" >= means at or past; the == you might expect would be wrong, because frames are not exact.
  3. 6Prints the message. The + glues text and number into one string.
  4. 7Moves the deadline a second into the future, so the gate closes again.Delete this line and the Console floods: the gate stays open, so it prints on every frame from then on.
Check your work: the finished filebuild it as you read — then open this to check every line

Do not paste it in — compare it against what you built, and be able to say what any differing line would do instead.

// ------------------------------------------------------------------
//  REFERENCE FILE — Assets/Scripts/PlayerLogger.cs
//  Written to be read, not pasted. Build it with the walkthrough
//  above, one step at a time, then open this and compare.
//  If it is sitting in your project unchanged, that is the thing
//  to go and do now — the walkthrough is still above you.
// ------------------------------------------------------------------
using UnityEngine;

public class PlayerLogger : MonoBehaviour
{
    // ---- class body: every field lives up here, outside every method --------
    [SerializeField] private float reportDelay = 1.0f;

    private float nextReportTime;

    // ---- Start(): Unity calls this once, before the first frame ------------
    void Start()
    {
        nextReportTime = Time.time + reportDelay;
        Debug.Log("PlayerLogger is alive.");
    }

    // ---- Update(): Unity calls this every frame ----------------------------
    void Update()
    {
        if (Time.time >= nextReportTime)
        {
            Debug.Log("Player has been running for " + Time.time + " seconds.");
            nextReportTime = Time.time + reportDelay;
        }
    }
}
Bug Hunt~20 min

Bug Hunt: Break the Logger on Purpose

  1. Cut private float nextReportTime; from the class body and declare it inside the method that runs every frame, giving it a starting value there. What does the Console do now?
  2. Rename the class to playerLogger (lowercase p), leaving the file named PlayerLogger.cs. What error appears when you press Play?
  3. Attach the script to the Turret instead of the Player. Does it still run, and what does that prove about components and GameObjects?
  4. Comment out nextReportTime = Time.time + reportDelay; inside the if. What happens to the Console within 2 seconds, and why does the gate never close again?

Checkpoint — before moving on, you must be able to:

Reference Docs

Need a deeper explanation? These pages cover the same concepts:

Next: 1.4 — Movement I: Input, Direction, Vector, where the scope rule from today becomes a moving character.

0/8 answered

Answers save on this device as you type. At the end of class, click Download and upload the file to the Canvas assignment.

Saved on this device only — the Dashboard tracks your progress.