Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

Guía de estilo de GDScript

Esta guía de estilo enumera las convenciones para escribir un GDscript elegante. La meta es motivar la escritura de un código limpio, legible y promover la consistencia entre proyectos, discusiones y tutoriales. Esperamos que esto también motive al desarrollo de herramientas de autoformateo.

Como GDScript es similar a Python, esta guía ha sido inspirada por la guía de estilo de programación PEP 8.

Las guías de estilos no están hechas con la intención de ser tratadas como reglamento estricto. Algunas veces no serás capaz de utilizar algunos de los lineamientos indicados, cuando eso suceda utiliza tu mejor criterio y busca la opinión de otros desarrolladores.

En general, mantener tu código consistente en tus proyectos y entre todos los de tu equipo es más importante que seguir esta guía al pié de la letra.

Nota

El editor de scripts de Godot usa muchas de estas convenciones por defecto. Deja que te ayude.

Aquí tenemos un ejemplo completo de una clase basada en esos lineamientos:

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


signal state_changed(previous, new)

@export var initial_state: Node
var is_active = true:
    set = set_is_active

@onready var _state = initial_state:
    set = set_state
@onready var _state_name = _state.name


func _init():
    add_to_group("state_machine")


func _enter_tree():
    print("this happens before the ready method!")


func _ready():
    state_changed.connect(_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.player_state_changed.emit(_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")
    state_changed.emit()


class State:
    var foo = 0

    func _init():
        print("Hello!")

Formateando

Codificación y caracteres especiales

  • Utiliza caracteres de salto de línea (LF) para separar líneas, no CRLF o CR. (por defecto del editor)

  • Utiliza un carácter de salto de línea al final de cada archivo. (por defecto del editor)

  • Usa la codificación UTF-8 sin Marca de orden de bytes. *( por defecto del editor) *

  • Usa Tabs en lugar de espacios para la indentación. *(por defecto del editor) *

Indentación

Cada nivel de la Indentación deberá uno mayor que el bloque que lo contiene.

Bien:

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

Mal:

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

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

Use 2 niveles de Indentación para distinguir las lineas de continuación de bloques regulares de código.

Bien:

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

Mal:

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

Las excepciones a esta regla son los arrays, diccionarios y enumeraciones. Usa una sangría simple para distinguir líneas de continuación:

Bien:

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

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

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

Mal:

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

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

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

Coma final

Utiliza una coma final en la última línea de arrays, diccionarios y enumeraciones. Esto resulta en una refactorización más sencilla y mejores indicaciones de diferencias (diffs) al trabajar con control de versions, ya que la última línea no necesita ser modificada cuando se agregan nuevos elementos.

Bien:

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

Mal:

enum Tiles {
    TILE_BRICK,
    TILE_FLOOR,
    TILE_SPIKE,
    TILE_TELEPORT
}

Las comas de finalización no son necesarias en listas de una sola línea, así que no las agregues en ese caso.

Bien:

enum Tiles {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT}

Mal:

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

Lineas en blanco

Envuelve las funciones y definiciones de clases con dos lineas vacías:

func heal(amount):
    health += amount
    health = min(health, max_health)
    health_changed.emit(health)


func take_damage(amount, effect=null):
    health -= amount
    health = max(0, health)
    health_changed.emit(health)

use una linea blanca dentro de las funciones para separar secciones lógicas.

Nota

En la referencia de clases y en fragmentos cortos de código en esta documentación, utilizamos una sola línea entre las definiciones de clases y funciones.

Longitud de línea

Mantiene líneas de código individuales por debajo de los 100 caracteres.

Si puedes, trata de mantener las líneas debajo de 80 caracteres. Esto ayuda a que se pueda leer el código en pantallas pequeñas y posibilita tener abiertos dos scripts lado a lado en un editor de texto externo. Por ejemplo, cuando se está mirando una revisión diferencial.

Una declaración/instrucción por linea

Nunca combine múltiples declaraciones en una única línea. No, programadores de C, ni siquiera declaraciones condicionales en una única línea.

Bien:

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

if flag:
    print("flagged")

Mal:

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

if flag: print("flagged")

La única excepción a esta regla es el operador ternario:

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

Formatear las sentencias de varias líneas para facilitar su lectura

Cuando se tiene sentencias if particularmente extensas o expresiones ternarias anidadas, envolverlas en varias líneas mejora la legibilidad. Dado que las líneas de continuación siguen formando parte de la misma expresión, deben utilizarse dos niveles de sangría en lugar de uno.

GDScript permite envolver las sentencias con múltiples líneas utilizando paréntesis o barras invertidas. En esta guía de estilo se prefieren los paréntesis porque facilitan la refactorización. Con las barras invertidas, tienes que asegurarte de que la última línea nunca contenga una barra invertida al final. Con los paréntesis, no tiene que preocuparse de que la última línea tenga una barra invertida al final.

Cuando se envuelve una expresión condicional en múltiples líneas, las palabras clave and/or deben ser colocadas al comienzo de la continuación de la línea, no al final de la anterior.

Bien:

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

Mal:

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

Evita paréntesis innecesarios

Evite paréntesis en expresiones y condicionales. A menos que sean necesarios para el orden de las operaciones o para envolver multilineas, solo reducen la legibilidad.

Bien:

if is_colliding():
    queue_free()

Mal:

if (is_colliding()):
    queue_free()

Operador booleanos

Usa la versión en inglés de operadores booleanos, ya que son más accesibles:

  • Usa and en lugar de &&.

  • Usa or en lugar de ||.

  • Use not instead of !.

También puedes usar paréntesis alrededor de operadores booleanos para evitar cualquier ambigüedad. Esto hace que expresiones largas sean más fáciles de leer.

Bien:

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

Mal:

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

Espaciado de comentarios

Regular comments (#) and documentation comments (##) should start with a space, but not code that you comment out. Additionally, code region comments (#region/#endregion) must follow that precise syntax, so they should not start with a space.

Using a space for regular and documentation comments helps differentiate text comments from disabled code.

Bien:

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

Mal:

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

Nota

In the script editor, to toggle commenting of the selected code, press Ctrl + K. This feature adds/removes a single # sign before any code on the selected lines.

Espacio en blanco

Utiliza siempre un espacio entre los operadores y después de las comas. Evita los espacios adicionales en las referencias de los diccionarios y las llamadas de función.

Bien:

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

Mal:

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

No uses espacios para alinear la verticalidad de las expresiones:

x        = 100
y        = 100
velocity = 500

Comillas

Utiliza comillas dobles a menos que las comillas simple hagan positble escapar menos caracteres en una cadena dada. Revisa los ejemplos a continuación:

# 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\"")

Numeros

No omitas el cero inicial o final en los números reales. De lo contrario, esto los hace menos legibles y más difíciles de distinguir de los números enteros a simple vista.

Bien:

var float_number = 0.234
var other_float_number = 13.0

Mal:

var float_number = .234
var other_float_number = 13.

Use minúsculas para las letras en números hexadecimales, ya que su menor altura hace que el número sea más fácil de leer.

Bien:

var hex_number = 0xfb8c0b

Mal:

var hex_number = 0xFB8C0B

Aprovechen los subrayados de GDScript en los literales para hacer más legibles los grandes números.

Bien:

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

Mal:

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

Convenciones para la definición de nombres

Estas convenciones de nombres siguen el estilo de Godot Engine. Romperlas hará que tu código choque con las convenciones de nomenclaturas incorporadas, lo que crea un código inconsistente.

Nombres de archivo

Utiliza snake_case para nombres de archivos. Para clases con nombre, convierte el nombre de clase en PascalCase a snake_case:

# This file should be saved as `weapon.gd`.
class_name Weapon
extends Node
# This file should be saved as `yaml_parser.gd`.
class_name YAMLParser
extends Object

Esto es consistente con cómo son nombrados los archivos en el código fuente C++ de Godot. Esto también evita problema de sensibilidad a mayúsculas que pueden surgir cuando se exporta un proyecto desde Windows a otras plataformas.

Clases y Nodos

Utiliza PascalCase para nombres de clases y nodos:

extends CharacterBody3D

También usa el PascalCase al cargar una clase en una constante o una variable:

const Weapon = preload("res://weapon.gd")

Funciones y Variables

Usa snake_case para nombrar funciones y variables:

var particle_effect
func load_level():

Utilice un solo símbolo de subrayado (_) para los métodos virtuales que el usuario debe sobreescribir, funciones privadas y variables privadas:

var _counter = 0
func _recalculate_path():

Señales

Usa el tiempo pasado para nombrar señales:

signal door_opened
signal score_changed

Constantes y enumerables

Escribe constantes en CONSTANT_CASE, todas en mayusculas con el símbolo de subrayado (_) para separar palabras:

const MAX_SPEED = 200

Usa PascalCase para nombres de enums y CONSTANT_CASE para sus miembros, ya que son constantes:

enum Element {
    EARTH,
    WATER,
    AIR,
    FIRE,
}

Orden de código

Esta sección inicial se enfoca en el orden del código. Para el formato, ver Formateando. Para convenciones de nombre, ver Convenciones para la definición de nombres.

Sugerimos organizar el código GDScript de este modo:

01. @tool
02. class_name
03. extends
04. # docstring

05. signals
06. enums
07. constants
08. @export variables
09. public variables
10. private variables
11. @onready variables

12. optional built-in virtual _init method
13. optional built-in virtual _enter_tree() method
14. built-in virtual _ready method
15. remaining built-in virtual methods
16. public methods
17. private methods
18. subclasses

Optimizamos el orden para hacer más fácil leer el código desde arriba hacia abajo, para ayudar a que los desarrolladores que leen el código por primera vez entiendan cómo funciona y para evitar errores vinculados al orden de declaración de variables.

Este orden de código sigue cuatros reglas:

  1. Propiedades y señales vienen primero, seguidas de métodos.

  2. Público viene antes de privado.

  3. Callbacks virtuales vienen antes de la interfaz de clase.

  4. Las funciones de construcción e inicialización de objetos, _init and _ready, vienen antes de funciones que modifican el objeto en tiempo de ejecución.

Declaración de la clase

If the code is meant to run in the editor, place the @tool annotation on the first line of the script.

Follow with the class_name if necessary. You can turn a GDScript file into a global type in your project using this feature. For more information, see GDScript reference.

Then, add the extends keyword if the class extends a built-in type.

Following that, you should have the class's optional documentation comments. You can use that to explain the role of your class to your teammates, how it works, and how other developers should use it, for example.

class_name MyNode
extends Node
## A brief description of the class's role and functionality.
##
## The description of the script, what it can do,
## and any further detail.

Señales y propiedades

Escribe la declaración de señales, seguida por propiedades, es decir, las variables miembro después de la docstring.

Los enums deberán venir después de las señales, ya que puedes usarlos como "export hints" para otras propiedades.

Luego, escribe constantes, variables exportadas, públicas, privades y onready, en ese orden.

signal player_spawned(position)

enum Jobs {KNIGHT, WIZARD, ROGUE, HEALER, SHAMAN}

const MAX_LIVES = 3

@export var job: Jobs = Jobs.KNIGHT
@export var max_health = 50
@export var attack = 5

var health = max_health:
    set(new_health):
        health = new_health

var _speed = 300.0

@onready var sword = get_node("Sword")
@onready var gun = get_node("Gun")

Nota

El compilador GDScript evalúa variables onready justo antes del callback _ready. Puedes usar esto para guardar referencias a dependencias de nodos, es decir, para obtener nodos en la escena de lo que tu clase depende. Esto es lo que muestra el ejemplo anterior.

Varaibles miembro

No declares variables miembro si sólo serán usadas localmente en un método, esto hace el código más difícil de seguir. En lugar de esto declaralas como varaibles locales en el cuerpo del método.

Variables locales

Declara las variables locales tan cerca como puedas de su primer uso. Esto hace que sea más fácil de seguir el código sin tener que desplazar el código demasiado para encontrar dónde fue declarada la variable.

Métodos y funciones estáticas

Después las propiedades de la clase vienen los métodos.

Comienza por el método callback _init() que el motor llamará cuando se crea el objeto en memoria. Seguido del callback _ready(), el que Godot llama cuando un nodo es agregado al árbol de escena.

Estas funciones deben ser lo primero porque muestran cómo se inicializa el objeto.

Otros callbacks virtuales integrados, como _unhandled_input() y _physics_process, van después. Estos controlan el bucle principal del objeto y las interacciones con el motor de juegos.

El resto de la interfaz de la clase, métodos públicos y privados, vienen después en ese orden.

func _init():
    add_to_group("state_machine")


func _ready():
    state_changed.connect(_on_state_changed)
    _state.enter()


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


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.player_state_changed.emit(_state.name)


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

Tipado estáticas

Desde Godot 3.1, GDScript soporta tipado estático opcional.

Tipos declarados

Para declarar el tipo de una variable, usa <variable>: <type>:

var health: int = 0

Para declarar el tipo de retorno de una función, usa -> <tipo>:

func heal(amount: int) -> void:

Tipos inferidos

In most cases you can let the compiler infer the type, using :=. Prefer := when the type is written on the same line as the assignment, otherwise prefer writing the type explicitly.

Bien:

var health: int = 0 # The type can be int or float, and thus should be stated explicitly.
var direction := Vector3(1, 2, 3) # The type is clearly inferred as Vector3.

Include the type hint when the type is ambiguous, and omit the type hint when it's redundant.

Mal:

var health := 0 # Typed as int, but it could be that float was intended.
var direction: Vector3 = Vector3(1, 2, 3) # The type hint has redundant information.

# What type is this? It's not immediately clear to the reader, so it's bad.
var value := complex_function()

In some cases, the type must be stated explicitly, otherwise the behavior will not be as expected because the compiler will only be able to use the function's return type. For example, get_node() cannot infer a type unless the scene or file of the node is loaded in memory. In this case, you should set the type explicitly.

Bien:

@onready var health_bar: ProgressBar = get_node("UI/LifeBar")

Alternativamente, puedes usar la palabra clave as para castear el tipo de retorno, y ese tipo será usado para inferir en el tipo de la variable.

@onready var health_bar := get_node("UI/LifeBar") as ProgressBar
# health_bar will be typed as ProgressBar

Esta opción también se considera más segura en cuanto al tipo que la primera.

Mal:

# The compiler can't infer the exact type and will use Node
# instead of ProgressBar.
@onready var health_bar := get_node("UI/LifeBar")