Skip to content

L1.1 — Your First Script & Running It

What you'll learn

  • Recognize that every GDScript file is a class, and that extends names the class it builds on.
  • Attach a script to a node, write _ready, and print to the Output dock.
  • Run a single scene with F6 versus the whole project with F5, and know which to reach for.
  • Diagnose the "I changed it but nothing changed" trap (an unsaved script runs the old bytecode).

How it applies

  • The base class is the API you get. extends decides which methods and properties exist on your script. Extend the wrong type and the first method you call that the base lacks is an error — at parse time for an unknown identifier, at runtime for a bad call. Choosing the base is choosing the surface you can use; it is not decoration.
  • print is your cheapest instrument. The Output dock is the first observability surface you have. A developer who cannot see what a script did at runtime is debugging blind; print is the one-line probe that ends most "is this code even running?" questions.
  • Run the right target or waste the iteration. Pressing F5 launches the project's main scene; F6 launches the scene you are editing. Running the whole game to test one scene buries the thing you changed under everything else that loads first.
  • Stale artifacts masquerade as logic bugs. Godot runs the saved version of a script. Edit, forget to save, run, and you are testing the old code — the "I fixed it and it still fails" phantom. It is the same defect class as shipping a build from a stale cache.

Concepts

A script is a class

In many languages you open a class with a keyword: class Player { … }. GDScript does not. Every .gd file is already a class — the file is the class body. You do not name it or wrap it; you just start writing members. The first meaningful line is almost always extends, which names the class your script builds on:

extends Node

This says: my class is a Node, plus whatever I add below. Everything a Node can do, your script can now do, and _ready, print, and the rest are available because Node (and its bases) provide them. Omit extends and your script silently extends RefCounted, the minimal base — fine for a plain data class, wrong for anything you attach to a node.

Example

Two scripts, same three lines of behavior, different bases:

extends Node      # can live in the scene tree, gets _ready, _process, etc.
extends Node2D    # everything Node has, plus a 2D position/rotation/scale

The choice is not cosmetic: only the second has a position. Extend Node and write position = Vector2(10, 0) and you get an error, because plain Node has no position. You meet the full node hierarchy in G1.2; for now, the rule is extend the type whose abilities you need.

The node-attached model

A script usually does not run on its own. You attach it to a node, and the script's class becomes that node's behavior — which is why it must extend the node's type (or a compatible one). The node provides the data and the place in the tree; the script provides the logic. You will build this out properly in Part B. For now you need exactly one entry point.

_ready — the entry point for a first run

When a node enters the running scene, Godot calls its _ready function once, if it has one. That is where your first code goes:

extends Node

func _ready() -> void:
    print("Script is running.")

func declares a function; _ready is the name Godot looks for; -> void states that it returns nothing (you meet return types in L1.4). The leading underscore marks it as one of Godot's virtual callbacks — a function the engine calls for you at the right moment, rather than one you call yourself. The full set and exact ordering of these callbacks is G2.1; _ready is the only one you need to run a line of code.

print(...) writes its arguments to the Output dock at the bottom of the editor. It accepts any number of values of any type and converts them to text:

print("speed is ", 150, " and that is ", true)

There is no setup and no cost worth worrying about during development. When a script "isn't working," a print at the top of the function answers the first question — did it even run? — before you theorize about anything subtler.

Running: F6 for this scene, F5 for the project

  • F5Run Project. Launches the main scene set in Project Settings. This is the whole game starting from its entry point. Early on, you may not have set a main scene yet.
  • F6Run Current Scene. Launches the scene open in the editor, by itself. This is what you use to test one piece in isolation.

Both open a separate game window. Closing it returns you to the editor. For everything in Part A, F6 on a throwaway scene is the right tool.

Walkthrough

Perform these in a new, empty Godot 4.6 project.

  1. In the Scene dock (top-left), click Other Node, choose Node (the plainest type), and confirm. You now have a one-node scene.
  2. Right-click that nodeAttach Script. Accept the defaults (it will offer res://node.gd and pre-fill extends Node). Click Create. The script editor opens.
  3. Below the extends Node line, write a _ready function that prints a line of your choosing. Type it; do not paste — the worked example above shows the shape of the two lines, not the text to copy.
  4. Save the script (Ctrl+S). Save the scene as well when prompted (Ctrl+S again, name it anything).
  5. Press F6 to run the current scene. A game window opens (empty — there is nothing to draw) and the Output dock shows your printed line.
  6. Change the printed text. Run again without saving first, and notice the old text still appears. Now save, run, and see the new text. That is the stale-artifact trap, demonstrated on purpose.

Optional sanity check

Temporarily change extends Node to extends Node2D, then add position = Vector2(50, 0) inside _ready, save, and run — it works. Change the base back to extends Node and run again: you get an error on the position line, because plain Node has no position. Revert when done. This is the "the base class is the API you get" point, felt rather than told.

Self-check quiz

Q1 — What does the line extends Node actually do?

A. Imports the Node module so its functions can be called. B. Declares that this script's class inherits from Node, gaining its members. C. Renames the script file to Node. D. Registers the script as an autoload named Node.

Reveal answer

B. A .gd file is a class; extends names its base class, so the script inherits that type's properties and methods and may override its virtual callbacks. A invents a "module import" GDScript does not have (there is no import). C confuses inheritance with the filename. D describes the autoload mechanism (G1.5), unrelated to extends.

Q2 — You are editing enemy.tscn and want to test just it. Which key launches that scene alone?

A. F5, because it always runs whatever scene is open. B. F6, which runs the current scene by itself. C. F5, then pick the scene from a dialog every time. D. Either key; they do the same thing.

Reveal answer

B. F6 runs the current scene. F5 runs the main scene set in Project Settings, regardless of what you have open — so A is wrong about what F5 does, and D is wrong that they are equivalent. C describes the one-time prompt you get when no main scene is set yet, not normal F5 behavior.

Q3 — You edit a print line, run with F6, and the old text still appears. Most likely cause?

A. Godot caches scripts for an hour; you must wait. B. You did not save the script, so the run used the previous saved version. C. print only updates once per editor session. D. The Output dock needs to be manually refreshed.

Reveal answer

B. Godot runs the saved bytecode; an unsaved edit is not part of the run. A, C, and D invent behaviors that do not exist — there is no time-based cache, no once-per-session limit, and no manual Output refresh. Save (Ctrl+S), then run.

Integration question

Q4 — open

You attach a script to a Node2D and write func _ready(): position = Vector2(10, 0). It works. You attach a script with the same body to a plain Node, and the position line errors. Explain, using the three ideas this chapter introduced — a script is a class, extends chooses the base, and _ready is called by the engine — why the same code is valid on one and invalid on the other.

Reveal expected answer

The script is a class whose base is whatever extends names. Node2D extends Node and adds a 2D transform, so position is a real member of the Node2D class — your script inherits it. Plain Node has no transform, so position is not a member of that class, and assigning it is an error. _ready runs in both cases (both are nodes, both get the callback), so the difference is not whether the code runs but whether the member exists on the base you chose. Choosing the base class is choosing the set of members your script is allowed to use — the chapter's central point.