Hola, a todos sean bienvenidos a mi nuevo post. En el post de hoy hablaremos sobre tres temas, pasemos al primero.

Recursos

Hasta ahora vimos la importancia de los nodos, porque estas se encargaran de todas nuestras acciones, desde la aplicacion de las señales, dibujo de los sprites, animaciones, controlar la GUI, etc. En cambio los recursos son meros contenedores de datos, no pueden recibir ni contener ninguna accion, es decir estos van a ser las texturas, los archivos de las escenas, los archivos de los scripts, las fuentes, etc., cuando godot graba (en el disco) las escenas o los scripts estas acciones los convierten en recursos.
Cuando un recurso es cargado desde el disco, este es cargado una sola vez y esto significa que una copia del recurso es cargado en memoria, tratar de recargar el recurso nos llevara a crear copias y copias del mismo recurso, y como dijimos antes por ser contenedores de datos no necesitan ser duplicados por ende con llamarlo una sola vez es necesario. Ahora veremos una estructura de nodos y recursos.

nodes_resources
Fuente: docs.godotengine.org

Como vimos hasta ahora, en godot todos los elementos (Recursos, nodos o cualquier otra cosa) puede exportar sus propiedades, estas pueden ser de muchos tipos (cadena, entero, vector2, etc) y cualquiera de estos tipos pueden ser un recurso, por esto tanto los nodos y los recursos pueden contener recursos como propiedades. Ahora veremos la diferencia entre los recursos externos y propios del programa. Veamos el siguiente ejemplo, una textura de un sprite

spriteprop
Fuente: docs.godotengine.org

En la imagen se puede ver el preview de la textura cargada para el sprite, si nosotros clickeamos en el boton > ubicada a la derecha de la imagen podremos acceder a las propiedades del recurso, como se ve en la siguiente imagen

resourcerobi
Fuente: docs.godotengine.org

Como podemos ver, cuando una textura es cargada a traves de un archivo, como se ve en la imagen son considerados recursos externos, ahora si nosotros borraramos el valor de path y grabaramos la escena, esta quedara igualmente en el sprite pero ahora pasara a ser del tipo propia (built-in) y no dependera de ningun recurso externo. Como dijimos anteriormente el recurso sera cargado una sola vez y con distintas instancias de nuestro sprite siempre se utilizara la misma. Ahora veremos como cargar un recurso desde un script, para esto existen dos metodos, veamos el primero:

func _ready():
 var res = load(“res://robi.png”)
 $(“sprite”).texture = res

Esta es la primera opcion donde le decimos via load() cual recurso debemos cargar para setear la textura de nuestro sprite, ahora veamos el segundo metodo:

func _ready():
 var res = preload(“res://robi.png”)
 get_node(“sprite”).texture = res

Este es mas optimo que el anterior porque lo carga durante el tiempo de compilacion pero solo funciona con parametros de cadena constantes por la forma de carga de este metodo. Las escenas almacenadas en el disco tambien son recursos y nosotros tambien podemos cargarlos como tales pero la diferencia va a radicar en el metodo de carga porque nosotros deberemos crearlo como instancia y agregarlo a un nodo, veamos el siguiente ejemplo:

func _ready():
 var bullet = preload(“res://bullet.tscn”).instance()
 add_child(bullet)

Como pueden ver, en la variable bullet usamos el preload() y luego utilizamos el metodo instance() proveniente de PackedScene porque un escena almacenada en el disco es este tipo de recurso y por medio del instance() luego nos permitira agregarlo como child por medio de la funcion add_child(). En general todos los recursos cuando no son mas utilizados son liberados, en la mayoria de los casos los recursos cargados en nodos, scripts u otros recursos son liberados, estos tambien liberan automaticamente a los childs. Por ultimo, tambien podemos utilizar scripting con los recursos pero como dijimos al principio estos son solo contenedores de datos y no es practico utilizar scripting en ellos, pasemos al siguiente tema.

Sistema de archivos

El sistema de archivos es el encargado de administrar todos los elementos (assets) y como son accedidos. En los principios de godot, se utilizaba un sistema de base de datos donde cada elemento era almacenado internamente y se le asignaba un ID, luego se paso a otros metodos pero finalmente se opto por el metodo utilizado actualmente donde los elementos son archivos dentro de un sistema de archivos.
Aca tenemos un ejemplo de un sistema de archivos de un proyecto:

/project.godot
/enemy/enemy.tscn
/enemy/enemy.gd
/enemy/enemysprite.png
/player/player.gd

Donde se puede ver el archivo de raiz (project.godot), luego un directorio llamado enemy donde almacena todos lo relacionado a enemy y otro llamado player donde almacenara todo lo del player y asi sucesivamente, esto nos permitira tener mejor organizado nuestros elementos y mas facil de modificar, pasemos a ver el archivo project.godot. Este archivo es el contenedor de nuestro proyecto, esta ubicado en el raiz de nuestro proyecto y tambien define donde esta el raiz del mismo, es el primer archivo que abre godot cuando abrimos nuestro proyecto, su formato es en texto plano y tiene el mismo formato de win.ini, un archivo con este nombre y en blanco, tambien define un nuevo proyecto en blanco. El delimitador del path es el estandar usado por todos los sistemas operativos, incluido Windows, el simbolo de division (/) como pudieron ver en el ejemplo de ubicacion de sistema de archivos, en windows nosotros utilizamos c:\godot\proyectos\juego pero en godot deberiamos usar c:/godot/proyectos/juego y windows lo interpretara igual, esto se hace para un tema de portabilidad. Luego tenemos el path de recursos, el path que hemos visto en todos nuestros proyectos (res://), esto fue creado para evitar problemas de compatibilidad y permitir portabilidad de los proyectos, este simbolo representa siempre el raiz de nuestro proyecto y es la ubicacion de project.godot, este recurso una vez compilado se convertira en solo lectura. Tambien como existe un path de recursos tambien tenemos un path de usuario, como el anterior es de solo lectura en este se permitira la escritura, esta identificado por user:// y se usara para guardar partidas, descargar contenidos, es decir todo lo relacionado al usuario. Por otro lado se pueden crear sistemas de archivos de host pero esto es poco recomendable por tener incompatibilidad con algunos sistemas pero si es util para crear herramientas de desarrollo de godot. Ahora hablaremos sobre algunos inconvenientes:

  • Nunca mueva o renombre las carpetas o elementos por afuera del editor de godot, si lo hiciera de esta forma debera modificar los paths de cada uno de los nodos si lo hace por el editor este lo hara por usted.
  • Los nombre de los elementos o carpetas, en caso de ser S.O non sensitive, los sistemas operativos que no diferencian entre mayusculas o minusculas como Windows o Mac, si el nombre alterna entre mayusculas o minusculas (rObI.png) y nosotros lo definimos como robi.png dentro de un nodo, no vamos a tener inconvenientes, en cambio si lo trasladamos a un S.O sensitive, como Linux o Unix, tendremos inconvenientes con la ubicacion de los mismos.

Para evitar esto, en el primer caso siempre debemos manejarnos con el editor y en el segundo caso, lo mas recomendable es utilizar un nombre mas estandar como por ejemplo todo minusculas, para evitar incompatiblidad entre los distintos S.O del mercado, pasemos al ultimo tema.

Arbol de escenas

El arbol de escenas, es la formacion creada por los nodos y los childs dentro de cada escena, tal como vimos hasta ahora en nuestros proyectos. Godot trabaja internamente de la siguiente forma, inicia la clase OS y esta es la unica instancia que corre desde el principio y el resto son cargadas, una vez finalizada la iniciacion se necesita de un MainLoop para correr y estos son procesos internos, pero si tienes curiosidad por como trabaja mira el archivo main/main.cpp, los unicos tres procesos iniciados, son idle (llamada de sincronizacion de frames), fixed (llamada de sincronizacion de fisicas) e input (metodos de ingresos). Veamos alguna de las formas de explicar como en godot trabaja:

  • El sistema de escenas es el game engine.
  • La clase OS y server son las API a bajo nivel.
  • SceneTree (Arbol de escenas) es el MainLoop creado para la clase OS y es automatico.
  • La clase OS contiene el root Viewport, esta es utilizada para convertir una escena en un child cuando es abierta por primera vez y hacerla miembro del arbol de escenas.
  • Contiene informacion sobre los grupos, y se usa para llamar a todos los nodos de un grupo o listar los mismos.
  • Contiene algunos estados globales de funcionalidad, como el modo pausa o quitar algun proceso.

Ahora hablemos sobre Root viewport, es el punto maximo de una escena y se puede obtener desde un nodo de las siguientes formas:

get_tree().get_root() # accediendo via scenemainloop
get_node(“/root”) # accediendo via path absoluto

Este nodo contiene el viewport principal, por esto todos los childs creados se haran por debajo de este por defecto, mientras se puede crear otros viewport (por ejemplo para una pantalla partida) este es el unico no creado por el usuario y si automaticamente para el arbol de escenas. Cuando un nodo esta conectado directa o indirectamente al viewport este se convierte en parte del arbol de escenas. Veamos la siguiente imagen:

activescene
Fuente: docs.godotengine.org

Tenemos un modo inactivo, el cual al entrar en modo activo pasa a formar parte del arbol de escenas, donde se procesara lo definido, y una vez removido del arbol de escenas, este pierde el acceso. En el arbol tambien hay un orden, donde cada nodo tiene un ranking y cada notificacion va a llegar en un orden dependiendo de la ubicacion del mismo, veamos la siguiente imagen

toptobottom
Fuente: docs.godotengine.org

Como pueden ver en el nodo Panel, el child name_label esta por arriba del resto de sus compañeros pero por debajo de Panel, y por ende las notificaciones de este y hacia el nodo raiz sera el primero en ejecutarse. Veamos una forma de cambiar una escena via scripting:

func _my_level_was_completed():
 get_tree().change_scene(“res://levels/level2.tscn”)

Este es un ejemplo donde una vez completado, atraves de get_tree() y el metodo chamge_scene() le informamos cual escena debe cargar. Ahora veamos como se convierte en activa ingresando al arbol de escenas:

  • Una escena es cargada del disco o via script.
  • El root de esa escena es cargado como child del root viewport.
  • Cada nodo de la nueva escena recibe la notificacion de _enter_tree() (llamado via GDscript) en el order descendente (de arriba hacia abajo).
  • Una notificacion extra del llamado _ready() (llamado via GDscript) es provista por conveniencia, esto sucede cuando un nodo y todos sus “chldrens” ingresan al arbol de escenas.
  • Cuando una escena, o una parte de ella, es removida recibe una notificacion de _exit_tree() (llamada via GDscript) en el orden ascendente (de abajo hacia arriba).

Con esto terminamos con estos tres temas, los cuales hemos visto en los posts anteriores pero nunca nos hemos metido muy a fondo para explicar cada uno de ellos, si bien no hemos hecho una explicacion muy exhaustiva si hemos podido ver algunos detalles del porque existen tal o cual forma de acceder a nuestros elementos en el disco y como son cargados como texturas y como algunos pueden interactuar via scripting, espero les haya sido de utilidad y nos vemos en el proximo post.