Bases de GDScript

Introducción

GDScript es un lenguaje de programación de alto nivel con tipado dinámico utilizado para crear contenido. Utiliza una sintaxis similar a Python (los bloques están basados en la indentación y muchas palabras clave son similares). Su objetivo es optimizarse e integrarse estrechamente con Godot Engine, permitiendo una gran flexibilidad en la creación e integración de contenido.

Historia

Nota

La documentation sobre la historia de GDScript se ha movido a Preguntas Frecuentes.

Ejemplo de GDScript

Algunas personas pueden aprender mejor echando un vistazo a la sintaxis, así que aquí hay un ejemplo simple de cómo se ve GDScript.

# A file is a class!

# Inheritance

extends BaseClass

# (optional) class definition with a custom icon

class_name MyClass, "res://path/to/optional/icon.svg"


# Member variables

var a = 5
var s = "Hello"
var arr = [1, 2, 3]
var dict = {"key": "value", 2: 3}
var typed_var: int
var inferred_type := "String"

# Constants

const ANSWER = 42
const THE_NAME = "Charly"

# Enums

enum {UNIT_NEUTRAL, UNIT_ENEMY, UNIT_ALLY}
enum Named {THING_1, THING_2, ANOTHER_THING = -1}

# Built-in vector types

var v2 = Vector2(1, 2)
var v3 = Vector3(1, 2, 3)


# Function

func some_function(param1, param2):
    var local_var = 5

    if param1 < local_var:
        print(param1)
    elif param2 > 5:
        print(param2)
    else:
        print("Fail!")

    for i in range(20):
        print(i)

    while param2 != 0:
        param2 -= 1

    var local_var2 = param1 + 3
    return local_var2


# Functions override functions with the same name on the base/parent class.
# If you still want to call them, use '.' (like 'super' in other languages).

func something(p1, p2):
    .something(p1, p2)


# Inner class

class Something:
    var a = 10


# Constructor

func _init():
    print("Constructed!")
    var lv = Something.new()
    print(lv.a)

Si tienes experiencia previa con lenguajes estáticamente tipados como C, C++ o C# pero nunca has utilizado uno dinámicamente tipado antes, es recomendable leer este tutorial: GDScript: Introducción a los lenguajes dinámicos.

Lenguaje

A continuación, se presenta un resumen de GDScript. Detalles, como qué métodos están disponibles para arrays u otros objetos deberán verse en las descripciones de las clases.

Identificadores

Cualquier cadena se restringe a caracteres del alfabeto (a a z y A a Z), dígitos (0 a 9) y _ también califica como identificador. Adicionalmente, los identificadores no deben comenzar con un dígito. Los identificadores son sensibles a mayúsculas y minúsculas (case-sensitive, foo no es lo mismo que FOO).

Palabras clave

La siguiente lista de palabras clave soportadas por el lenguaje. Como las palabras claves son palabras reservadas (símbolos), no pueden ser utilizadas como identificadores. Operadores(como in, not, and or or) y nombres de tipo integrados listados en la siguiente sección tambien son reservados.

Las palabras clave están definidas en el GDScript tokenizer en caso de que se desee ver en detalle su funcionamiento.

Palabras clave

Descripción

if

Ver if/else/elif.

elif

Ver if/else/elif.

else

Ver if/else/elif.

for

Ver for.

while

Ver while.

match

Ver match.

break

Sale de la ejecución del bucle for o while actual.

continue

Pasa inmediatamente a la siguiente iteración del bucle for o while.

pass

Utilizado cuando sintácticamente se requiere una declaración pero no se desea ejecución de código, por ejemplo, en funciones vacías.

return

Devuelve el valor de una función.

class

Define una clase interna.

class_name(nombre de clase)

Define un nombre de clase y un icono opcional para tu script.

extends

Define de qué clase extiende la clase actual.

is

Prueba si una variable extiende de la clase dada o es de un tipo integrado.

as

Convertir el valor a un tipo dado si es posible.

self

Se refiere a la instancia de clase actual.

herramienta

Ejecuta el script en el editor.

señal

Define una señal.

func

Define una función.

static

Define una función estática. Variables miembro estáticas no están permitidas.

const

Define una constante.

enum

Define una enum.

var

Define una variable.

onready

Inicializa una variable cuando el Node al que el script está vinculado y todos sus hijos son parte del árbol de escenas.

export

Guarda una variable junto con los recursos a los que está vinculada y los hace visibles y modificables en el editor.

setget

Define funciones setter y getter para una variable.

breakpoint

Editor helper para depurar puntos de ruptura.

precarga

Precarga una clase o variable. Ver Clases como recursos.

yield

Soporte de corrutinas. Ver Corrutinas con yield.

assert

Afirma una condición, registra un error si falla. Ignorado en builds que no son de depuración. Ver Palabra clave Assert.

remoto

Anotación de Networking RPC. Ver high-level multiplayer docs.

master

Anotación de Networking RPC. Ver high-level multiplayer docs.

puppet

Anotación de Networking RPC. Ver high-level multiplayer docs.

remotesync

Anotación de Networking RPC. Ver high-level multiplayer docs.

mastersync

Anotación de Networking RPC. Ver high-level multiplayer docs.

puppetsync

Anotación de Networking RPC. Ver high-level multiplayer docs.

PI

Constante PI.

TAU

Constante TAU.

INF

Constante de infinito. Utilizado para comparaciones.

NAN

Constante NAN (no es un número). Usada para comparaciones.

Operadores

La siguiente lista muestra los operadores soportados y su precedencia.

Operador

Descripción

x[index]

Suscripción (máxima prioridad)

x.atribute

Referencia de atributo

foo()

Llamada a funciones

is

Comprobador de tipo de instancia

~

Negación lógica a nivel de bits (NOT)

-x

Negación negativa / unaria

* / %

Multiplicación / División / Resto

Estos operadores tienen el mismo comportamiento que en C++. La división de enteros se trunca en lugar de devolver un número fraccional, y el operador % solo está disponible para ints ("fmod" para floats), y es usado adicionalmente para formatos de cadena de texto

+

Adición / Concatenación de arrays

-

Resta

<< >>

Poco desplazamiento

&

Conjunción lógica a nivel de bits (AND)

^

Disyunción exclusiva a nivel de bits (XOR)

|

Disyunción lógica a nivel de bits (OR)

< > == != >= <=

Comparaciones

in

Cuando se usa con la palabra clave if, comprueba si un valor está dentro de una cadena, lista, rango, diccionario o nodo. Cuando se usa con la palabra clave for, se utiliza para iterar a través del contenido de una cadena, lista, rango, diccionario o nodo.

! not

Negación booleana

and &&

Conjunción booleana

or ||

Disyunción booleana

if x else

if / else en una linea

as

Conversión (casting) de tipo

= += -= *= /= %= &= |= <<= >>=

Asignación (prioridad más baja)

Literales

Literal

Tipo

45

Entero base 10

0x8F51

Base de 16 enteros (hexadecimal)

0b101010

Entero base 2 (binario)

3.14, 58.1e-10

Número de coma flotante (real)

"Hello", "Hi"

Cadenas de caracteres

"""Hola"""

Cadena de caracteres con múltiples líneas

@"Node/Label"

NodePath o StringName

$NodePath

Abreviatura para get_node("NodePath")

Los enteros y los reales pueden tener sus números separados con _ para hacerlos más legibles. Las siguientes formas de escribir los números son todas válidas:

12_345_678  # Equal to 12345678.
3.141_592_7  # Equal to 3.1415927.
0x8080_0000_ffff  # Equal to 0x80800000ffff.
0b11_00_11_00  # Equal to 0b11001100.

Comentarios

Todo lo escrito después de # hasta terminar la linea, es ignorado y se interpretá como un comentario.

# This is a comment.

Tipos integrados

Los tipos incorporados están asignados en pila. Se pasan como valores. Esto significa que se crea una copia en cada tarea o al pasarlas como argumentos a funciones. Las únicas excepciones son `` Array`` sy `` Dictionaries``, que se pasan por referencia para que se compartan. (Los arrays agrupados como `` PoolByteArray`` todavía se pasan como valores)

Tipos integrados básicos

Una variable en GDScript se puede asignar a varios tipos integrados.

null

null es un tipo de dato vacío que no contiene información y no se le puede asignar ningún otro valor.

bool

Abreviatura de "booleano", solo puede contener `` verdadero`` o `` falso``.

int

Abreviatura de "entero", almacena números enteros (positivos y negativos). Se almacena como un valor de 64 bits, equivalente a "int64_t" en C ++.

float

Almacena números reales, incluidos decimales, utilizando valores de punto flotante. Se almacena como un valor de 64 bits, equivalente a "double" en C++. Nota: Actualmente, las estructuras de datos como Vector2, Vector3 y PoolRealArray almacenan valores "flotantes" de precisión simple de 32 bits.

String

Una secuencia de caracteres en formato Unicode <https://en.wikipedia.org/wiki/Unicode>`_. Las cadenas pueden contener las siguientes secuencias de escape:

Secuencias de escape

Se expande a

\n

Nueva linea (salto de linea)

\t

Carácter de tabulador horizontal

\r

Retorno de carro

\a

Alerta(bip/bell)

\b

Retroceso

\f

Salto de página de Formfeed

\v

Carácter de tabulado vertical

\"

Dobles comillas

\'

Simples comillas

\\

Barra Invertida

\uXXXX

Código Unicode XXXX (hexadecimal, insensible a mayúsculas y minúsculas

GDScript también soporta Cadenas de formato en GDScript.

Tipos de Vectores internos

Vector2

Tipo de vector 2D que contiene los campos x y y. También puede ser accedido como un array.

Rect2

El tipo 2D Rectangle contiene dos campos de vectores: position y size. También contiene un campo end el cual es position + size`.

Vector3

Tipo de vector 3D que contiene los campos x, y y z. También puede ser accedido como si fuera un array.

Transform2D

Matriz 3 × 2 utilizada para transformaciones 2D.

Plane

Tipo Plano 3D en forma normalizada que contiene un vector normal y una distancia escalar d.

Quat

Quaternion es un tipo de datos utilizado para representar una rotación 3D. Es útil para interpolar rotaciones.

AABB

El cuadro delimitador alineado con el eje (o cuadro 3D) contiene 2 campos de vectores: `` posición '' y `` tamaño ''. También contiene un campo de `` fin '' que es `` posición + tamaño ''.

Basis

Matriz de 3x3 utilizada para la rotación y la escala 3D. Contiene 3 campos vectoriales (x, y y z) y también se puede acceder como un array de vectores 3D.

Transform

La clase Transform tiene un campo Basis, llamado basis y una campo Vector3 llamado origin.

Tipos integrados del motor

Color

El tipo de datos de color contiene los campos r, g, b y a. También puede ser accedido como h, s, y v, siendo estos tono/saturación/valor.

NodePath

Ruta compilada a un nodo usado principalmente en el sistema de escenas. Puede ser asignado de manera fácil a una cadena de caracteres y leído nuevamente.

RID

ID de recurso (RID). Los servidores utilizan RID genéricos para referenciar datos opacos.

Object

Clase base para cualquier cosa que no sea un tipo incorporado.

Tipos de Container integrados

Array

Secuencia genérica de tipos de objetos arbitrarios, incluidos otros arrays o diccionarios (ver más abajo). El array puede cambiar de tamaño dinámicamente. Los arrays se indexan a partir del índice `` 0``. Los índices negativos cuentan desde el final.

var arr = []
arr = [1, 2, 3]
var b = arr[1] # This is 2.
var c = arr[arr.size() - 1] # This is 3.
var d = arr[-1] # Same as the previous line, but shorter.
arr[0] = "Hi!" # Replacing value 1 with "Hi!".
arr.append(4) # Array is now ["Hi!", 2, 3, 4].

Las matrices GDScript se asignan linealmente en la memoria para mayor velocidad. Sin embargo, las matrices grandes (más de decenas de miles de elementos) pueden causar fragmentación de la memoria. Si esto le preocupa, hay disponibles tipos especiales de matrices. Estos solo aceptan un único tipo de datos. Evitan la fragmentación de la memoria y usan menos memoria, pero son atómicos y tienden a funcionar más lentamente que las matrices genéricas. Por lo tanto, solo se recomienda su uso para grandes conjuntos de datos:

Diccionario

Container asociativo que contiene valores referenciados por claves únicas.

var d = {4: 5, "A key": "A value", 28: [1, 2, 3]}
d["Hi!"] = 0
d = {
    22: "value",
    "some_key": 2,
    "other_key": [2, 3, 4],
    "more_key": "Hello"
}

El estilo de sintaxis de tabla en Lua también está soportado. Lua usa = en lugar de : y no usa comillas para marcar las claves de texto (haciendo que haya que escribir algo menos). Tenga en cuenta, no obstante, que como cualquier otro identificador en Gdscript, las claves escritas de esta forma no pueden empezar con un número.

var d = {
    test22 = "value",
    some_key = 2,
    other_key = [2, 3, 4],
    more_key = "Hello"
}

Para añadir una clave a un diccionario ya existente, accede a ella como a cualquier clave ya existente y asígnala:

var d = {} # Create an empty Dictionary.
d.waiting = 14 # Add String "waiting" as a key and assign the value 14 to it.
d[4] = "hello" # Add integer 4 as a key and assign the String "hello" as its value.
d["Godot"] = 3.01 # Add String "Godot" as a key and assign the value 3.01 to it.

var test = 4
# Prints "hello" by indexing the dictionary with a dynamic key.
# This is not the same as `d.test`. The bracket syntax equivalent to
# `d.test` is `d["test"]`.
print(d[test])

Nota

Los corchetes puede pueden usarse para acceder a propiedades de cualquier Object, no sólo Dictionary. Ten en cuenta que esto puede provocar errores en scripts cuando se intenta acceder a una propiedad que no existe. Para evitar esto, una los métodos Object.get() y Object.set() en su lugar.

Datos

Variables

Las variables puede existir como miembros de una clase o de forma local en una función. Se crean con la palabra clave var y pueden, opcionalmente, ser asignadas a un valor en su inicialización.

var a # Data type is 'null' by default.
var b = 5
var c = 3.8
var d = b + c # Variables are always initialized in order.

Las variables pueden tener opcionalmente una especificación de tipo. Cuando se especifica un tipo, la variable se verá obligada a tener siempre el mismo tipo, y al intentar asignar un valor incompatible se generará un error.

Los tipos se especifican en la declaración de la variable usando un símbolo : (dos puntos) después del nombre de la variable, seguido del tipo.

var my_vector2: Vector2
var my_node: Node = Sprite.new()

Si la variable se inicializa dentro de la declaración, se puede inferir el tipo, por lo que es posible omitir el nombre del tipo:

var my_vector2 := Vector2() # 'my_vector2' is of type 'Vector2'.
var my_node := Sprite.new() # 'my_node' is of type 'Sprite'.

La inferencia del tipo solo es posible si el valor asignado tiene un tipo definido, de lo contrario generará un error.

Los tipos válidos son:

  • Tipos incorporados (Array, Vector2, int, String, etc.).

  • Clases del Engine(Nodo, Recurso, Referencia, etc.).

  • Nombres constantes si contienen un recurso de script (MyScript si declaró const MyScript = preload("res://my_script.gd")).

  • Otras clases en el mismo script, respetando el alcance (`` Clase interna Clase anidada '' si declaró `` clase Clase anidada '' dentro de la `` clase Clase interna '' en el mismo alcance).

  • Clases de script declaradas con la palabra clave class_name.

Casting

Los valores asignados a las variables escritas deben tener un tipo compatible. Si es necesario forzar un valor para que sea de un determinado tipo, en particular para los tipos de objetos, puede usar el operador de conversión as.

La conversión entre tipos de objetos da como resultado el mismo objeto si el valor es del mismo tipo o subtipo del tipo de conversión.

var my_node2D: Node2D
my_node2D = $Sprite as Node2D # Works since Sprite is a subtype of Node2D.

Si el valor no es un subtipo, la operación de conversión dará como resultado un valor null.

var my_node2D: Node2D
my_node2D = $Button as Node2D # Results in 'null' since a Button is not a subtype of Node2D.

Para los tipos integrados, se convertirán a la fuerza si es posible, de lo contrario, el motor generará un error.

var my_int: int
my_int = "123" as int # The string can be converted to int.
my_int = Vector2() as int # A Vector2 can't be converted to int, this will cause an error.

La conversión también es útil para tener mejores variables de tipo seguro cuando interactúa con el árbol de escenas

# Will infer the variable to be of type Sprite.
var my_sprite := $Character as Sprite

# Will fail if $AnimPlayer is not an AnimationPlayer, even if it has the method 'play()'.
($AnimPlayer as AnimationPlayer).play("walk")

Constantes

Las constantes son valores que no se pueden cambiar cuando el juego está en marcha. Su valor debe ser conocido en tiempo de compilación. Usar la palabra clave const te permite dar un nombre a un valor constante. Si intentas asignar un valor a una constante después de declararla, te dará un error.

Recomendamos el uso de constantes siempre que un valor no esté destinado a cambiar.

const A = 5
const B = Vector2(20, 20)
const C = 10 + 20 # Constant expression.
const D = Vector2(20, 30).x # Constant expression: 20.
const E = [1, 2, 3, 4][0] # Constant expression: 1.
const F = sin(20) # 'sin()' can be used in constant expressions.
const G = x + 20 # Invalid; this is not a constant expression!
const H = A + 20 # Constant expression: 25 (`A` is a constant).

Aunque el tipo de constantes se deduce del valor asignado, también es posible agregar una especificación explícita:

const A: int = 5
const B: Vector2 = Vector2()

Asignar un valor de un tipo incompatible generará un error.

Nota

Como los arrays y diccionarios se pasan por referencia, las constantes son "planas". Esto significa que si declaras un array o diccionario de constantes, todavía puede ser modificado después. Sin embargo, no pueden ser reasignados con otro valor.

Enumeraciones

Las enumeraciones básicamente son atajos para las constantes, son muy útiles si quieres asignar números consecutivos a alguna constante.

Si le das un nombre al enum, todos los valores que este contenga se asignarán a un diccionario de constantes con ese nombre.

Importante

A partir de Godot 3.1, las claves de un enum con nombre no son registradas como constantes globales. Deben accederse con el nombre del enum como prefijo (Nombre.CLAVE), ver el ejemplo a continuación.

enum {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT}
# Is the same as:
const TILE_BRICK = 0
const TILE_FLOOR = 1
const TILE_SPIKE = 2
const TILE_TELEPORT = 3

enum State {STATE_IDLE, STATE_JUMP = 5, STATE_SHOOT}
# Is the same as:
const State = {STATE_IDLE = 0, STATE_JUMP = 5, STATE_SHOOT = 6}
# Access values with State.STATE_IDLE, etc.

Funciones

Las funciones siempre pertenecen a una clase. El ámbito de prioridad de comprobación de variables es: local → miembro de clase → global. La variable self siempre está disponible y se proporciona como opción para el acceso a los miembros de la clase, pero no suele ser necesaria (y no se debe de enviar como primer parámetro a una función, al contrario que en Python).

func my_function(a, b):
    print(a)
    print(b)
    return a + b  # Return is optional; without it 'null' is returned.

Una función puede retornar, usando la palabra clave return, en cualquier punto. El valor de retorno por defecto es null.

Las funciones también pueden tener una especificación de tipo para los argumentos y para el valor de retorno. Los tipos de argumentos se pueden agregar de manera similar a las variables:

func my_function(a: int, b: String):
    pass

Si un argumento de una función tiene un valor por defecto, es posible inferir el tipo:

func my_function(int_arg := 42, String_arg := "string"):
    pass

El tipo de retorno de la función se puede especificar después de la lista de argumentos usando el token de flecha (->):

func my_int_function() -> int:
    return 0

Las funciones que tienen un tipo de retorno ** deben ** devolver un valor adecuado. Establecer el tipo como void significa que la función no devuelve nada. Las funciones anuladas pueden regresar pronto con la palabra clave return, pero no pueden devolver ningún valor.

func void_function() -> void:
    return # Can't return a value

Nota

Las funciones no nulas deben ** siempre ** devolver un valor, por lo que si su código tiene sentencias de bifurcación (como una construcción if/else), todas las rutas posibles deben tener un retorno. Por ejemplo, si tiene un return dentro de un bloque if pero no después de él, el editor generará un error porque si el bloque no se ejecuta, la función no tendrá un valor válido para devolver.

Funciones de referencia

A diferencia de Python, las funciones no son objetos de primera clase en GDScript. Esto significa que no pueden ser almacenadas en variables, pasadas como argumento a otra función o devueltas desde otras funciones. Esto es por razones de rendimiento.

Para hacer referencia a una función por nombre en tiempo de ejecución (por ejemplo, para almacenarla en una variable o pasarla a otra función como argumento), uno debe usar los ayudantes `` call`` o `` funcref``

# Call a function by name in one step.
my_node.call("my_function", args)

# Store a function reference.
var my_func = funcref(my_node, "my_function")
# Call stored function reference.
my_func.call_func(args)

Funciones estáticas

Una función puede declararse estática. Cuando una función es estática, no tiene acceso a las variables miembro de la instancia o `` self ''. Esto es principalmente útil para hacer bibliotecas de funciones auxiliares

static func sum2(a, b):
    return a + b

Declaraciones y estructuras de control

Las declaraciones son estándar y pueden ser asignaciones, llamadas a funciones, estructuras de control, etc (mirar abajo). Usar ; como separador de las declaraciones es opcional.

if/else/elif

Los condicionales básicos se crean usando la sintaxis if/else/elif. Se permite el uso de paréntesis detrás de estos, pero no son obligatorios. Dada la naturaleza del código basado en tabulaciones, se puede usar elif en lugar de else/if para mantener la sangría al mismo nivel.

if [expression]:
    statement(s)
elif [expression]:
    statement(s)
else:
    statement(s)

Las declaraciones cortas se pueden escribir en la misma línea que la condición:

if 1 + 1 == 2: return 2 + 2
else:
    var x = 3 + 3
    return x

A veces, es posible que desee asignar un valor inicial diferente en función de una expresión booleana. En este caso, las expresiones ternary-if son útiles

var x = [value] if [expression] else [value]
y += 3 if y < 10 else -1

Las expresiones condicionales ternarias se pueden anidar para manejar más de 2 casos. Al anidar expresiones condicionales ternarias, se recomienda envolver la expresión completa en varias líneas para preservar la legibilidad:

var count = 0

var fruit = (
        "apple" if count == 2
        else "pear" if count == 1
        else "banana" if count == 0
        else "orange"
)
print(fruit)  # banana

# Alternative syntax with backslashes instead of parentheses (for multi-line expressions).
# Less lines required, but harder to refactor.
var fruit_alt = \
        "apple" if count == 2 \
        else "pear" if count == 1 \
        else "banana" if count == 0 \
        else "orange"
print(fruit_alt)  # banana

También puede desear verificar si un valor está contenido dentro de algo. Puede usar una declaración if combinada con el operador in para lograr esto:

# Check if a letter is in a string.
var text = "abc"
if 'b' in text: print("The string contains b")

# Check if a variable is contained within a node.
if "varName" in get_parent(): print("varName is defined in parent!")

while

Simple loops are created by using while syntax. Loops can be broken using break or continued using continue (i.e. skipping to the next iteration of the loop without executing any further code in the current iteration):

while [expression]:
    statement(s)

for

Para iterar en un rango, como un array o una tabla, se utiliza un bucle for. Cuando se itera sobre un array, el elemento actual del array se guarda en la variable del bucle. Cuando se itera sobre un diccionario, el índice se guarda en la variable del bucle.

for x in [5, 7, 11]:
    statement # Loop iterates 3 times with 'x' as 5, then 7 and finally 11.

var dict = {"a": 0, "b": 1, "c": 2}
for i in dict:
    print(dict[i]) # Prints 0, then 1, then 2.

for i in range(3):
    statement # Similar to [0, 1, 2] but does not allocate an array.

for i in range(1, 3):
    statement # Similar to [1, 2] but does not allocate an array.

for i in range(2, 8, 2):
    statement # Similar to [2, 4, 6] but does not allocate an array.

for c in "Hello":
    print(c) # Iterate through all characters in a String, print every letter on new line.

for i in 3:
    statement # Similar to range(3)

for i in 2.2:
    statement # Similar to range(ceil(2.2))

match

La expresión match se usa para bifurcar la ejecución del programa. Es el equivalente de la expresión switch encontrada en otros lenguajes de programación, pero ofrece funcionalidades adicionales.

Sintaxis básica:

match [expression]:
    [pattern](s):
        [block]
    [pattern](s):
        [block]
    [pattern](s):
        [block]

Curso rápido para las personas familiarizadas con las expresiones switch:

  1. Reemplace "switch'' con " match''.

  2. Elimina "case".

  3. Elimina todos los break. Si no quieres que la ejecución se detenga por defecto, puedes usar continue para continuar la ejecución en cascada.

  4. Cambia default por un guión bajo.

Control del flujo:

Los patrones se comparan desde arriba hacia abajo. Si el patrón coincide, el bloque correspondiente se ejecuta. Después de eso, la ejecución continua por debajo de la declaración match. Si quieres seguir recorriendo hacia abajo la expresión, puedes usar continue para detener la ejecución del bloque actual y comprobar el siguiente patrón.

Hay 6 tipos de patrones:

  • Patrón constante

    Primitivas constantes, como números y cadenas

    match x:
        1:
            print("We are number one!")
        2:
            print("Two are better than one!")
        "test":
            print("Oh snap! It's a string!")
    
  • Patrón de variables

    Coincide con el contenido de una variable / enum

    match typeof(x):
        TYPE_REAL:
            print("float")
        TYPE_STRING:
            print("text")
        TYPE_ARRAY:
            print("array")
    
  • Patrón comodín

    Este patrón busca emparejar cualquier cosa. Se escribe con un guión bajo.

    Se puede usar como el equivalente del `` valor predeterminado '' en una declaración de `` cambio '' en otros idiomas

    match x:
        1:
            print("It's one!")
        2:
            print("It's one times two!")
        _:
            print("It's not 1 or 2. I don't care to be honest.")
    
  • Patrón vinculante

    Un patrón de enlace introduce una nueva variable. Al igual que el patrón comodín, coincide con todo, y también le da un nombre a ese valor. Es especialmente útil en patrones de matriz y diccionario

    match x:
        1:
            print("It's one!")
        2:
            print("It's one times two!")
        var new_var:
            print("It's not 1 or 2, it's ", new_var)
    
  • Patrón de matriz

    Coincide con una matriz. Cada elemento individual del patrón de matriz es un patrón en sí mismo, por lo que puede anidarlos.

    Se comprueba primero la longitud del array, este tiene que tener el mismo tamaño que el patrón, de otra manera no habrá coincidencia.

    ** Matriz abierta **: una matriz puede ser más grande que el patrón haciendo el último subpatrón `` ..``.

    Cada subpatrón tiene que estar separado por comas.

    match x:
        []:
            print("Empty array")
        [1, 3, "test", null]:
            print("Very specific array")
        [var start, _, "test"]:
            print("First element is ", start, ", and the last is \"test\"")
        [42, ..]:
            print("Open ended array")
    
  • Patrón de diccionario

    Funciona de la misma manera que el patrón de array. Cada clave tiene que ser un patrón de constante.

    Se comprueba primero el tamaño del diccionario y tiene que ser igual al del patrón, de otro modo el patrón no coincidirá.

    ** Diccionario abierto **: un diccionario puede ser más grande que el patrón haciendo el último subpatrón `` ..``.

    Cada sub-patrón tiene que estar separado por comas.

    Si no especificas un valor, solo se comprobará la existencia de la clave.

    Un patrón de valor se separa del patrón clave con un ``: ``.

    match x:
        {}:
            print("Empty dict")
        {"name": "Dennis"}:
            print("The name is Dennis")
        {"name": "Dennis", "age": var age}:
            print("Dennis is ", age, " years old.")
        {"name", "age"}:
            print("Has a name and an age, but it's not Dennis :(")
        {"key": "godotisawesome", ..}:
            print("I only checked for one entry and ignored the rest")
    
  • Patrones múltiples

    También puede especificar múltiples patrones separados por una coma. No se permite que estos patrones tengan enlaces en ellos.

    match x:
        1, 2, 3:
            print("It's 1 - 3")
        "Sword", "Splash potion", "Fist":
            print("Yep, you've taken damage")
    

Clases

Por defecto, todos los archivos de script son clases sin nombre. En este caso, solo puede hacer referencia a ellos utilizando la ruta del archivo, utilizando una ruta relativa o absoluta. Por ejemplo, si nombra un archivo de script `` character.gd``

# Inherit from 'Character.gd'.

extends "res://path/to/character.gd"

# Load character.gd and create a new node instance from it.

var Character = load("res://path/to/character.gd")
var character_node = Character.new()

Registrar clases con nombres

Puede darle un nombre a su clase para registrarlo como un nuevo tipo en el editor de Godot. Para eso, usa la palabra clave `` class_name``. Puede agregar una coma opcional seguida de una ruta a una imagen, para usarla como un icono. Su clase aparecerá con su nuevo icono en el editor

# Item.gd

extends Node
class_name Item, "res://interface/icons/item.png"
../../../_images/class_name_editor_register_example.png

Advertencia

Si el script se encuentra en el directorio res://addons/, class_name sólo causará que el nodo se muestre en el diálogo Crear nuevo nodo si el script forma parte de un plugin de editor habilitado. Ver Creando plugins para más información.

Aquí hay un ejemplo de archivo de clase:

# Saved as a file named 'character.gd'.

class_name Character


var health = 5


func print_health():
    print(health)


func print_this_script_three_times():
    print(get_script())
    print(ResourceLoader.load("res://character.gd"))
    print(Character)

Nota

La sintaxis de clase de Godot es compacta: solo puede contener variables o funciones miembro. Puede usar funciones estáticas, pero no variables miembro estáticas. De la misma manera, el motor inicializa las variables cada vez que creas una instancia, y esto incluye Arrays y Dictionaries. Esto está en el espíritu de seguridad de subprocesos, ya que los scripts pueden inicializarse en subprocesos separados sin que el usuario lo sepa.

Herencia

Una clase (guardada como un archivo) puede heredar de:

  • Una clase global.

  • Otro archivo de clase.

  • Una clase interna situada dentro de otro archivo de clase.

La herencia múltiple no está permitida.

La herencia usa la palabra clave extends:

# Inherit/extend a globally available class.
extends SomeClass

# Inherit/extend a named class file.
extends "somefile.gd"

# Inherit/extend an inner class in another file.
extends "somefile.gd".SomeInnerClass

Para comprobar si una instancia concreta hereda de una clase determinada, se puede usar la palabra clave is:

# Cache the enemy class.
const Enemy = preload("enemy.gd")

# [...]

# Use 'is' to check inheritance.
if entity is Enemy:
    entity.apply_damage()

Para llamar a una función de una clase base (es decir, una extend-dida por tu clase actual), precede el nombre de la función con un . (punto):

.base_func(args)

Esto es especialmente útil porque extendiendo clases se reemplazan funciones con el mismo nombre en la clase base. Si se quiere llamar a estas, puedes utilizar . (que es como la palabra clave super en otros lenguajes):

func some_func(x):
    .some_func(x) # Calls the same function on the parent class.

Nota

Recuerda que las funciones por defecto como _init, y muchas de las notificaciones como _enter_tree, _exit_tree, _process, _physics_process, etc. son ejecutadas en todas las clases base de forma automática. Por lo tanto solo hay necesidad de hacer llamadas explicitas a estas funciones cuando se quieran sobrecargar de alguna manera.

Constructor de clase

El constructor de clase, ejecutado cuando se instancia la clase, se llama _init. Como se ha mencionado antes, los constructores de las clases heredadas se ejecutan automáticamente cuando se heredan dichas clases. Por lo tanto no suele haber necesidad de llamar a ._init() explícitamente.

A diferencia de la llamada de una función normal como en el ejemplo anterior con `` .some_func``, si el constructor de la clase heredada toma argumentos, se pasan de este modo:

func _init(args).(parent_args):
   pass

Esto se explica mejor con ejemplos. Consideremos este escenario:

# State.gd (inherited class)
var entity = null
var message = null


func _init(e=null):
    entity = e


func enter(m):
    message = m


# Idle.gd (inheriting class)
extends "State.gd"


func _init(e=null, m=null).(e):
    # Do something with 'e'.
    message = m

Hay algunas cosas a tener en cuenta aquí:

  1. Si la clase heredada (State.gd) define un constructor _init que toma argumentos (e en este caso), entonces la clase heredada (Idle.gd) * debe * definir _init también y pasar los parámetros correspondientes a _init de State.gd.

  2. Idle.gd puede tener un número diferente de argumentos que la clase base State.gd.

  3. En el ejemplo anterior, el valor e pasado al constructor de State.gd es el mismo e pasado al de Idle.gd.

  4. Si el constructor _init de Idle.gd toma 0 argumentos, aún necesita pasar algún valor a la clase base State.gd aunque no haga nada. Lo que nos lleva al hecho de que también se pueden pasar literales en el constructor base, no solo variables. Por ejemplo:

    # Idle.gd
    
    func _init().(5):
        pass
    

Clases internas

Un archivo de clase puede contener otras clases internas. Las clases internas se definen usando la palabra clave class. Para instanciarlas se usa la función ClassName.new().

# Inside a class file.

# An inner class in this class file.
class SomeInnerClass:
    var a = 5


    func print_value_of_a():
        print(a)


# This is the constructor of the class file's main class.
func _init():
    var c = SomeInnerClass.new()
    c.print_value_of_a()

Clases como recursos

Las clases guardadas como archivos se tratan como recursos. Estos recursos se deben de cargar desde disco para acceder a ellos desde otras clases. Esto se hace usando cualquiera de estas dos funciones: load o preload (ver abajo). Instanciar una clase cargada como recurso se hace llamando a la función new en el objeto de dicha clase:

# Load the class resource when calling load().
var MyClass = load("myclass.gd")

# Preload the class only once at compile time.
const MyClass = preload("myclass.gd")


func _init():
    var a = MyClass.new()
    a.some_function()

Exports

Nota

La documentación sobre exportaciones está ahora en Exports de GDScript.

Setters/getters

A veces es útil saber cuando una variable de clase cambia por lo que sea. También se puede querer encapsular para restringir su acceso de determinada manera.

Para esto, GDScript proporciona una sintaxis de setter/getter usando la palabra clave setget. Esta palabra se usa directamente después de la definición de una variable:

var variable = value setget setterfunc, getterfunc

Siempre que el valor de variable sea modificado por una fuente externa (es decir, no por el uso local en la clase), la función setter (setterfunc arriba) será llamada. Esto ocurre antes de que se cambie el valor. El setter debe decidir qué hacer con el nuevo valor. Viceversa, cuando se accede a la variable, la función getter (getterfunc de arriba) debe devolver el valor deseado. A continuación se muestra un ejemplo:

var my_var setget my_var_set, my_var_get


func my_var_set(new_value):
    my_var = new_value


func my_var_get():
    return my_var # Getter must return a value.

Cualquiera de las dos funciones, setter o getter, se pueden omitir:

# Only a setter.
var my_var = 5 setget my_var_set
# Only a getter (note the comma).
var my_var = 5 setget ,my_var_get

Los getters y setters son especialmente útiles cuando se exportan variables al editor en scripts del editor o plugins, para validar la entrada de datos.

Como se ha indicado previamente, los accesos locales no ejecutan setter ni getter. Aquí hay una ilustración de esto:

func _init():
    # Does not trigger setter/getter.
    my_integer = 5
    print(my_integer)

    # Does trigger setter/getter.
    self.my_integer = 5
    print(self.my_integer)

Modo Tool

Los scripts, por defecto, no se ejecutan dentro del editor y solo las propiedades exportadas pueden modificarse. En algunos casos se puede desear que se ejecuten dentro del propio editor (siempre que no ejecuten código del juego, o evitando que esto suceda de forma manual). Para esto, existe la palabra clave tool que se deberá escribir en la parte superior del archivo:

tool
extends Button


func _ready():
    print("Hello")

Ver :ref:`doc_running_code_in_the_editor`para más información.

Advertencia

Tenga cuidado al liberar nodos con `` queue_free () `` o `` free () `` en un script de herramienta (especialmente el propietario del script). A medida que los scripts de herramientas ejecutan su código en el editor, usarlos incorrectamente puede provocar el bloqueo del editor.

Gestión de la memoria

Si una clase hereda de Reference, entonces las instancias serán liberadas cuando ya no estén en uso. No existe ningún recolector de basura, sólo un conteo de referencia. Por defecto, todas las clases que no definan herencias se extienden Reference. Si esto no es lo deseado, entonces una clase debe heredar Object manualmente y debe llamar a instance.free(). Para evitar ciclos de referencia que no puedan ser liberados, se proporciona una función weakref para crear referencias débiles. Aquí está un ejemplo:

extends Node

var my_node_ref

func _ready():
    my_node_ref = weakref(get_node("MyNode"))

func _this_is_called_later():
    var my_node = my_node_ref.get_ref()
    if my_node:
        my_node.do_something()

Por otra parte, cuando no se utilizan referencias, el is_instance_valid(instance) puede usarse para comprobar si un objeto ha sido liberado.

Señales

Las señales son una forma de emitir mensajes desde un objeto, mensaje con el que otros objetos pueden reaccionar. Para crear señales personalizadas, usa la palabra clave signal.

extends Node


# A signal named health_depleted.
signal health_depleted

Nota

Las señales son un mecanismo de devolución de llamada <https://en.wikipedia.org/wiki/Callback (computer_programming)> _. También cumplen el papel de observadores, un patrón de programación común. Para obtener más información, lea el `Tutorial del observador <https://gameprogrammingpatterns.com/observer.html> _ en el libro electrónico Patrones de programación de juegos.

Las señales pueden ser conectadas a métodos de la misma forma en la que conectas señales propias de nodos tales como Button o RigidBody.

En el ejemplo de abajo conectamos la señal health_depleted de un nodo Character a un nodo Game. Cuando el nodo Character emite la señal, el nodo del juego _on_Character_health_depleted es llamado:

# Game.gd

func _ready():
    var character_node = get_node('Character')
    character_node.connect("health_depleted", self, "_on_Character_health_depleted")


func _on_Character_health_depleted():
    get_tree().reload_current_scene()

Puedes emitir todos los argumentos que necesites con una señal.

Aquí hay un ejemplo donde esto es útil. Digamos que queremos una barra de vida en la pantalla para reaccionar a los cambios de salud con una animación, pero queremos mantener la interfaz de usuario separada del jugador en nuestro árbol de escenas.

En nuestro script Character.gd, definimos una señal health_changed y la emitimos con Object.emit_signal(), y desde Game, nodo que se encuentra más arriba en el árbol de escena, lo conectamos con Lifebar usando el método Object.connect():

# Character.gd

...
signal health_changed


func take_damage(amount):
    var old_health = health
    health -= amount

    # We emit the health_changed signal every time the
    # character takes damage.
    emit_signal("health_changed", old_health, health)
...
# Lifebar.gd

# Here, we define a function to use as a callback when the
# character's health_changed signal is emitted.

...
func _on_Character_health_changed(old_value, new_value):
    if old_value > new_value:
        progress_bar.modulate = Color.red
    else:
        progress_bar.modulate = Color.green

    # Imagine that `animate` is a user-defined function that animates the
    # bar filling up or emptying itself.
    progress_bar.animate(old_value, new_value)
...

Nota

Para usar señales, tu clase debe extender de la clase Object o otro de los tipos que extienda de esta, como Node, KinematicBody, Control...

En el nodo Juego, obtenemos ambos nodos Personaje y barra_de_vida, los conectamos al personaje, que emite la señal, al recibidor, en este caso el nodo barra_de_vida.

# Game.gd

func _ready():
    var character_node = get_node('Character')
    var lifebar_node = get_node('UserInterface/Lifebar')

    character_node.connect("health_changed", lifebar_node, "_on_Character_health_changed")

Esto permite a la barra_de_vida reaccionar a los cambios de salud sin acoplarse al nodo personaje.

Puede escribir nombres de argumentos opcionales entre paréntesis después de la definición de la señal

# Defining a signal that forwards two arguments.
signal health_changed(old_value, new_value)

Estos argumentos se muestran en el panel de nodos del editor, y Godot puede usarlos para generar funciones de llamadas de vuelta para usted. Sin embargo, usted puede seguir emitiendo cualquier número de argumentos cuando emite señales, está en sus manos emitir los valores correctos.

../../../_images/gdscript_basics_signals_node_tab_1.png

GDScript puede asociar argumentos a las conexiones entre una señal y un método. Cuando la señal es emitida, llamando así al método conectado, el argumento asociado es pasado al método. Estos argumentos asociados son en realidad específicos a la conexión y sus valores se mantendrán iguales.

Puedes usar este array de valores para añadir información constante extra a la conexión si la señal emitida por sí misma no te da acceso a todos los datos que necesitas.

Tomando el ejemplo anterior, digamos que queremos mostrar un registro del daño recibido por cada personaje en pantalla, como Jugador1 recibió 22 de daño.. La señal health_changed no nos dice el nombre del personaje que recibió daño. Entonces cuando conectemos la señal a la consola del juego, podemos agregar el nombre del personaje en el array pasado como argumento:

# Game.gd

func _ready():
    var character_node = get_node('Character')
    var battle_log_node = get_node('UserInterface/BattleLog')

    character_node.connect("health_changed", battle_log_node, "_on_Character_health_changed", [character_node.name])

Nuestro nodo BattleLog (registro de batalla) recibe cada elemento en el array como un argumento extra:

# BattleLog.gd

func _on_Character_health_changed(old_value, new_value, character_name):
    if not new_value <= old_value:
        return

    var damage = old_value - new_value
    label.text += character_name + " took " + str(damage) + " damage."

Corrutinas con yield

GDScript ofrece soporte para corrutinas a través de la función integrada yield. Llamar a yield() hará que se retorne de la función actual, con el estado congelado que esta función tenga como valor de retorno. Ejecutando resume en el objeto resultante, se continuará la ejecución y se retornará lo que sea que la función retorne. Una vez resumido, el estado del objeto se tornará inválido. Aquí hay un ejemplo:

func my_func():
    print("Hello")
    yield()
    print("world")


func _ready():
    var y = my_func()
    # Function state saved in 'y'.
    print("my dear")
    y.resume()
    # 'y' resumed and is now an invalid state.

Imprimirá:

Hello
my dear
world

También es posible pasar valores entre yield() y resume(), por ejemplo:

func my_func():
    print("Hello")
    print(yield())
    return "cheers!"


func _ready():
    var y = my_func()
    # Function state saved in 'y'.
    print(y.resume("world"))
    # 'y' resumed and is now an invalid state.

Imprimirá:

Hello
world
cheers!

Recuerda guardar el estado nuevo de la función cuando uses múltiples yields:

func co_func():
    for i in range(1, 5):
        print("Turn %d" % i)
        yield();


func _ready():
    var co = co_func();
    while co is GDScriptFunctionState && co.is_valid():
        co = co.resume();

Corrutinas y Señales

La fortaleza real del uso de yield aparece cuando se combina con señales. yield puede aceptar dos parámetros, un objeto y una señal. Cuando la señal se recibe, la ejecución continuará. Aquí hay algunos ejemplos:

# Resume execution the next frame.
yield(get_tree(), "idle_frame")

# Resume execution when animation is done playing.
yield(get_node("AnimationPlayer"), "animation_finished")

# Wait 5 seconds, then resume execution.
yield(get_tree().create_timer(5.0), "timeout")

Las corrutinas mismas usan la señal completed cuando pasan a un estado inválido, por ejemplo:

func my_func():
    yield(button_func(), "completed")
    print("All buttons were pressed, hurray!")


func button_func():
    yield($Button0, "pressed")
    yield($Button1, "pressed")

my_func sólo continuará la ejecución una vez que se hayan presionado ambos botones.

También puedes obtener el argumento de la señal una vez fue emitida por un objeto:

# Wait for when any node is added to the scene tree.
var node = yield(get_tree(), "node_added")

Si hay más de un argumento, yield devuelve un array que contiene los argumentos:

signal done(input, processed)

func process_input(input):
    print("Processing initialized")
    yield(get_tree(), "idle_frame")
    print("Waiting")
    yield(get_tree(), "idle_frame")
    emit_signal("done", input, "Processed " + input)


func _ready():
    process_input("Test") # Prints: Processing initialized
    var data = yield(self, "done") # Prints: waiting
    print(data[1]) # Prints: Processed Test

Si no tienes seguridad de si una función podrá usar yield o no, o si podría usar yield muchas veces, puedes usar yield con la señal completed con alguna condición:

func generate():
    var result = rand_range(-1.0, 1.0)

    if result < 0.0:
        yield(get_tree(), "idle_frame")

    return result


func make():
    var result = generate()

    if result is GDScriptFunctionState: # Still working.
        result = yield(result, "completed")

    return result

Esto asegura que la función retornará cuando se suponga que deba retornar, indepenientemente de si se usaron corrutinas internamente. Nota que usar while sería redundante aquí ya que la señal completed se emite sólo cuando la función no usa más yield.

Palabra clave onready

Cuando se usan nodos, es muy común querer guardar referencias de partes de la escena en una variable. Las escenas sólo están garantizadas para ser configuradas al entrar en el árbol de escenas activo, por lo que los subnodos sólo se pueden obtener cuando la llamada a un Node._ready() se ha realizado.

var my_label


func _ready():
    my_label = get_node("MyLabel")

Esto puede dar quebraderos de cabeza, especialmente cuando los nodos y las referencias externas se acumulan. Para esto, GDScript tiene la palabra clave onready, que difiere la inicialización de una variable miembro hasta el momento en que _ready() es ejecutado. Esta palabra reemplaza el código de abajo en una sola línea:

onready var my_label = get_node("MyLabel")

Palabra clave Assert

La palabra clave assert puede ser usada para revisar condiciones en compilaciones de depuración. Esas comprobaciones son ignoradas en compilaciones que no son de depuración. Esto quiere decir que la expresión pasada como argumento no será evaluada en un proyecto exportado como release. Debido a esto, estas comprobaciones no deben contener expresiones que tengan efectos secundarios. De otro modo, el comportamiento del script variará dependiendo de si el proyecto se ejecuta o no en una versión de depuración.

# Check that 'i' is 0. If 'i' is not 0, an assertion error will occur.
assert(i == 0)

Cuando corre un proyecto desde el editor, el proyecto será pausado si ocurre un error con la aserción.