Scripting

Introducción

Antes de Godot 3.0, la única opción para scripting en un juego era usar GDScript. Hoy en día, Godot tiene cuatro (¡sí, cuatro!) idiomas oficiales ¡y la capacidad de añadir lenguajes de script adicionales de forma dinámica!

Esto es genial, sobre todo debido a la gran cantidad de flexibilidad proporcionada, pero también hace que nuestro trabajo de apoyo a los idiomas sea más difícil.

Sin embargo, los lenguajes «principales» en Godot son GDScript y VisualScript. La razón principal para elegirlos es su nivel de integración con Godot, ya que esto hace que la experiencia sea más agradable; ambos tienen una integración con el editor muy fluida, mientras que con C# y C++ se necesita editarlos en un IDE separado. Si eres un gran fan de los lenguajes de tipado estático, utiliza C# y C++ en su lugar.

GDScript

GDScript es, como se mencionó anteriormente, el lenguaje principal utilizado en Godot. Su uso tiene algunos puntos positivos en comparación con otros idiomas debido a su alta integración con Godot:

  • Es simple, elegante, y diseñado para que resulte muy familiar a los usuarios de otros idiomas como Lua, Python, Squirrel, etc.
  • Carga y compila a toda velocidad.
  • Es un placer trabajar con la integración del editor, con completado de código para nodos, señales y muchos otros elementos relacionados con la escena que se está editando.
  • Tiene tipos de vectores propios (tales como Vector, transforms, etc.), haciéndolo eficiente para el uso intensivo del álgebra lineal.
  • Soporta múltiples hilos de forma tan eficiente como los lenguajes de tipado estático - una de las limitaciones que nos hizo evitar VMs como Lua, Squirrel, etc.
  • No utiliza ningún recolector de basura, por lo que intercambia un poco de automatización (de todos modos, la mayoría de objetos son pasados por referencia), por determinismo.
  • Su naturaleza dinámica hace que sea fácil optimizar secciones de código en C++ (vía GDNative) si se requiere más rendimiento, todo ello sin recompilar el motor.

Si estás indeciso y tienes experiencia en programación, especialmente con lenguajes de tipado dinámico ¡decídete por GDScript!

VisualScript

A partir de la versión 3.0, Godot ofrece Visual Scripting. Esta es una implementación típica de un lenguaje de «bloques y conexiones», pero adaptado a cómo funciona Godot.

Visual scripting es una gran herramienta para no-programadores, o incluso para desarrolladores experimentados que quieren hacer partes del código más accesibles a otros, como diseñadores o artistas de juegos.

También lo pueden utilizar programadores para construir máquinas de estado o flujos de trabajo de nodos visuales personalizados - por ejemplo, un sistema de diálogo.

.NET / C#

Dado que Microsoft C# es uno de los favoritos entre los desarrolladores de juegos, hemos añadido soporte oficial para él. C# es un lenguaje maduro con una gran cantidad de código escrito para él, y el soporte se añadió gracias a una generosa donación de Microsoft.

Tiene un excelente equilibrio entre rendimiento y facilidad de uso, aunque uno debe ser consciente de su recolector de basura.

Puesto que Godot utiliza el runtime Mono.NET, en teoría cualquier librería o framework .NET de terceros se puede utilizar para scripting en Godot, así como cualquier lenguaje de programación compatible con Common Language Infrastructure, como F#, Boo o ClojureCLR. Sin embargo, en la práctica, C# es la única opción compatible oficialmente con .NET.

GDNative / C++

Finalmente, una de nuestros añadidos más brillantes para la versión 3.0: GDNative permite la creación de scripts en C++ sin necesidad de recompilar (o incluso reiniciar) Godot.

Se puede utilizar cualquier versión de C++, y la combinación de marcas y versiones de compiladores para las bibliotecas compartidas generadas funciona perfectamente, gracias al uso de nuestro C API Bridge interno.

Este lenguaje es la mejor opción por rendimiento y no es necesario que se use en todo el juego, ya que otras partes se pueden escribir en GDScript o Visual Script. Sin embargo, la API es clara y fácil de usar, ya que se parece, en su mayoría, a la API C++ actual de Godot.

Se podrían utilizar más lenguajes a través de la interfaz de GDNative, pero debes tener en cuenta que no tenemos soporte oficial para ellos.

Scripting de una escena

Para el resto de este tutorial configuraremos una escena GUI que consistirá en un botón y una etiqueta, donde al pulsar el botón se actualizará la etiqueta. Esto lo demostrará:

  • Escribir un script y añadirlo a un nodo.
  • Conexión de los elementos de la UI mediante señales.
  • Escribir un script que pueda acceder a otros nodos de la escena.

Antes de continuar, asegúrate de leer la referencia GDScript. Es un lenguaje diseñado para ser simple, y la referencia es corta, por lo que no tomará más que unos pocos minutos para obtener una visión general de los conceptos.

Configuración de la escena

Utiliza el cuadro de diálogo «Añadir nodo hijo» al que se accede desde la pestaña Escena (o pulsando Ctrl+A) para crear una jerarquía con los siguientes nodos:

  • Panel
    • Label
    • Botón

El árbol de la escena debería verse así:

../../_images/scripting_scene_tree.png

Utiliza el editor 2D para situar y redimensionar el Botón y la Etiqueta para que se parezcan a la imagen de abajo. Puedes configurar el texto desde la pestaña Inspector.

../../_images/label_button_example.png

Finalmente, guarda la escena con un nombre como sayhello.tscn.

Añadir un script

Haz clic con el botón derecho del ratón en el nodo Panel y selecciona » Añadir Script » en el menú contextual:

../../_images/add_script.png

Aparecerá el cuadro de diálogo de creación del script. Este cuadro de diálogo permite definir el idioma del script, nombre de la clase y otras opciones relevantes.

En GDScript, el propio archivo representa la clase, por lo que el campo del nombre de la clase no se puede editar.

El nodo al que estamos añadiendo el script es un panel, por lo que el campo Hereda se rellenará automáticamente con «Panel». Esto es lo que queremos, ya que el objetivo del script es ampliar la funcionalidad de nuestro nodo panel.

Finalmente, introduce un nombre de ruta para el script y haz clic en Crear:

../../_images/script_create.png

A continuación, se creará el script y se añadirá al nodo. Puedes ver esto como un icono de «Abrir script » al lado del nodo en la pestaña Escena, así como en la propiedad del script en el Inspector:

../../_images/script_added.png

Para editar el script, pulsa cualquiera de estos botones, ambos resaltados en la imagen de arriba. Esto te llevará al editor de scripts, donde se incluirá una plantilla predeterminada:

../../_images/script_template.png

No hay mucho allí. La función _ready() se llama cuando el nodo, y todos sus hijos, entran a la escena activa. Nota: _ready() no es el constructor, ese es _init().

El papel del script

Un script agrega comportamiento al nodo. Este se utiliza para controlar cómo se quiere que el nodo funcione y cómo debe interactuar con otros nodos: hijos (children), padre (parent), hermanos, etc. El ámbito local del script es el nodo. En otras palabras, el script hereda las funciones provistas por ese nodo.

../../_images/brainslug.jpg

Manejando una señal

Las señales son «emitidas» cuando alguna acción específica sucede, y se pueden conectar a cualquier función de cualquier instancia de script. Las señales se utilizan sobre todo en nodos de la IGU, aunque otros nodos también tienen señales, e incluso puedes definir señales en tu propio script.

En este paso, conectaremos la señal pressed a una función personalizada. Formar conexiones es la primera parte y definir la función personalizada es la segunda parte. Para la primera parte, Godot proporciona dos maneras de crear conexiones: a través de una interfaz visual que proporciona el editor o a través del código.

Aunque usaremos el método de código para el resto de esta serie de tutoriales, cubriremos cómo funciona la interfaz del editor para futuras referencias.

Selecciona el nodo Button en el árbol de escenas y luego selecciona la pestaña Nodo. A continuación, asegúrate de que has seleccionado Señales.

../../_images/signals.png

Si seleccionas «pressed()» en «BaseButton» y haces clic en el botón «Conectar… » en la parte inferior derecha, se abrirá el cuadro de diálogo de creación de la conexión.

../../_images/connect_dialogue.png

The top of the dialogue displays a list of your scene’s nodes with the emitting node’s name highlighted in blue. Select the «Panel» node here.

The bottom of the dialogue shows the name of the method that will be created. By default, the method name will contain the emitting node’s name («Button» in this case), resulting in _on_[EmitterNode]_[signal_name].

Y así concluye la guía sobre cómo usar la interfaz visual. Sin embargo, este es un tutorial de scripting, así que por el bien del aprendizaje, ¡vamos a sumergirnos en el proceso manual!

Para lograr esto, veremos una función que es probablemente la más usada por los programadores de Godot: Node.get_node(). Esta función utiliza rutas para recuperar nodos en cualquier parte de la escena, en relación con el nodo al que pertenece el script.

Para mayor comodidad, borra todo lo que se encuentra debajo de extends Panel. Rellenarás el resto del script manualmente.

Dado que el Botón y la Etiqueta son hermanos en el Panel donde se adjunta el script, puedes recuperar el Botón escribiendo lo siguiente debajo de la función _ready():

func _ready():
    get_node("Button")
public override void _Ready()
{
    GetNode("Button");
}

A continuación, escribe una función que se llamará cuando se pulse el botón:

func _on_Button_pressed():
    get_node("Label").text = "HELLO!"
public void _OnButtonPressed()
{
    GetNode<Label>("Label").Text = "HELLO!";
}

Finalmente, conecta la señal «pressed» del botón a _ready() utilizando Object.connect().

func _ready():
    get_node("Button").connect("pressed", self, "_on_Button_pressed")
public override void _Ready()
{
    GetNode("Button").Connect("pressed", this, nameof(_OnButtonPressed));
}

El script final debería verse así:

extends Panel

func _ready():
    get_node("Button").connect("pressed", self, "_on_Button_pressed")

func _on_Button_pressed():
    get_node("Label").text = "HELLO!"
using Godot;

// IMPORTANT: the name of the class MUST match the filename exactly.
// this is case sensitive!
public class sayhello : Panel
{
    public override void _Ready()
    {
        GetNode("Button").Connect("pressed", this, nameof(_OnButtonPressed));
    }

    public void _OnButtonPressed()
    {
        GetNode<Label>("Label").Text = "HELLO!";
    }
}

Ejecuta la escena y pulsa el botón. Deberías obtener el siguiente resultado:

../../_images/scripting_hello.png

¡Vaya, hola! Felicidades por el script de tu primera escena.

Nota

Un malentendido común con respecto a este tutorial es cómo funciona get_node(path). Para un nodo concreto, get_node(path) busca a sus hijos inmediatos. En el código anterior, esto significa que Button debe ser hijo de Panel. Si Button fuera en cambio un hijo de Label, el código para obtenerlo sería:

# Not for this case,
# but just in case.
get_node("Label/Button")
// Not for this case,
// but just in case.
GetNode("Label/Button")

También, recuerda que los nodos se referencian por nombre, no por tipo.

Nota

El panel derecho del diálogo de conexión sirve para vincular valores específicos a los parámetros de la función conectada. Puedes añadir y eliminar valores de diferentes tipos.

El enfoque de código también permite esto con un 4º parámetro Array que está vacío por defecto. Tómate la libertad de leer sobre el método Object.connect para más información.