Hace unas semanas me pidieron un tutorial sobre barras de vida en Godot… sinceramente tengo años resolviendo el tema de las vidas con los clásicos corazones 😂️.
Sabía que un día eso cambiaría; estoy desarrollando un juego donde me quitaron los corazones para reemplazarlos con una bonita barra de color rojo. Algo como esto:
En este tutorial aprenderán a hacer una barra de vida y actualizar su porcentaje. ¡Empezamos!
Nodos necesarios 📝️
Deben tener en cuenta qué quieren mostrar en su barra. En el ejemplo de arriba sólo necesito la barra en sí y un ícono del corazón. A simple vista dos nodos.
- TextureProgress: nodo de tipo GUI (Game User Interface) que tiene Godot para calcular el porcentaje y modificar la imagen que le facilitemos.
- TextureRect: otro nodo de tipo GUI que sirve para mostrar imágenes. Es algo así como el Sprite, pero optimizado para interfaces.
Con esos nodos podemos resolver nuestro problema. Sin embargo Godot no se limita a eso: teniendo nodos sueltos así puede llevar más tiempo hacer un cambio en el diseño de la interfaz, y nadie quiere perder tiempo.
¿Cómo ahorrar tiempo diseñando una interfaz en Godot?
Las herramientas para controlar la interfaz de nuestros juegos que ofrece Godot, son únicas. Primero que todo: hay diversos contenedores para cada caso del desarrollador.
Esos dos nodos que creamos hace un momento se pueden juntar en un contenedor de elementos horizontales o verticales, así como en una rejilla. Vean las opciones:
Vamos a centrarnos en los contenedores de “caja”: HBoxContainer y VBoxContainer.
- HBoxContainer: sirve para ajustar todos los nodos de tipo GUI que sean sus hijos en varias celdas con un tamaño establecido por él. Posicionará horizontalmente todos los nodos.
- VBoxContainer: hace lo mismo que el nodo de arriba, con la diferencia de que su orden es vertical.
Agrego que también hay un GridContainer suele usarse para hacer inventarios y básicamente es una buena forma de mantener una cuadrícula de elementos GUI.
En el ejemplo que mostré arriba, los dos nodos que forman la barra de vida están dentro de un HBoxContainer. La ventaja de esto es muy clara: si cambian el orden de los hijos de un contenedor, automáticamente cambiarán la celda donde se encuentra, algo así:
Para que esto funcione los hijos de un contenedor deben ser nodos de tipo GUI (verdes).
Tener nuestros elementos dentro de contenedores no sólo es beneficioso para estos casos, pueden convertirse en algo indispensable en cualquier interfaz que estén diseñando.
Aquí tienen un apartado de la documentación de Godot sobre la GUI.
Haciendo una barra de vida desde 0
Hay que acostumbrarse a trabajar con interfaces de usuario como un montón de nodos contenidos en otros. Empiecen creando un nodo de tipo Control.
Cuando hacen clic sobre él, se desbloquea el botón “Layout”:
Si entran en Layout verán que tiene las opciones para diferentes márgenes o tamaños. Si hacen clic sobre uno de esos, el nodo Control se posicionará de una forma diferente. Y lo más importante: si le dicen que se posicione en el centro (o cualquier otra posición), siempre buscará estar en ese lugar cuando carguemos la escena.
Esto hace de los nodos de tipo GUI, más “responsive” o adaptables para cualquier dispositivo.
Nosotros buscaremos la opción de “Completo” o “Full Rect”, ya que con ella podemos ocupar siempre toda la pantalla.
Ahora que tenemos el nodo que “Controla” nuestra interfaz de usuario, podemos añadir un apartado para la barra: en mi ejemplo estoy usando un HBoxContainer, dentro de él hay un nodo de tipo TextureProgress.
En cuanto a texturas se refiere (del nodo TextureProgress), estas son las propiedades que le podemos ajustar:
- Fill Mode: es para especificar donde tiene que empezar a quitar la textura de “progreso”. Como mi barra es horizontal y además el 0 se ubica en la izquierda, la opción correcta es Left to Right (incluso creo que es la opción por defecto).
- Nine Path Rect: el “Nine Path Rect” es un tipo de nodo GUI que está preparado para estirarse a la medida del usuario. Generalmente se usa para la imagen de un fondo. En este caso, si activamos esa propiedad tendremos que estirar la imagen haciendo uso de las propiedades “Stretch”.
- Under: es la textura que está debajo, el marco.
- Over: es la textura (imagen) que está encima de la barra de progreso y su marco.
- Progress: es el bonito color con la forma de nuestra barra que Godot estará reduciendo y aumentando en función del valor que tenga nuestra barra.
Antes de que coloquen las texturas de su barra de vida, tienen que considerar lo siguiente: no pueden editar la posición de las imágenes. Quiero decir que si tienen algo como esto: (supongan que el área seleccionada es un archivo .png)
Si tienen en un archivo único la barra de vida, la imagen del color tiene que medir exactamente lo que queremos y además contar con el mismo espacio que desde el marco hasta el lugar donde ubicaremos la vida:
Deben exportarla con todo ese espacio en transparente si hace falta. Porque de lo contrario tendremos algo que no deseamos…
Volviendo al nodo TextureProgress, todavía faltan unas propiedades muy importantes:
En la sección “Range” pueden ajustar el valor máximo que manejarán (no necesariamente debe ser el 100%), y el valor actual. Ya con esto prácticamente tienen su barra de vida creada.
Si quieren agregar un icono como yo, añadan un nuevo nodo hijo del contenedor. El tipo de nodo que suelen usarse para “iconos” es TextureRect. Para resumir la estructura de nodos, necesitan algo como esto:
En las propiedades del icono, simplemente voy a ponerle la imagen, “Texture”:
Seguramente tienen algo como esto: (elementos separados)
Para personalizar cuánto espacio se separan los elementos dentro del contenedor, tienen que entrar en el nodo padre (HBoxContainer) y activar su propiedad “Separation”.
Para juntar los elementos dentro del contenedor se usa un valor negativo y para alejarlos uno positivo.
Actualizar el valor de la barra
Para hacer esto tienen que agregar un script en algún nodo que quieran usar para controlar… la barra. ¡Sí! el Control que creamos hace un rato puede funcionar.
Pueden hacer esto: un script global (Singlenton) emite una señal que sea “cambiaron las vidas” y el script de la barra toma esa señal y llama a su propia función para actualizarse:
export var heart_bar_path: NodePath func change_heart_bar(new_value) -> void: var heart_bar: Range = get_node(heart_bar_path) heart_bar.value = new_value / 0.04
La variable “heart_bar_path” se asigna desde el Inspector de Godot. En ella deben buscar la ruta del nodo TextureProgress donde está la barra de vida. En la función de abajo una nueva variable local obtendrá el nodo en base a la ruta, y entonces hará que el nuevo valor se ajuste.
Seguro se preguntan ¿de dónde viene ese nuevo valor? Miren mi estructura general de nodos:
En el Script del nodo Level, tengo lo siguiente:
func _ready() -> void: $Jugador.connect("damaged", owner, "change_heart_bar") $Jugador.connect("shooted", owner, "change_mana_bar")
Como sé que dentro del nodo Level hay un jugador, accedo a él y digo que cuando emita una señal de “vida cambiada”, se produzca una llamada a la función “change_heart_bar” del nodo “owner”. El nodo “owner” es el propietario actual de la escena.
Ojo con esto: el owner sólo es el propietario cuando inicias la estructura de escenas que guarda Godot. Si instancias un nuevo nodo, ese nodo no tendrá owner hasta que digas algo como: nodo.owner = owner (para hacer que su propietario sea el propietario del script donde lo estás instanciando).
En mi juego ya sé que el “owner” del nodo Level es el nodo Game. Y dentro de ese nodo es que llamo a la función:
export var heart_bar_path: NodePath func change_heart_bar(new_value) -> void: var heart_bar: Range = get_node(heart_bar_path) heart_bar.value = new_value / 0.04
Esta es mi forma de hacer las cosas, pero no la única.
La fórmula para encontrar porcentajes es dividir el valor actual entre el valor máximo que se tenía. El máximo de vidas que tiene mi nave es 4, y el nuevo valor obligatoriamente será 3 cuando se ejecute esa función.
Si divido 3 / 4 voy a tener el resultado y sólo tendría que multiplicarlo por 100. Sin embargo puedes ahorrarte tiempo si al número máximo le agregas dos ceros (Ojo, esto sólo lo hago porque mis vidas son menores a 10 y 100).
Si te gustan estos tutoriales puedes apoyarme en Patreon o hacer una donación en PayPal 🙂️.
Interesante cuestión. ¡Ya deseo aplicarla! Ahora solo me queda pensar que juego seria apropiado para ello… Solo me viene a la mente Megaman. Así de intenso estuve jugando hasta pasado ayer XD.
y en tal caso a los enemigo también le quiero poner barra de vida como seria ??