Bases de GDScript

Introducción

GDScript is a high-level, dynamically typed programming language used to create content. It uses a syntax similar to Python (blocks are indent-based and many keywords are similar). Its goal is to be optimized for and tightly integrated with Godot Engine, allowing great flexibility for content creation and integration.

Historia

En los primeros días, el motor utilizaba el lenguaje de scripting Lua <https://www.lua.org> __. Lua es rápido, pero crear enlaces a un sistema orientado a objetos (mediante el uso de retrocesos) fue complejo y lento y requirió una enorme cantidad de código. Después de algunos experimentos con Python <https://www.python.org> __, también resultó difícil de insertar.

El último lenguaje de scripting de terceros que se utilizó para los juegos enviados fue Squirrel <http://squirrel-lang.org> __, pero también se eliminó. En ese punto, se hizo evidente que un lenguaje de script personalizado podría hacer un uso más óptimo de la arquitectura particular de Godot:

  • Godot embebe scripts en nodos. La mayoría de los lenguajes no está diseñado con eso en mente.
  • Godot usa varios tipos de datos incorporados para matemática 2D y 3D. Los lenguajes de scripting no proveen esto y vincularlos es ineficiente.
  • Godot hace uso fuerte de hilos para obtener e inicializar datos desde la red o el disco. Intérpretes para lenguajes de scripting comunes no son amigables con ello.
  • Godot ya posee un modelo de manejo de memoria para recursos, la mayoría de los lenguajes de scripting tienen el suyo, lo que resulta en un esfuerzo duplicado y errores.
  • Vincular código siempre es desordenado y resulta en varios puntos de falla, errores inesperados y generalmente baja capacidad para mantenerlo.

El resultado de esas consideraciones es GDScript. El lenguaje e interprete para GDScript terminó siendo más pequeño que los vínculos de código mismos para Lua y Squirrel, manteniendo la funcionalidad. Con el tiempo, tener un lenguaje incorporado ha probado ser de gran ventaja.

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.
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.
tool Ejecuta el script en el editor.
signal 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 puntos de interrupción del depurador.
preload 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.
remote 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
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 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)

+ Adición / Concatenación de matrices
- 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 Prueba de contenido
! not Negación booleana
and && Conjunción booleana
or || Disyunción booleana
if x else if / else en una linea
= += -= *= /= %= &= |= 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" :ref:»Clase Ruta de nodo » o Nombre de cadena
$NodePath Abreviatura para get_node("NodePath")

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. (Las matrices agrupadas 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 secuencias de escape C estándar <https://en.wikipedia.org/wiki/Escape_sequences_in_C> _. GDScript también admite: ref: doc_gdscript_printf.

Tipos de Vectores built-in

Vector2

El tipo Vector2D contiene los campos x y y. También puede ser accedido como un arreglo.

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 arreglo.

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

Matríz de 3x3 usada para rotación 3D y escala. Contiene 3 campos para vectores (x, y y z) y puede ser accesada 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, incluidas otras matrices o diccionarios (ver más abajo). La matriz puede cambiar de tamaño dinámicamente. Las matrices 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:

  • ref:PoolByteArray <class_PoolByteArray>: Un array de bytes (enteros entre 0 y 255).
  • PoolIntArray: Un array de números enteros.
  • PoolRealArray: Un array de números decimales.
  • PoolStringArray: Un array de cadenas de texto.
  • PoolVector2Array: Un array de objetos Vector2.
  • PoolVector3Array: Un array de objetos Vector3.
  • PoolColorArray: Un array de objetos Color.

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 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.

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 similares a las variables, pero deben de mantener su valor o expresión de forma constante y deben de ser asignadas en su inicialización.

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.

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.

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

In Godot 3.1 and later, keys in a named enum are not registered as global constants. They should be accessed prefixed by the enum’s name (Name.KEY); see an example below.

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.

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

Al contrario de Python, las funciones son * no * objetos de primera clase en GDScript. Esto significa que no pueden almacenarse en variables, pasarse como argumento a otra función o devolverse 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

while

Los bucles primarios se pueden crear usando la sintaxis while. De los bucles se puede salir usando break o se puede pasar a la siguiente iteración usando continue:

while [expression]:
    statement(s)

for

Para iterar a través de 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.

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 arreglo, 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()

En cambio, 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

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()

To call a function in a parent class (i.e. one extend-ed in your current class), prepend . to the function name:

.base_func(args)

This is especially useful because functions in extending classes replace functions with the same name in their parent classes. If you still want to call them, you can prefix them with . (like the super keyword in other languages):

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

Nota

Default functions like _init, and most notifications such as _enter_tree, _exit_tree, _process, _physics_process, etc. are called in all parent classes automatically. There is no need to call them explicitly when overloading them.

Constructor de clase

The class constructor, called on class instantiation, is named _init. As mentioned earlier, the constructors of parent classes are called automatically when inheriting a class. So, there is usually no need to call ._init() explicitly.

Unlike the call of a regular function, like in the above example with .some_func, if the constructor from the inherited class takes arguments, they are passed like this:

func _init(args).(parent_args):
   pass

This is better explained through examples. Consider this scenario:

# 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. If the inherited class (State.gd) defines a _init constructor that takes arguments (e in this case), then the inheriting class (Idle.gd) must define _init as well and pass appropriate parameters to _init from State.gd.

  2. Idle.gd can have a different number of arguments than the parent class State.gd.

  3. In the example above, e passed to the State.gd constructor is the same e passed in to Idle.gd.

  4. If Idle.gd’s _init constructor takes 0 arguments, it still needs to pass some value to the State.gd parent class, even if it does nothing. This brings us to the fact that you can pass literals in the base constructor as well, not just variables. eg.:

    # 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 my_class = 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 GDScript exports.

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

Whenever the value of variable is modified by an external source (i.e. not from local usage in the class), the setter function (setterfunc above) will be called. This happens before the value is changed. The setter must decide what to do with the new value. Vice versa, when variable is accessed, the getter function (getterfunc above) must return the desired value. Below is an example:

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.

Either of the setter or getter functions can be omitted:

# 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

Setters and getters are useful when exporting variables to the editor in tool scripts or plugins, for validating input.

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

By default, scripts don’t run inside the editor and only the exported properties can be changed. In some cases, it is desired that they do run inside the editor (as long as they don’t execute game code or manually avoid doing so). For this, the tool keyword exists and must be placed at the top of the file:

tool
extends Button

func _ready():
    print("Hello")

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 class_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 class_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.

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

Signals are a tool to emit messages from an object that other objects can react to. To create custom signals for a class, use the signal keyword.

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.

You can connect these signals to methods the same way you connect built-in signals of nodes like class_Button or class_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.

In our Character.gd script, we define a health_changed signal and emit it with Object.emit_signal(), and from a Game node higher up our scene tree, we connect it to the Lifebar using the Object.connect() method:

# 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 can bind an array of values to connections between a signal and a method. When the signal is emitted, the callback method receives the bound values. These bound arguments are unique to each connection, and the values will stay the same.

Usted puede usar este arreglo de valores para añadir información constante demás a la conexión si la señal emitida por sí misma no le da acceso a todos los datos que necesita.

Building on the example above, let’s say we want to display a log of the damage taken by each character on the screen, like Player1 took 22 damage.. The health_changed signal doesn’t give us the name of the character that took damage. So when we connect the signal to the in-game console, we can add the character’s name in the binds array argument:

# 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])

Our BattleLog node receives each element in the binds array as an extra argument:

# 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 offers support for coroutines via the yield built-in function. Calling yield() will immediately return from the current function, with the current frozen state of the same function as the return value. Calling resume() on this resulting object will continue execution and return whatever the function returns. Once resumed, the state object becomes invalid. Here is an example:

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.

Will print:

Hello
my dear
world

It is also possible to pass values between yield() and resume(), for example:

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.

Will print:

Hello
world
cheers!

Corrutinas y Señales

The real strength of using yield is when combined with signals. yield can accept two arguments, an object and a signal. When the signal is received, execution will recommence. Here are some examples:

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

Coroutines themselves use the completed signal when they transition into an invalid state, for example:

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.

Palabra clave Onready

When using nodes, it’s common to desire to keep references to parts of the scene in a variable. As scenes are only warranted to be configured when entering the active scene tree, the sub-nodes can only be obtained when a call to Node._ready() is made.

var my_label

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

This can get a little cumbersome, especially when nodes and external references pile up. For this, GDScript has the onready keyword, that defers initialization of a member variable until _ready() is called. It can replace the above code with a single line:

onready var my_label = get_node("MyLabel")

Palabra clave Assert

The assert keyword can be used to check conditions in debug builds. These assertions are ignored in non-debug builds. This means that the expression passed as argument won’t be evaluated in a project exported in release mode. Due to this, assertions must not contain expressions that have side effects. Otherwise, the behavior of the script would vary depending on whether the project is run in a debug build.

# 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.