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")
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:
Propiedades y señales vienen primero, seguidas de métodos.
Público viene antes de privado.
Callbacks virtuales vienen antes de la interfaz de clase.
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")
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:
Mal:
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.