Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Game Development with Godot & GDScript  Module 1 – Godot, the editor and your first scene

Nodes, scenes and instancing

Build Godot 4.7 scenes from nodes, instance them many times, read the .tscn text format and change a scene safely with inherited scenes and editable children.

  • Beginner
  • 15 minutes
  • Examples run with godot 4.7.2-stable and Python 3.14.8
  • By MySmartCoPilot
Free lesson, Pro answers: 15 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Describe nodes, the scene tree and scenes, and name a node's path in a running game
  • Build a scene and instance it several times, overriding a property on one instance
  • Read a .tscn file and find its nodes, external resources, instances and overrides
  • Use inherited scenes and editable children to change a scene without editing its source

Before you start

What you’ll learn · Before you start

What you will learn

  • Describe nodes, the scene tree and scenes, and name a node's path in a running game
  • Build a scene and instance it several times, overriding a property on one instance
  • Read a .tscn file and find its nodes, external resources, instances and overrides
  • Use inherited scenes and editable children to change a scene without editing its source

Before you start

On this page

A level with 40 coins needs one coin scene, not 40 coins: change the scene once and every coin follows, except the ones you deliberately made different. That rule, and the text file where Godot records the exceptions, decide how easy a game is to change a month from now.

Here you build a player from nodes and see it in the running scene tree, instance a coin scene five times and watch an override survive a change to the coin, read the .tscn files Godot saves, and change scenes through inherited scenes and editable children without touching their source.

A node does one job, and a scene is a tree of them

A node: A building block of a Godot game with one job, such as drawing a picture or following the player with a camera. Every node has a name, properties and callbacks, and it can have child nodes. is one building block with one job: a Sprite2D draws a picture, a CollisionShape2D gives a body its shape, a Camera2D decides what the screen shows. Every node has a name, properties you edit in the Inspector, callbacks the engine calls every frame, and a place in a tree: it can have children, and those children can have children. A scene: A tree of nodes with a single root node, saved to a file such as player.tscn so that it can be instanced as often as needed. is such a tree with a single root node, saved to a file so that it can be used again.

This program builds the player of the Godot documentation’s own example, a CharacterBody2D with a sprite, a collision shape and a camera, and adds it to the running game:

tree_tour.gd: a player built from four nodes GD · tree_tour.gd
extends SceneTree

# A player made of four nodes, as the Scene dock would show it, added to the running scene tree.
func _initialize() -> void:
	var player := CharacterBody2D.new()
	player.name = "Player"
	for part in [["Sprite", Sprite2D.new()], ["Hitbox", CollisionShape2D.new()], ["Camera", Camera2D.new()]]:
		part[1].name = part[0]
		player.add_child(part[1])
	root.add_child(player)
	await process_frame
	player.print_tree_pretty()
	print("Path of the camera: ", player.get_node("Camera").get_path())
	print("Parent of Player: ", player.get_parent().name, ", a ", player.get_parent().get_class())

	# Siblings need different names, and Godot renames a newcomer that clashes.
	var names := []
	for readable in [false, true, false]:
		var coin := Node.new()
		coin.name = "Coin"
		player.add_child(coin, readable)
		names.append(String(coin.name))
	print("Three children named Coin became: ", ", ".join(names))
	quit()
The player in the running scene tree, Godot 4.7.2 Python · tree_session.py
from session import files, run

files("project.godot", "tree_tour.gd")
run("""
godot --headless --no-header --script tree_tour.gd
""")

Output

$ godot --headless --no-header --script tree_tour.gd
 ┖╴Player
    ┠╴Sprite
    ┠╴Hitbox
    ┖╴Camera

Path of the camera: /root/Player/Camera
Parent of Player: root, a Window
Three children named Coin became: Coin, Coin2, @Node@2

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 tree_session.py

print_tree_pretty() draws the same tree the Scene dock shows: Player with its three children, in the order they were added. Once the game runs, the scene sits inside the scene tree: The one tree of nodes of a running game, with a Window named root at the top. Every node in it has a path from the top, such as /root/Player/Camera. of the whole game, under a node called root, a Window the engine creates. That gives every node a path, here /root/Player/Camera, read like a folder path from the top.

The scene tree of tree_tour.gd: root, a Window, above Player, a CharacterBody2D, with Sprite, Hitbox and Camera as its children.roota Window, made by the enginePlayerCharacterBody2D, the scene's rootPlayer's childrenSpriteSprite2D: draws the pictureHitboxCollisionShape2D: its shapeCameraCamera2D: path /root/Player/Camerachildchildren

The Player scene inside the running scene tree

Text description of the diagram

The diagram shows the tree from top to bottom.

  1. At the top is root, the Window the engine creates for every running game.
  2. Its child is Player, a CharacterBody2D, the root node of the Player scene.
  3. Player has three children: Sprite, a Sprite2D that draws the picture; Hitbox, a CollisionShape2D that gives the body its shape; and Camera, a Camera2D, whose path in the running tree is /root/Player/Camera.

Siblings need different names, because a path must lead to one node. The last line of the run shows what Godot does with three children called Coin: the first keeps the name, and the others are renamed, to Coin2 when add_child() is asked for a readable name and to @Node@2 when it is not. The editor picks readable names for you; code that adds nodes should name them itself.

Name nodes in PascalCase

The project organisation guide recommends PascalCase for node names, like Godot’s own node types, and snake_case for files: a Player node saved as player.tscn.

Checkpoint

Check: Nodes, scenes and the scene tree

  1. Question 1 of 2 In the running game of tree_tour.gd, what is the path of the Hitbox node?

    Choose one answer.

    A hint

    The camera's path was printed. Hitbox is its sibling.

    Show the answer: Question 1 of 2

    Answer: /root/Player/Hitbox

    The run printed /root/Player/Camera for the camera. Hitbox sits next to it under Player, so its path is /root/Player/Hitbox, read from the top of the tree like a folder path.

  2. Question 2 of 2 A node already has a child named Coin. You add another node named Coin with add_child(coin, true). What is it called?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Coin2

    The run added three nodes called Coin to one parent: the first kept the name, the second became Coin2 with a readable name, and the third became @Node@2 without one.

Instances follow their scene until you override them

An instance: A copy of a saved scene placed inside another scene. It takes every property from its scene except the ones changed on it, its overrides, which survive later changes to the scene. is a copy of a saved scene placed in another scene. In the editor, the chain-link button of the Scene dock adds one, and every instance starts identical to its scene. Change the scene and save it, and every instance changes with it, except in the properties you changed on that instance: those are overrides, and they win.

Step through a coin scene used five times in a level, the case of the next program, and watch where each coin’s colour comes from:

Interactive · step by step Five instances of coin.tscn and one override Step through the run of instancing.gd. The sub-label under each coin says where its colour comes from.
Five instances of coin.tscn and one override, step 6 of 6. Showing coin.tscn (modulate sky blue), level.tscn, Coin1 (sky blue from coin.tscn), Coin2 (sky blue from coin.tscn), Coin3 (sky blue from coin.tscn), Coin4 (sky blue from coin.tscn), Coin5 (sky blue from coin.tscn). In focus: Coin5.coin.tscnmodulate sky bluelevel.tscnfive instancesCoin1sky blue from coin.tscnCoin2sky blue from coin.tscnCoin3sky blue from coin.tscnCoin4sky blue from coin.tscnCoin5sky blue from coin.tscn

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. One scene, one node. coin.tscn holds a single node, Coin, a Node2D whose modulate colour is gold. Saved, it is a scene that can be used again and again.
  2. Five instances. level.tscn adds five instances of coin.tscn, Coin1 to Coin5, each a node of its own in the level. They take their colour from coin.tscn, so all five are gold.
  3. Positions are overrides. Coin2 to Coin5 move to x = 40, 80, 120 and 160. Each new position is an override, stored in level.tscn for that instance only; Coin1 stays at the scene's own position.
  4. Coin5 turns red. Changing Coin5's colour in the level overrides it too. level.tscn now stores modulate for Coin5 alone, and the editor shows a revert arrow beside that property.
  5. The scene changes. coin.tscn is edited to sky blue and saved. Coin1 to Coin4 never overrode the colour, so they follow it; Coin5 keeps its red, as the run printed.
  6. Revert brings it back. Clicking the revert arrow beside Coin5's modulate deletes the override, and Coin5 takes the scene's sky blue like the others.

The program does exactly that: it saves a gold coin scene, instances it five times with positions 40 pixels apart, turns Coin5 red, then recolours the coin scene sky blue and loads the level again:

instancing.gd: five instances, one override, one change of the scene GD · instancing.gd
extends SceneTree

# Saves a coin scene, instances it five times in a level, changes one instance and then the coin scene itself.
func _initialize() -> void:
	var coin := Node2D.new()
	coin.name = "Coin"
	coin.modulate = Color.GOLD
	_save(coin, "res://coin.tscn")

	var coin_scene := load("res://coin.tscn") as PackedScene
	var level := Node2D.new()
	level.name = "Level"
	for i in 5:
		# GEN_EDIT_STATE_INSTANCE keeps the scene's own values, as the editor does, so saving stores only changes.
		var instance := coin_scene.instantiate(PackedScene.GEN_EDIT_STATE_INSTANCE) as Node2D
		instance.name = "Coin%d" % (i + 1)
		instance.position = Vector2(40 * i, 0)
		level.add_child(instance)
		instance.owner = level
	level.get_node("Coin5").modulate = Color.RED
	_save(level, "res://level.tscn")

	# Recolour the coin scene, as editing coin.tscn and saving it would.
	coin.modulate = Color.SKY_BLUE
	_save(coin, "res://coin.tscn")

	ResourceLoader.load("res://coin.tscn", "", ResourceLoader.CACHE_MODE_REPLACE)
	var level_scene := ResourceLoader.load("res://level.tscn", "", ResourceLoader.CACHE_MODE_IGNORE) as PackedScene
	var reloaded := level_scene.instantiate()
	for c: Node2D in reloaded.get_children():
		print(c.name, " at ", c.position, " is #", c.modulate.to_html(false))
	reloaded.free()
	level.free()
	coin.free()
	quit()

func _save(root_node: Node, path: String) -> void:
	var packed := PackedScene.new()
	packed.pack(root_node)
	ResourceSaver.save(packed, path)
What the level shows after the coin changes, Godot 4.7.2 Python · instancing_session.py
from session import files, run

files("project.godot", "instancing.gd", "plain_level.gd")
# Godot gives every node a random unique_id when it first saves a scene; the session shows those ids as <random id>.
# The last two commands show a common mistake: a level saved after a plain instantiate().
run("""
godot --headless --no-header --script instancing.gd
cat level.tscn
cat coin.tscn
godot --headless --no-header --script plain_level.gd
cat plain_level.tscn
""", mask=[(r"unique_id=\d+", "unique_id=<random id>")])

Output

$ godot --headless --no-header --script instancing.gd
Coin1 at (0.0, 0.0) is #87ceeb
Coin2 at (40.0, 0.0) is #87ceeb
Coin3 at (80.0, 0.0) is #87ceeb
Coin4 at (120.0, 0.0) is #87ceeb
Coin5 at (160.0, 0.0) is #ff0000
$ cat level.tscn
[gd_scene format=3]

[ext_resource type="PackedScene" path="res://coin.tscn" id="1_u52ul"]

[node name="Level" type="Node2D" unique_id=<random id>]

[node name="Coin1" parent="." unique_id=<random id> instance=ExtResource("1_u52ul")]

[node name="Coin2" parent="." unique_id=<random id> instance=ExtResource("1_u52ul")]
position = Vector2(40, 0)

[node name="Coin3" parent="." unique_id=<random id> instance=ExtResource("1_u52ul")]
position = Vector2(80, 0)

[node name="Coin4" parent="." unique_id=<random id> instance=ExtResource("1_u52ul")]
position = Vector2(120, 0)

[node name="Coin5" parent="." unique_id=<random id> instance=ExtResource("1_u52ul")]
modulate = Color(1, 0, 0, 1)
position = Vector2(160, 0)
$ cat coin.tscn
[gd_scene format=3]

[node name="Coin" type="Node2D" unique_id=<random id>]
modulate = Color(0.5294118, 0.80784315, 0.92156863, 1)
$ godot --headless --no-header --script plain_level.gd
$ cat plain_level.tscn
[gd_scene format=3]

[ext_resource type="PackedScene" path="res://coin.tscn" id="1_buv6t"]

[node name="Level" type="Node2D" unique_id=<random id>]

[node name="Coin1" type="Node2D" parent="." unique_id=<random id> instance=ExtResource("1_buv6t")]
modulate = Color(0.5294118, 0.80784315, 0.92156863, 1)

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 instancing_session.py

The first five lines are the result: Coin1 to Coin4 are #87ceeb, sky blue, because they never changed their colour, and Coin5 is still #ff0000, red. In the editor, an overridden property shows a revert arrow beside it; clicking it deletes the override, and the instance follows its scene again.

Key idea

An instance takes every property from its scene except the ones you changed on it, and those overrides are kept even when the scene changes.

Checkpoint

Check: Instances and overrides

  1. Question 1 of 2 A level has ten instances of tree.tscn. You scale instance 3 to 1.5 in the level, then edit tree.tscn: scale 2 and a darker colour. What does instance 3 show?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Scale 1.5 and the darker colour

    Each property of an instance is either an override, stored in the level, or taken from the scene. Instance 3 overrides only its scale, so it keeps 1.5 and takes the new colour.

  2. Question 2 of 2 What does the revert arrow beside Coin5's modulate do?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: It deletes the override, so Coin5 takes the colour of coin.tscn again

    An overridden property shows a revert arrow in the Inspector. Clicking it removes the override, and the instance follows its scene for that property again.

A .tscn file lists nodes and stores only what differs

The same run printed level.tscn and coin.tscn, and a third file that the mistake at the end of this section explains. A .tscn file is the text form of a scene, made to be readable and to diff well in version control. Read level.tscn from the top, using this map:

Line in level.tscn What it records
[gd_scene format=3] the file descriptor: a scene saved by Godot 4 (Godot 3 wrote format=2)
[ext_resource type="PackedScene" path="res://coin.tscn" id="1_u52ul"] a file the scene uses, with a local id
[node name="Level" type="Node2D" …] the root node: a name and a type, no parent
[node name="Coin2" parent="." … instance=ExtResource("1_u52ul")] an instance of coin.tscn under the root, with no type of its own
position = Vector2(40, 0) an override: a property that differs from the instanced scene

Look at what is not there. Coin1 has no property lines at all, because nothing about it differs from coin.tscn. None of the instances stores a colour except Coin5, which is why the recolour reached the other four. coin.tscn holds one property, modulate; every other property of its node is at its default and is not written, the same rule as in project.godot. parent="." means a child of the scene’s root, and deeper nodes write a path such as parent="Arm/Hand".

New in Godot 4.6

Every node line carries a unique_id: scenes saved with 4.6 or later give each node a unique internal id, so that Godot can follow it through renames and moves. The ids are random when a node is first saved, which is why the session shows them as <random id>.

A scene saved by an older Godot has no ids yet and may carry load_steps= in its first line, which 4.6 and later ignore; it gets its ids the next time it is saved.

Instancing from code without the editor's state

A script that instances a scene with a plain instantiate() and then packs and saves the result stores the scene’s own values as overrides. plain_level.gd, below, does that, and the session’s last file shows the result: in plain_level.tscn, Coin1 has a type of its own and the coin’s modulate, so a later recolour of coin.tscn would never reach it. instancing.gd passes PackedScene.GEN_EDIT_STATE_INSTANCE, which the editor uses and only editor builds have, so only real overrides are saved.

plain_level.gd: the mistake, a level saved after a plain instantiate() GD · plain_level.gd
extends SceneTree

# The mistake: one coin instanced with a plain instantiate(), then the level packed and saved.
func _initialize() -> void:
	var coin_scene := load("res://coin.tscn") as PackedScene
	var level := Node2D.new()
	level.name = "Level"
	var coin := coin_scene.instantiate() as Node2D
	coin.name = "Coin1"
	level.add_child(coin)
	coin.owner = level
	var packed := PackedScene.new()
	packed.pack(level)
	ResourceSaver.save(packed, "res://plain_level.tscn")
	level.free()
	quit()

Checkpoint

Check: Reading a .tscn file

  1. Question 1 of 2 A level.tscn holds these two lines and nothing else for Coin7. What do they say about Coin7?

    Read the code, then choose one answer.

    [node name="Coin7" parent="." unique_id=1629170331 instance=ExtResource("1_u52ul")]
    
    [node name="Coin8" parent="." unique_id=1873390524 instance=ExtResource("1_u52ul")]
    Show the answer: Question 1 of 2

    Answer: Coin7 is an instance of the scene with id 1_u52ul and differs from it in nothing

    instance=ExtResource("1_u52ul") points at the [ext_resource] with that id, the coin scene. A node line with no properties under it is an exact copy of the scene it instances.

  2. Question 2 of 2 Why does level.tscn store a colour for Coin5 and for no other coin?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Only Coin5 overrode modulate; the others take theirs from coin.tscn

    Overrides are the only per-instance data in level.tscn. That is exactly what let the recolour of coin.tscn reach Coin1 to Coin4 and not Coin5.

Inherited scenes and editable children change a scene without touching it

Overrides on an instance work in one place. Two more tools let you change a scene’s insides without editing the scene itself. An inherited scene: A scene built on a base scene with Scene > New Inherited Scene. It saves only its differences from the base and follows every other change made to the base. is a new scene built on a base: Scene > New Inherited Scene creates one whose root is an instance of the base, and it saves only its differences. Here fast_enemy.tscn inherits from enemy.tscn, a Node2D with a Body sprite and a Name label, halves the body and gives the label its own text. The session then edits the base twice, the second time by renaming its Name node:

An inherited scene while its base changes, Godot 4.7.2 Python · inherit_session.py
from session import files, run

files("slimes=.")
# enemy.tscn is the base scene; fast_enemy.tscn inherits from it, as Scene > New Inherited Scene makes it.
run("""
godot --headless --quiet --import
cat fast_enemy.tscn
godot --headless --no-header --script show_enemies.gd
godot --headless --no-header --script edit_base.gd -- move
godot --headless --no-header --script show_enemies.gd
godot --headless --no-header --script edit_base.gd -- rename
godot --headless --no-header --script show_enemies.gd
""")

Output

$ godot --headless --quiet --import
$ cat fast_enemy.tscn
[gd_scene format=3 uid="uid://bfdge71m070fq"]

[ext_resource type="PackedScene" uid="uid://myliukswiblc" path="res://enemy.tscn" id="1_enemy"]

[node name="FastEnemy" unique_id=971141019 instance=ExtResource("1_enemy")]

[node name="Body" parent="." index="0" unique_id=1998074536]
scale = Vector2(0.5, 0.5)

[node name="Name" parent="." index="1" unique_id=1428043951]
text = "Fast slime"
$ godot --headless --no-header --script show_enemies.gd
Enemy: Body at (0.0, 0.0), scale (1.0, 1.0); Name "Slime"
FastEnemy: Body at (0.0, 0.0), scale (0.5, 0.5); Name "Fast slime"
$ godot --headless --no-header --script edit_base.gd -- move
$ godot --headless --no-header --script show_enemies.gd
Enemy: Body at (0.0, -8.0), scale (1.0, 1.0); Name "Big slime"
FastEnemy: Body at (0.0, -8.0), scale (0.5, 0.5); Name "Fast slime"
$ godot --headless --no-header --script edit_base.gd -- rename
$ godot --headless --no-header --script show_enemies.gd
Enemy: Body at (0.0, -8.0), scale (1.0, 1.0); Title "Big slime"
WARNING: res://fast_enemy.tscn: A node in the scene this one inherits from has been removed or moved, so a recovery process needs to take place. Please re-save this scene to avoid the cost of this process next time.
     at: instantiate (scene/resources/packed_scene.cpp:292)
     GDScript backtrace (most recent call first):
         [0] _initialize (res://show_enemies.gd:6)
FastEnemy: Body at (0.0, -8.0), scale (0.5, 0.5); Title "Fast slime"

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 inherit_session.py

The first edit moved the base’s Body up 8 pixels and changed its label to “Big slime”. FastEnemy took the new position, because it never overrode it, and kept its own scale and its own “Fast slime”. The second edit renamed Name to Title in the base, which before 4.6 could break the inherited scene’s change to that node. Godot 4.7.2 found the renamed node, as the unique ids of 4.6 allow, printed a warning asking you to re-save the inherited scene, and applied “Fast slime” to Title.

editable children: An option on one instance that shows its inner nodes in the scene holding it, so they can be changed for that instance alone without editing the instanced scene. serve a single instance instead: right-click an instance in the Scene dock, choose Editable Children, and its inner nodes appear in that scene for you to change. This level places one enemy as Guard and changes its label only there:

guard_level.gd: one instance with editable children GD · slimes/guard_level.gd
extends SceneTree

# A level with one enemy whose children are made editable, and one of them changed only in this level.
func _initialize() -> void:
	var level := Node2D.new()
	level.name = "Level"
	var guard := (load("res://enemy.tscn") as PackedScene).instantiate(PackedScene.GEN_EDIT_STATE_INSTANCE)
	guard.name = "Guard"
	level.add_child(guard)
	guard.owner = level
	level.set_editable_instance(guard, true)
	guard.get_node("Name").text = "Gate guard"
	var packed := PackedScene.new()
	packed.pack(level)
	ResourceSaver.save(packed, "res://level.tscn")
	level.free()
	print(FileAccess.get_file_as_string("res://level.tscn"))
	quit()
The level a script with editable children saves, Godot 4.7.2 Python · editable_session.py
from session import files, run

files("slimes=.")
# Godot gives every new node a random unique_id when it first saves a scene; the session shows those ids as <random id>.
run("""
godot --headless --quiet --import
godot --headless --no-header --script guard_level.gd
""", mask=[(r"unique_id=\d+", "unique_id=<random id>")])

Output

$ godot --headless --quiet --import
$ godot --headless --no-header --script guard_level.gd
[gd_scene format=3]

[ext_resource type="PackedScene" path="res://enemy.tscn" id="1_u52ul"]

[node name="Level" type="Node2D" unique_id=<random id>]

[node name="Guard" parent="." unique_id=<random id> instance=ExtResource("1_u52ul")]

[node name="Name" parent="Guard" index="1" unique_id=<random id>]
text = "Gate guard"

[editable path="Guard"]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 editable_session.py

The override is stored as a node line with parent="Guard", an inner node of the instance, and the last line, [editable path="Guard"], records that the instance’s children are editable. enemy.tscn itself did not change.

You want Use Where the change is saved
One instance to differ in a property of its root an override in the Inspector the scene that holds the instance
One instance to differ inside, in its child nodes Editable Children on that instance the scene that holds the instance
A family of variants that share a base and each change it an inherited scene per variant each variant’s own .tscn

Re-save after a warning

When Godot warns that a node of the base scene “has been removed or moved”, open the inherited scene and save it once. The recovery then happens one last time, and the file records the new node.

Checkpoint

Check: Inherited scenes and editable children

  1. Question 1 of 2 fast_enemy.tscn inherits from enemy.tscn and overrides the Body's scale. The base moves the Body up 8 pixels. What does FastEnemy's Body do?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Moves up 8 pixels and keeps its own scale

    After the first edit the run printed FastEnemy's Body at (0.0, -8.0) with scale (0.5, 0.5): the new position from the base, the scale from its own override.

  2. Question 2 of 2 You make an enemy instance's children editable in level.tscn and change its label. Where is that change saved?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: In level.tscn, as a node line with parent="Guard" and an [editable path="Guard"] entry

    The saved level.tscn holds [node name="Name" parent="Guard" …] with the new text and [editable path="Guard"] at the end, while enemy.tscn is unchanged.

Practise: build a player and a level, then check them

Build these two scenes in your own Godot 4.7.2 project, in the editor:

  1. player.tscn: a CharacterBody2D root named Player, with a Sprite2D named Sprite and a CollisionShape2D named Hitbox as its children.
  2. level.tscn: a Node2D root named Level, with one instance of player.tscn, still named Player, moved to position (64, 32) in the Inspector.
  3. Save check_scenes.gd in the project’s folder and run godot --headless --script check_scenes.gd -- level.tscn in a terminal there.
check_scenes.gd: the checker of this task GD · practice/check_scenes.gd
extends SceneTree

# Checks player.tscn and a level against the practice task:
# godot --headless --script check_scenes.gd -- level.tscn
var passed := 0
var failed := 0

func _initialize() -> void:
	var args := OS.get_cmdline_user_args()
	var level_path := "res://" + (args[0] if args.size() > 0 else "level.tscn")

	var player := (load("res://player.tscn") as PackedScene).get_state()
	_check(player.get_node_name(0) == &"Player" and player.get_node_type(0) == &"CharacterBody2D",
		"player.tscn: the root is a CharacterBody2D named Player")
	_check(_child_of_root(player, "Sprite") == &"Sprite2D", "player.tscn: a Sprite2D named Sprite under the root")
	_check(_child_of_root(player, "Hitbox") == &"CollisionShape2D", "player.tscn: a CollisionShape2D named Hitbox under the root")

	var level := (load(level_path) as PackedScene).get_state()
	_check(level.get_node_name(0) == &"Level" and level.get_node_type(0) == &"Node2D", "level: the root is a Node2D named Level")
	var index := _index_of_child(level, "Player")
	var instance := level.get_node_instance(index) if index >= 0 else null
	_check(instance != null and instance.resource_path == "res://player.tscn", "level: Player is an instance of player.tscn")
	_check(instance != null and _property(level, index, "position") == Vector2(64, 32), "level: the instance overrides position to (64, 32)")

	print("%d of %d checks passed" % [passed, passed + failed])
	quit(1 if failed else 0)

func _check(ok: bool, what: String) -> void:
	if ok:
		passed += 1
		print("PASS ", what)
	else:
		failed += 1
		print("FAIL ", what)

func _child_of_root(state: SceneState, child: String) -> StringName:
	var i := _index_of_child(state, child)
	return state.get_node_type(i) if i >= 0 else &""

func _index_of_child(state: SceneState, child: String) -> int:
	for i in state.get_node_count():
		if state.get_node_path(i) == NodePath("./" + child):
			return i
	return -1

func _property(state: SceneState, node: int, property: String) -> Variant:
	for p in state.get_node_property_count(node):
		if state.get_node_property_name(node, p) == StringName(property):
			return state.get_node_property_value(node, p)
	return null

Here is the checker run on two scenes that meet the task, and on a level whose Player was added as a plain CharacterBody2D instead of an instance:

The checker on a correct level and on a wrong one, Godot 4.7.2 Python · check_session.py
from session import files, run

# The checker of the practice task, run on two scenes that meet the task and on a level whose Player is not an instance.
files("practice=.")
run("""
godot --headless --quiet --import
godot --headless --no-header --script check_scenes.gd -- level.tscn
!godot --headless --no-header --script check_scenes.gd -- level_without_instance.tscn
""")

Output

$ godot --headless --quiet --import
$ godot --headless --no-header --script check_scenes.gd -- level.tscn
PASS player.tscn: the root is a CharacterBody2D named Player
PASS player.tscn: a Sprite2D named Sprite under the root
PASS player.tscn: a CollisionShape2D named Hitbox under the root
PASS level: the root is a Node2D named Level
PASS level: Player is an instance of player.tscn
PASS level: the instance overrides position to (64, 32)
6 of 6 checks passed
$ godot --headless --no-header --script check_scenes.gd -- level_without_instance.tscn
PASS player.tscn: the root is a CharacterBody2D named Player
PASS player.tscn: a Sprite2D named Sprite under the root
PASS player.tscn: a CollisionShape2D named Hitbox under the root
PASS level: the root is a Node2D named Level
FAIL level: Player is an instance of player.tscn
FAIL level: the instance overrides position to (64, 32)
4 of 6 checks passed
[exit status 1]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 check_session.py

Every line says what it checked, and the exit status is 1 when any check fails, so the checker also works in a build script. Your output should end with 6 of 6 checks passed. Then go over the four parts of this page once more; each question explains its answer as soon as you choose, starting from the part you have got wrong most often:

Practice that adapts to you

Practise this lesson

8 questions about 4 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 8 Which describes a scene in Godot?

    Choose one answer.

    Show the answer: Question 1 of 8

    Answer: A tree of nodes with one root, saved to a file such as player.tscn

    Nodes are the building blocks; a scene is a saved tree of them with one root node, which can be instanced in other scenes as often as needed.

  2. Question 2 of 8 When a game runs, what is above the root node of your main scene in the scene tree?

    Choose one answer.

    Show the answer: Question 2 of 8

    Answer: A Window named root, created by the engine

    Every running game has one scene tree with a Window called root at the top. Scenes you run or add appear under it, which is why node paths start with /root.

  3. Question 3 of 8 A level has 40 instances of coin.tscn and you turned three of them red. You then make coin.tscn spin. Which coins spin?

    Choose one answer.

    Show the answer: Question 3 of 8

    Answer: All 40, the red ones included

    An override covers one property. The three red coins still take every other property and change from coin.tscn, so all 40 spin.

  4. Question 4 of 8 Why build a level from 40 instances of coin.tscn rather than 40 hand-made coin nodes?

    Choose one answer.

    Show the answer: Question 4 of 8

    Answer: A change to coin.tscn reaches all 40 at once, and each coin still stores only its own differences

    Scenes are Godot's way of reusing things. The documentation describes building one scene per element of the game and nesting instances to assemble it, which keeps changes in one place.

  5. Question 5 of 8 A scene file starts with [gd_scene format=2]. What does that tell you?

    Choose one answer.

    Show the answer: Question 5 of 8

    Answer: It was saved by Godot 3

    The first line of a .tscn file is its descriptor. format=3 means a Godot 4 scene and format=2 a Godot 3 one; a Godot 4.6 or later file also has a unique_id on every node line.

  6. Question 6 of 8 A node line says parent="Arm/Hand". Where is this node?

    Choose one answer.

    Show the answer: Question 6 of 8

    Answer: A child of Hand, which is a child of Arm, which is a child of the scene's root

    The TSCN documentation says parent paths are written from the root without its name: "." for the root, "Arm" for its child Arm, and "Arm/Hand" for Hand inside Arm.

  7. Question 7 of 8 You need a slime, a fast slime and an armoured slime that share most nodes and differ in a few properties. Which fits best?

    Choose one answer.

    Show the answer: Question 7 of 8

    Answer: One base scene and an inherited scene for each variant

    Inherited scenes are made for families of variants: the base holds what they share, and each variant's .tscn holds only what makes it different, as fast_enemy.tscn did.

  8. Question 8 of 8 Godot warns that a node in the scene fast_enemy.tscn inherits from "has been removed or moved". What do you do?

    Choose one answer.

    Show the answer: Question 8 of 8

    Answer: Check that the variant still looks right, then open fast_enemy.tscn and save it once

    After the base renamed Name to Title, Godot 4.7.2 recovered the override and still printed "Fast slime" under Title. Saving the inherited scene once records the new name so the recovery is no longer needed.

Interview questions

Warm-up: what is the difference between a node and a scene in Godot?

A node is a single building block with one job, such as a Sprite2D that draws a picture or a Camera2D that decides what the screen shows; it has a name, properties and children. A scene is a tree of nodes with one root, saved to a file such as player.tscn, that you can instance as often as you like: in the editor an instance looks like one node, with the scene’s nodes inside. A running game is itself one big tree, the scene tree, with a Window called root at the top, so every node has a path such as /root/Player/Camera. In short: nodes are the parts, scenes are the saved, reusable assemblies of parts.

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Fresher to mid

    A designer recoloured coin.tscn, and 37 of the 40 coins in a level changed, but 3 kept the old colour. Nobody edited the coins' scripts. Why did those 3 not change, how do you find them quickly, and how do you fix them without losing anything else they were meant to keep?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to senior

    A game needs 30 enemy types that share movement and health but differ in sprite, speed and hit points. Would you use one enemy scene with overrides on each instance, an inherited scene per type, or 30 separate scenes? Defend your choice, and say what happens when the shared part changes.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • A node does one job and has a name, properties and children; a scene is a tree of nodes with one root, and in a running game every node has a path from /root, such as /root/Player/Camera.
  • An instance takes all its properties from its scene except its overrides, which survive changes to the scene and disappear when you revert them.
  • A .tscn file lists external resources and nodes and stores only properties that differ, from the defaults or from the instanced scene, plus a unique_id per node since 4.6.
  • Inherited scenes save only their differences from a base and follow its changes, and editable children let one instance change its inner nodes; neither touches the source scene.

Terms in this lesson

editable children
An option on one instance that shows its inner nodes in the scene holding it, so they can be changed for that instance alone without editing the instanced scene.
inherited scene
A scene built on a base scene with Scene > New Inherited Scene. It saves only its differences from the base and follows every other change made to the base.
instance
A copy of a saved scene placed inside another scene. It takes every property from its scene except the ones changed on it, its overrides, which survive later changes to the scene.
node
A building block of a Godot game with one job, such as drawing a picture or following the player with a camera. Every node has a name, properties and callbacks, and it can have child nodes.
scene
A tree of nodes with a single root node, saved to a file such as player.tscn so that it can be instanced as often as needed.
scene tree
The one tree of nodes of a running game, with a Window named root at the top. Every node in it has a path from the top, such as /root/Player/Camera.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.