Hola, a todos sean bienvenidos a mi post. Hoy veremos como crear la interaccion entre los distintos elementos de nuestro GUI para un juego. En esta leccion aprenderemos:
- Como conectar un caracter a la GUI con señales
- Como controlar la GUI con GDScript
- Como animar una barra de vida con un nodo Tween
Cuando creamos un codigo de juego, primero crearemos todo el nucleo del gameplay: las mecanicas del juego, los controles del jugador, las condiciones de victoria y derrota. Esto lo podran ver en este post, donde realizamos nuestro segundo juego, y luego haremos nuestra UI, User Interface. En general la mejor practica es crear una escena para cada caracter, con sus propios scripts y por ende propios elementos.
En el ejemplo de este post veremos como un «enemigo» ataca al jugador y produce la reduccion de la barra de vida, empecemos con nuestro ejemplo. Para iniciar vamos a descargar este archivo ui_code_life_bar, una vez descargado lo extraen dentro de su carpeta de proyectos. Desde el selector de proyectos, elegiremos importar e iremos hasta la carpeta donde lo extrajimos para abrir el archivo proyecto.godot contenido en la carpeta start, como hicimos hasta ahora, una vez realizado se abrira el editor de proyectos y deberan tener una pantalla de este estilo
Una vez cargado, la escena es basicamente a la barra rosa atacara al cuadrado verde pero como todavia no esta conectada a la barra de vida del GUI, este solamente desaparecera, les dejo un video de ejemplo
Nuestra escena contiene los siguientes elementos:
- Un sprite de fondo de pantalla
- Un nodo que hace de GUI
- Dos caracteres
Esto lo pueden ver en la siguiente imagen
Si se fijan podran ver como GUI ya tiene asignado un script, este es el contenido del mismo:
extends MarginContainer
onready var number_label = $Bars/LifeBar/Count/Background/Number
onready var bar = $Bars/LifeBar/TextureProgress
onready var tween = $Tween
func _ready():
pass
Veamos las tres variables con mas detalles:
- number_label: Es un nodo de tipo label, este se encargara de mostrar la cantidad de vida como un numero.
- bar: Es un nodo TextureProgress, y es la barra de vida misma.
- tween: Es un nodo de estilo de componente, es el encargado de controlar cualquier valor o metodo desde cualquier otro nodo.
Si se preguntan por el signo de pesos ($), este es un equivalente a la funcion get_node(), les recomiendo este post para verlo en mas detalle, ahora pasaremos a ver como rellenar nuestra barra de vida. Primero iremos al nodo llamado GUI, elegiremos el icono de script para editarlo
Una vez elegido, nos abrira el script para modificarlo, vamos a agregar el texto resaltado en negrita:
extends MarginContainer
onready var number_label = $Bars/LifeBar/Count/Background/Number
onready var bar = $Bars/LifeBar/TextureProgress
onready var tween = $Tween
func _ready():
var player_max_health = $»../Characters/Player».max_health
bar.max_value=player_max_health
A comparacion del codigo anterior reemplazamos el comando pass por lo resaltado en negrita, aqui vamos a crear una nueva variable llamada player_max_health, donde le diremos que busque el nodo Player y le asigne el valor definido en max_health, luego asignaremos el valor maximo (max_value) de la variable bar, es decir la barra de progreso, con el valor ingresado en player_max_health. Esto lo hacemos por dos razones, primero porque el script Player.gd setea el valor de max_health al comienzo del juego y nada nos garantiza que el valor de health sea igual a max_health. Ahora vincularemos a una señal para poder actualizar la barra de vida por cada golpe efectuado al jugador, porque hacerlo por señal y no por la funcion _process() como hemos visto en otros post?. Simple, en este caso tenemos un solo nodo para monitorear y podriamos hacerlo pero en la vida real, un juego tendra mas nodos y mientras mas vinculemos por este metodo seremos mas propensos a generar errores por eso es recomendable aprender a vincularlos por señales, pasemos a generar nuestra señal para la reduccion de la barra de vida. Primero elegiremos al nodo Player, luego iremos hacia abajo y elegiremos la solapa Nodo, al lado de Inspector, y ahi elegiremos la señal health_changed()
Luego eligen el boton Conectar, aparecera un menu de dialogo y ahi deben elegir el nodo GUI, deberia quedar algo asi:
Si todo sale bien, le damos Conectar y esto deberia crear una funcion relacionada a health_changed() dentro del script encargado de manejar a GUI, es decir GUI.gd, deberia quedar algo asi
Ahora haremos nuestra modificacion, tenemos nuestra funcion creada por el editor:
func _on_Player_health_changed():
Le agregaremos un argumento para recibir el valor de health cada vez que es modificado, la nueva definicion de la funcion deberia ser asi:
func _on_Player_health_changed(player_health):
Esto se actualizara gracias a la señal porque si vemos el script player.gd, este cuando emite la señal health_changed() nos enviara el valor health
emit_signal(«health_changed», health)
Ahora vamos a crear una nueva funcion para actualizar los valores de nuestras indicadores de la GUI, veamos la funcion:
func update_health(new_value):
number_label.text = str(new_value)
bar.value = new_value
Una vez creada esta funcion vamos a agregarla en dos funciones, la primera va a ser _ready() la cual se encargara de inicializar el indicador de salud, el otro va a ser la funcion de health_changed(), pasemos a ver las mismas:
func _ready():
var player_max_health = $»../Characters/Player».max_health
bar.max_value=player_max_health
update_health(player_max_health)
ahora veamos la otra funcion:
func _on_Player_health_changed(player_health):
update_health(player_health)
Una vez realizado, podemos probar como esta nuestra escena hasta ahora, esto lo pueden hacer apretando F5, o ver el video siguiente para ver como estamos hasta ahora
Hasta aqui tenemos una barra funcional de cuando perdemos energia y morimos pero ahora vamos a introducir un poco mas de animacion por medio del nodo Tween, este es un tipo esencial para las animaciones de las propiedades.
Como habran visto, ya tenemos un nodo Tween, ahora los vincularemos a la funcion update_health() para lograr una mejor animacion cada vez que el jugador es impactado. Pasemos a hacer la modificacion y luego la explicaremos:
func update_health(new_value):
tween.interpolate_property(self,»animated_health», animated_health, new_value, 0.6, Tween.TRANS_LINEAR,Tween.EASE_IN)
if not tween.is_active():
tween.start()
Antes de modificar nuestra funcion, debemos crear una variable al comienzo del script, a esta la llamaremos animated_health y tendra un valor inicial de cero, les muestro como deberia ser creada:
var animated_health = 0
Despues de creada esta variable pasaremos a analizar nuestra nueva funcion update_health(), para esta vamos a utilizar al Tween almacenado en la variable tween, en este caso vamos a usar la propiedad interpolate_property() porque nos permite animar un numero pero no nos permitira animar un texto, en este caso el valor de number, pero por ahora analizaremos los valores de interpolate_property():
- El primer campo dice la referencia de quien es el dueño de la propiedad para animar, en este caso self, significa a si mismo.
- El segundo campo, sera la propiedad identificadora como cadena (string), en este caso la variable animated_value.
- El tercer campo, ira el valor inicial, en este caso sera seteado por el valor contenido en animated_health.
- El cuarto campo, sera el valor final, en este caso sera seteado por new_value.
- El quinto campo, sera la duracion de la animacion en segundos, pondremos 0.6
- El sexto campo, es el encargado del tipo de transicion, para este caso elegimos un tipo lineal
- El septimo campo, es el encargado de setear con cual facilidad se combinara con la ecuacion.
Como vemos es bastante sencillo las acciones tomadas por cada campo, salvo la ultima para este caso no tiene ninguna funcion por no afectar la funcion lineal del campo anterior pero en este caso se utiliza sino nos devolveria un error, hasta aqui creamos como es la animacion pero todavia no la activamos para esto debemos utilizar el siguiente condicional if:
if not tween.is_active():
tween.start
Este condicional dice si tween no esta activo sera iniciado, esta es su unica funcion y la unica vez donde lo llamamos pero todavia no tenemos una modificacion de nuestra barra ni de nuestro valor al lado de la misma, para eso debemos agregar las siguientes lineas:
func _process(delta):
var round_value = round(animated_health)
number_label.text = str(round_value)
bar.value = animated_health
Como ven para actualizarlo utilizamos la funcion _process(delta), para saber sobre esta funcion les recomiendo este post, la cual se encargara de setear los valores en los dos nodos de la GUI, number_label y bar, primero haremos un redondeo del valor contenido en animated_health y este lo almacenara en la variable round_value, luego a traves de la funcion llamada str(), esta se encarga de convertir el valor entre parentesis en cadena (string), le ingresamos el valor obtenido en round_value, y por ultimo asignaremos el valor de animated_health a la propiedad value de la barra (bar) y tween se encargara de darle una animacion mas fluida. Hasta aqui tenemos un barra perfectamente animada pero ahora agregaremos la ultima funcion, la cual hara desaparecer la barra de vida cuando nuestro personaje muera, vamos a poner manos a la obra. Como vimos ya tenemos una animacion para la barra, pero tambien se encarga de hacer la desaparicion de nuestro personaje cuando muere y todo hecho a traves de tween, por lo tanto para animar la desaparicion de la barra usaremos el mismo nodo, empecemos con este paso final. Iremos a los nodos y elegiremos el nodo Player, luego pasaremos a la solapa Nodos y seleccionaremos la señal died():
Una vez seleccionado haremos click en Conectar y en el menu que se nos abre elegiremos a GUI y deberia quedar asi:
Elegimos Conectar y esto nos creara una nueva funcion en nuestro script GUI.gd:
func _on_Player_died():
Ahora procederemos a modificar nuestra nueva funcion para crear el efecto final, veamos el codigo y luego lo explicamos:
func _on_Player_died():
var start_color=Color(1.0,1.0,1.0,1.0)
var end_color = Color(1.0,1.0,1.0,0.0)
tween.interpolate_property(self, «modulate», start_color, end_color,1.0, Tween.TRANS_LINEAR, Tween.EASE_IN)
Para esta funcion vamos a crear dos variables, una va a ser start_color y la otra va a ser end_color, en ambos casos utilizaremos el constructor Color() con los cuales crearemos el valor inicial y el valor final respectivamente, el constructor Color() se compone de cuatro variables, las tres primeras se encargan de setear el color, el primero es el valor de rojo, el segundo de verde y el tercero de azul, en la cuarta variable se informara el valor de alfa, observen que en ambos casos seteamos un color blanco pero variamos el valor de alfa. En la siguiente linea volveremos a usar interpolate_property() pero en este caso en la segunda propiedad usaremos modulate, el cual se encarga en base a los cuatro valores de color de modificarlo, en este caso como cambiamos a alfa de 1 a 0, este hara un efecto de desaparicion. Aqui les muestro el resultado final:

El resultado final de nuestra escena (Fuente: docs.godotengine.org)
Hasta aqui hemos logrado animar nuestra barra de vida cada vez que somos atacados, ahora les muestro como queda el script final de GUI.gd:
extends MarginContainer
onready var number_label = $Bars/LifeBar/Count/Background/Number
onready var bar = $Bars/LifeBar/TextureProgress
onready var tween = $Tween
var animated_health = 0
func _ready(): var player_max_health = $»../Characters/Player».max_health
bar.max_value=player_max_health
update_health(player_max_health)
func _on_Player_health_changed(player_health):
update_health(player_health)
func update_health(new_value): tween.interpolate_property(self,»animated_health», animated_health, new_value, 0.6, Tween.TRANS_LINEAR,Tween.EASE_IN)
if not tween.is_active():
tween.start()
func _process(delta): var round_value = round(animated_health)
number_label.text = str(round_value)
bar.value = animated_health
func _on_Player_died():
var start_color=Color(1.0,1.0,1.0,1.0)
var end_color = Color(1.0,1.0,1.0,0.0)
tween.interpolate_property(self, «modulate», start_color, end_color,1.0, Tween.TRANS_LINEAR, Tween.EASE_IN)
Aca hemos visto como animar nuestras barras, como vincularlas a traves de señales para una mejor interaccion, como utilizar a tween para lograr una animacion mas suave, espero les haya sido util y nos vemos en el proximo post.