Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
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:
var array = [
1,
2,
3,
]
Mal:
var array = [
1,
2,
3
]
Las comas de finalización no son necesarias en listas de una sola línea, así que no las agregues en ese caso.
Bien:
var array = [1, 2, 3]
Mal:
var array = [1, 2, 3,]
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
Avoid combining multiple statements on a single line, including conditional statements, to adhere to the GDScript style guidelines for readability.
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
Always use one space around operators and after commas. Also, avoid extra spaces
in dictionary references and function calls. One exception to this is for
single-line dictionary declarations, where a space should be added after the
opening brace and before the closing brace. This makes the dictionary easier to
visually distinguish from an array, as the []
characters look close to
{}
with most fonts.
Bien:
position.x = 5
position.y = target_position.y + 10
dict["key"] = 5
my_array = [4, 5, 6]
my_dictionary = { key = "value" }
print("foo")
Mal:
position.x=5
position.y = mpos.y+10
dict ["key"] = 5
myarray = [4,5,6]
my_dictionary = {key = "value"}
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
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. As a summary table:
Tipo |
Convention |
Ejemplo |
---|---|---|
Nombres de archivo |
snake_case |
|
Class names |
PascalCase |
|
Node names |
PascalCase |
|
Funciones |
snake_case |
|
Variables |
snake_case |
|
Señales |
snake_case |
|
Constantes |
CONSTANT_CASE |
|
Enum names |
PascalCase |
|
Enum members |
CONSTANT_CASE |
|
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,
}
Write enums with each item on its own line. This allows adding documentation comments above each item more easily, and also makes for cleaner diffs in version control when items are added or removed.
Bien:
enum Element {
EARTH,
WATER,
AIR,
FIRE,
}
Mal:
enum Element { EARTH, WATER, AIR, FIRE }
Orden de código
This section focuses on code order. For formatting, see Formateando. For naming conventions, see Convenciones para la definición de nombres.
Sugerimos organizar el código GDScript de este modo:
01. @tool, @icon, @static_unload
02. class_name
03. extends
04. ## doc comment
05. signals
06. enums
07. constants
08. static variables
09. @export variables
10. remaining regular variables
11. @onready variables
12. _static_init()
13. remaining static methods
14. overridden built-in virtual methods:
1. _init()
2. _enter_tree()
3. _ready()
4. _process()
5. _physics_process()
6. remaining virtual methods
15. overridden custom methods
16. remaining methods
17. subclasses
And put the class methods and variables in the following order depending on their access modifiers:
1. public
2. private
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 optional @icon
then the class_name
if necessary. You can turn a
GDScript file into a global type in your project using class_name
. 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
GDScript evaluates @onready
variables right before the _ready
callback. You can use that to cache node dependencies, that is to say, to get
child nodes in the scene that your class relies on. This is what the example
above shows.
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
GDScript supports optional static typing.
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.