Classes, Inheritance & Inner Classes
13.1 Every script is a class
A .gd file defines a class. extends sets the base class (defaults to RefCounted if omitted). class_name registers it globally so other scripts can use it as a type and the editor shows it in the *Create Node* dialog.
# res://scripts/character.gd
class_name Character
extends CharacterBody2D
signal died
@export var max_health := 100
var health := max_health
func take_damage(amount: int) -> void:
health -= amount
if health <= 0:
die()
func die() -> void:
died.emit()
queue_free()13.2 Inheritance and super
Override a method by defining one with the same name (signature must be compatible).
Engine virtual methods (_ready, _process) in parent scripts are not automatically skipped — in Godot 4, a child's _ready() overrides the parent's; call super() to run both.
Godot 3 note: 3.x used .take_damage(amount) to call the parent method and automatically called parent _ready/_process callbacks. Godot 4 uses super and requires explicit calls.
# res://scripts/boss.gd
class_name Boss
extends Character # extend a class_name...
# extends "res://scripts/character.gd" # ...or a script path
var phase := 1
func take_damage(amount: int) -> void:
super.take_damage(amount / 2) # call the parent implementation
if health < max_health / 2 and phase == 1:
phase = 2
func die() -> void:
print("The boss falls!")
super() # shorthand for calling the same method on parent13.3 Constructors: _init
_init runs when the object is created (before it enters the tree, before @onready).
Scripts attached to nodes in scenes are instantiated with no arguments, so give every _init parameter a default (or avoid _init parameters for node scripts).
Call the parent constructor with super(args) inside _init if needed.
class_name Item
extends RefCounted
var name: String
var price: int
func _init(p_name: String = "Unknown", p_price: int = 0) -> void:
name = p_name
price = p_price
# Usage
var sword := Item.new("Sword", 150)13.4 Inner classes
Inner classes are accessible from other scripts via the outer class: Player.Stats.new().
extends Node
class Stats:
var strength := 10
var agility := 10
func power() -> int:
return strength * 2 + agility
class Weapon extends RefCounted:
var damage: int
func _init(d: int) -> void:
damage = d
func _ready() -> void:
var s := Stats.new()
print(s.power())
var w := Weapon.new(25)13.5 Object, RefCounted, Resource, Node — memory rules
You call free() manually Rare; low-level Last reference disappears (automatic) Data/helper classes Refcounted; can be saved to disk Data assets (Chapter 26)
You call queue_free() (or its parent is freed) Anything in the scene tree
Never call free() on a node during physics callbacks or signals — prefer queue_free(). Watch out for reference cycles between RefCounted objects (they leak); use weakref() to break them.
var n := Node.new()
add_child(n) # parent now owns it; freed with parent
n.queue_free() # safely delete at end of frame13.6 Abstract classes (4.5+)
Shape.new() is an error; abstract classes still work as types. On older versions, emulate with a method that calls push_error("not implemented").
@abstract
class_name Shape
extends RefCounted
@abstract func area() -> float # no body; subclasses must implement
class_name Circle extends Shape # (in another file)
var r := 1.0
func area() -> float: return PI * r * r13.7 Duck typing and introspection
Prefer typed checks (if body is Character:) where possible — they are faster and safer.
if body.has_method("take_damage"):
body.take_damage(10)
if "health" in body: # property check
print(body.health)
body.call("take_damage", 10)
body.set("speed", 300); body.get("speed")
var script: Script = get_script()
print(get_property_list().size())