GDScript style guide

This style guide lists conventions to write elegant GDScript. The goal is to encourage writing clean, readable code and promote consistency across projects, discussions, and tutorials. Hopefully, this will also support the development of auto-formatting tools.

Since GDScript is close to Python, this guide is inspired by Python's PEP 8 programming style guide.

Style guides aren't meant as hard rulebooks. At times, you may not be able to apply some of the guidelines below. When that happens, use your best judgment, and ask fellow developers for insights.

In general, keeping your code consistent in your projects and within your team is more important than following this guide to a tee.

Note

Godot's built-in script editor uses a lot of these conventions by default. Let it help you.

Here is a complete class example based on these guidelines:

class_name StateMachine
extends Node
# Hierarchical State machine for the player.
# Initializes states and delegates engine callbacks
# (_physics_process, _unhandled_input) to the state.


signal state_changed(previous, new)

export var initial_state = NodePath()
var is_active = true setget set_is_active

@onready var _state = get_node(initial_state) setget set_state
@onready var _state_name = _state.name


func _init():
    add_to_group("state_machine")


func _ready():
    connect("state_changed", self, "_on_state_changed")
    _state.enter()


func _unhandled_input(event):
    _state.unhandled_input(event)


func _physics_process(delta):
    _state.physics_process(delta)


func transition_to(target_state_path, msg={}):
    if not has_node(target_state_path):
        return

    var target_state = get_node(target_state_path)
    assert(target_state.is_composite == false)

    _state.exit()
    self._state = target_state
    _state.enter(msg)
    Events.emit_signal("player_state_changed", _state.name)


func set_is_active(value):
    is_active = value
    set_physics_process(value)
    set_process_unhandled_input(value)
    set_block_signals(not value)


func set_state(value):
    _state = value
    _state_name = _state.name


func _on_state_changed(previous, new):
    print("state changed")
    emit_signal("state_changed")

Formatting

Encoding and special characters

  • Use line feed (LF) characters to break lines, not CRLF or CR. (editor default)

  • Use one line feed character at the end of each file. (editor default)

  • Use UTF-8 encoding without a byte order mark. (editor default)

  • Use Tabs instead of spaces for indentation. (editor default)

Indentation

Each indent level should be one greater than the block containing it.

Good:

for i in range(10):
    print("hello")

Bad:

for i in range(10):
  print("hello")

for i in range(10):
        print("hello")

Use 2 indent levels to distinguish continuation lines from regular code blocks.

Good:

effect.interpolate_property(sprite, "transform/scale",
            sprite.get_scale(), Vector2(2.0, 2.0), 0.3,
            Tween.TRANS_QUAD, Tween.EASE_OUT)

Bad:

effect.interpolate_property(sprite, "transform/scale",
    sprite.get_scale(), Vector2(2.0, 2.0), 0.3,
    Tween.TRANS_QUAD, Tween.EASE_OUT)

Exceptions to this rule are arrays, dictionaries, and enums. Use a single indentation level to distinguish continuation lines:

Good:

var party = [
    "Godot",
    "Godette",
    "Steve",
]

var character_dict = {
    "Name": "Bob",
    "Age": 27,
    "Job": "Mechanic",
}

enum Tiles {
    TILE_BRICK,
    TILE_FLOOR,
    TILE_SPIKE,
    TILE_TELEPORT,
}

Bad:

var party = [
        "Godot",
        "Godette",
        "Steve",
]

var character_dict = {
        "Name": "Bob",
        "Age": 27,
        "Job": "Mechanic",
}

enum Tiles {
        TILE_BRICK,
        TILE_FLOOR,
        TILE_SPIKE,
        TILE_TELEPORT,
}

Trailing comma

Use a trailing comma on the last line in arrays, dictionaries, and enums. This results in easier refactoring and better diffs in version control as the last line doesn't need to be modified when adding new elements.

Good:

enum Tiles {
    TILE_BRICK,
    TILE_FLOOR,
    TILE_SPIKE,
    TILE_TELEPORT,
}

Bad:

enum Tiles {
    TILE_BRICK,
    TILE_FLOOR,
    TILE_SPIKE,
    TILE_TELEPORT
}

Trailing commas are unnecessary in single-line lists, so don't add them in this case.

Good:

enum Tiles {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT}

Bad:

enum Tiles {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT,}

Blank lines

Surround functions and class definitions with two blank lines:

func heal(amount):
    health += amount
    health = min(health, max_health)
    emit_signal("health_changed", health)


func take_damage(amount, effect=null):
    health -= amount
    health = max(0, health)
    emit_signal("health_changed", health)

Use one blank line inside functions to separate logical sections.

Note

We use a single line between classes and function definitions in the class reference and in short code snippets in this documentation.

Line length

Keep individual lines of code under 100 characters.

If you can, try to keep lines under 80 characters. This helps to read the code on small displays and with two scripts opened side-by-side in an external text editor. For example, when looking at a differential revision.

One statement per line

Never combine multiple statements on a single line. No, C programmers, not even with a single line conditional statement.

Good:

if position.x > width:
    position.x = 0

if flag:
    print("flagged")

Bad:

if position.x > width: position.x = 0

if flag: print("flagged")

The only exception to that rule is the ternary operator:

next_state = "fall" if not is_on_floor() else "idle"

Format multiline statements for readability

When you have particularly long if statements or nested ternary expressions, wrapping them over multiple lines improves readability. Since continuation lines are still part of the same expression, 2 indent levels should be used instead of one.

GDScript allows wrapping statements using multiple lines using parentheses or backslashes. Parentheses are favored in this style guide since they make for easier refactoring. With backslashes, you have to ensure that the last line never contains a backslash at the end. With parentheses, you don't have to worry about the last line having a backslash at the end.

When wrapping a conditional expression over multiple lines, the and/or keywords should be placed at the beginning of the line continuation, not at the end of the previous line.

Good:

var angle_degrees = 135
var quadrant = (
        "northeast" if angle_degrees <= 90
        else "southeast" if angle_degrees <= 180
        else "southwest" if angle_degrees <= 270
        else "northwest"
)

var position = Vector2(250, 350)
if (
        position.x > 200 and position.x < 400
        and position.y > 300 and position.y < 400
):
    pass

Bad:

var angle_degrees = 135
var quadrant = "northeast" if angle_degrees <= 90 else "southeast" if angle_degrees <= 180 else "southwest" if angle_degrees <= 270 else "northwest"

var position = Vector2(250, 350)
if position.x > 200 and position.x < 400 and position.y > 300 and position.y < 400:
    pass

Avoid unnecessary parentheses

Avoid parentheses in expressions and conditional statements. Unless necessary for order of operations or wrapping over multiple lines, they only reduce readability.

Good:

if is_colliding():
    queue_free()

Bad:

if (is_colliding()):
    queue_free()

Boolean operators

Prefer the plain English versions of boolean operators, as they are the most accessible:

  • Use and instead of &&.

  • Use or instead of ||.

You may also use parentheses around boolean operators to clear any ambiguity. This can make long expressions easier to read.

Good:

if (foo and bar) or baz:
    print("condition is true")

Bad:

if foo && bar || baz:
    print("condition is true")

Comment spacing

Regular comments should start with a space, but not code that you comment out. This helps differentiate text comments from disabled code.

Good:

# This is a comment.
#print("This is disabled code")

Bad:

#This is a comment.
# print("This is disabled code")

Note

In the script editor, to toggle the selected code commented, press Ctrl + K. This feature adds a single # sign at the start of the selected lines.

Whitespace

Always use one space around operators and after commas. Also, avoid extra spaces in dictionary references and function calls.

Good:

position.x = 5
position.y = target_position.y + 10
dict["key"] = 5
my_array = [4, 5, 6]
print("foo")

Bad:

position.x=5
position.y = mpos.y+10
dict ["key"] = 5
myarray = [4,5,6]
print ("foo")

Don't use spaces to align expressions vertically:

x        = 100
y        = 100
velocity = 500

Quotes

Use double quotes unless single quotes make it possible to escape fewer characters in a given string. See the examples below:

# Normal string.
print("hello world")

# Use double quotes as usual to avoid escapes.
print("hello 'world'")

# Use single quotes as an exception to the rule to avoid escapes.
print('hello "world"')

# Both quote styles would require 2 escapes; prefer double quotes if it's a tie.
print("'hello' \"world\"")

Numbers

Don't omit the leading or trailing zero in floating-point numbers. Otherwise, this makes them less readable and harder to distinguish from integers at a glance.

Good:

var float_number = 0.234
var other_float_number = 13.0

Bad:

var float_number = .234
var other_float_number = 13.

Use lowercase for letters in hexadecimal numbers, as their lower height makes the number easier to read.

Good:

var hex_number = 0xfb8c0b

Bad:

var hex_number = 0xFB8C0B

Take advantage of GDScript's underscores in literals to make large numbers more readable.

Good:

var large_number = 1_234_567_890
var large_hex_number = 0xffff_f8f8_0000
var large_bin_number = 0b1101_0010_1010
# Numbers lower than 1000000 generally don't need separators.
var small_number = 12345

Bad:

var large_number = 1234567890
var large_hex_number = 0xfffff8f80000
var large_bin_number = 0b110100101010
# Numbers lower than 1000000 generally don't need separators.
var small_number = 12_345

Naming conventions

These naming conventions follow the Godot Engine style. Breaking these will make your code clash with the built-in naming conventions, leading to inconsistent code.

File names

Use snake_case for file names. For named classes, convert the PascalCase class name to snake_case: