Árbol de Escenas

Introducción

En tutoriales previos, todo giraba en torno al concepto de nodos. Las escenas son simplemente una colección de nodos. Ellas se activan una vez que entran al árbol de escenas.

MainLoop

La forma en la cual Godot funciona internamente es la siguiente. Existe la clase OS<class_OS, la cual es la única instancia que corre desde el principio. Posteriormente son cargados todos los controladores, servidores, lenguajes de scripting, sistema de escenas, etc.

Una vez finalizada la inicialización, OS necesita que se le suministre un MainLoop para que se ejecute. Hasta este punto, todo esto está funcionando internamente (puedes comprobar el archivo main/main.cpp en el código fuente si alguna vez estás interesado en ver cómo funciona internamente).

El programa del usuario o juego, comienza en el MainLoop. Esta clase tiene algunos métodos, para inicialización, inactivo (devolución de llamada sincronizada por el marco), fijo (devolución de llamada sincronizada por la física) y entrada. De nuevo, este es de bajo nivel y cuando creas juegos en Godot, escribir tu propio MainLoop tiene poco sentido.

Árbol de Escenas

Una de las maneras de explicar cómo funciona Godot es que es un motor de juegos de alto nivel sobre un middleware de bajo nivel.

El sistema de escenas es el motor del juego, mientras que la clase OS y los servidores son la API de bajo nivel.

El sistema de escenas provee al SO su propio bucle principal, SceneTree. Este es automáticamente instanciado y configurado cuando se ejecuta una escena, no es necesario ningún trabajo extra.

Es necesario saber que esta clase existe porque tiene algunos usos importantes:

  • It contains the root Viewport, to which a scene is added as a child when it’s first opened to become part of the Scene Tree (more on that next).
  • Contiene información sobre los grupos y dispone de los medios para llamar a todos los nodos de un grupo u obtener una lista de ellos.
  • Contiene alguna funcionalidad de estado global, como configurar el modo de pausa o salir del proceso.

Cuando un nodo es parte del Árbol de Escenas, el singleton SceneTree puede obtenerse simplemente llamando Node.get_tree().

Viewport raíz

El Viewport raíz está siempre en la parte superior de la escena. Desde un nodo, se puede obtener de dos maneras diferentes:

get_tree().get_root() # Access via scene main loop.
get_node("/root") # Access via absolute path.
GetTree().GetRoot(); // Access via scene main loop.
GetNode("/root"); // Access via absolute path.

Este nodo contiene el viewport principal. Cualquier cosa hija de un Viewport es dibujado en su interior por defecto, toma sentido entonces que el nodo principal sea siempre uno de este tipo, de otro modo no se podría dibujar nada en pantalla.

Mientras que se pueden crear otros viewports en la escena (para obtener resultados en pantalla dividida, etc.), ésta es la única que nunca es creada por el usuario. Se crea automáticamente dentro de SceneTree.

Árbol de escenas

Cuando un nodo es conectado, directa o indirectamente, al viewport raíz, se convierte en parte del árbol de escenas.

Esto significa que, como se explicó en los tutoriales anteriores, se obtendrán las llamadas _enter_tree() y _ready() (así como _exit_tree()).

../../_images/activescene.png

Cuando los nodos entran en el Árbol de Escenas, se activan. Tienen acceso a todo lo que necesitan para procesar, obtener entradas, mostrar 2D y 3D, notificaciones, reproducir sonido, etc. Cuando se remueven del árbol de escenas, pierden acceso a todas esas funcionalidades.

Orden del árbol

La mayoría de las operaciones de nodos en Godot, como dibujar 2D, procesar u obtener notificaciones, se hacen en el orden del árbol. Esto significa que los padres y hermanos con un rango menor en el orden del árbol serán notificados antes del nodo actual.

../../_images/toptobottom.png

«Activación» entrando en el Árbol de escenas

  1. Una escena es cargada desde disco o creada mediante scripting.
  2. El nodo raíz de esa escena (sólo una raíz, ¿recuerdas?) se añade como hijo del Viewport «raíz» (desde SceneTree), o a cualquier hijo o nieto del mismo.
  3. Cada nodo de la nueva escena añadida recibirá la notificación «enter_tree» (llamada de retorno _enter_tree() en GDScript) en orden descendente.
  4. Se proporciona una notificación adicional, «ready» ( _ready() callback en GDScript) por conveniencia, cuando un nodo y todos sus hijos están dentro de la escena activa.
  5. Cuando se elimina una escena (o parte de ella), reciben la notificación «exit scene» ( _exit_tree() callback en GDScript) en orden descendente

Cambiando la escena actual

Después de cargar una escena, a menudo se desea cambiar esta escena por otra. La forma más sencilla de hacerlo es usando la función SceneTree.change_scene():

func _my_level_was_completed():
    get_tree().change_scene("res://levels/level2.tscn")
public void _MyLevelWasCompleted()
{
    GetTree().ChangeScene("res://levels/level2.tscn");
}

En lugar de usar rutas de archivos, también se pueden usar recursos listos para usar PackedScene usando la función equivalente SceneTree.change_scene_to(PackedScene scene):

var next_scene = preload("res://levels/level2.tscn")

func _my_level_was_completed():
    get_tree().change_scene_to(next_scene)
public void _MyLevelWasCompleted()
{
    var nextScene = (PackedScene)ResourceLoader.Load("res://levels/level2.tscn");
    GetTree().ChangeSceneTo(nextScene);
}

Esta es una forma rápida y útil de cambiar de escena, pero tiene el inconveniente de que el juego se detendrá hasta que la nueva escena se cargue y se ejecute. En algún momento del desarrollo del juego, puede ser preferible crear pantallas de carga adecuadas con una barra de progreso, indicadores animados o carga en hilos (background loading). Esto debe hacerse manualmente usando autoloads (ver el siguiente capítulo) y Background loading.