Godot Game: señales y Bus de eventos
Señales en Godot
Una de las características más útiles en Godot es el uso de señales. Una señal es una forma de indicar que ha ocurrido algo, un evento, que puede ser interesante para algunas partes de tu juego. Para usar señales se necesita:
- Definir una señal, que se referirá al evento que tiene que ocurrir.
- Conectar la señal a funciones que se darán por enteradas cuando se emita la señal
- Emitir la señal, para que las funciones conectadas puedan ser llamadas
Muchos nodos tienen señales predefinidas que se pueden conectar utilizando el editor, pero este método puede ser un poco limitado, sobre todo porque cuando queremos conectar señales entre escenas diferentes empiezan a complicarse las cosas.
Por eso, prefiero definir y manejar las señales mediante código. La forma más sencilla de definir una señal es:
signal cosa_que_pasa
donde cosa_que_pasa suele hacer referencia a un evento como un impacto recibido, un powerup recogido o un enemigo abatido. El manual de estilo recomienda nombrarlas en pasado, por ejemplo, moneda_recogida.
Para que la señal sea útil tiene que estar conectada a una función. La fórmula para hacerlo en código también es muy sencillo:
nombre_de_la_señal.connect("nombre_de_la_función")
Aunque para que la conexión tenga efecto lo más normal es colocar la expresión anterior dentro de la función _ready():
func _ready() -> void:
nombre_de_la_señal.connect("nombre_de_la_función")
Por supuesto, en algún lugar de ese script tenemos que tener definida la función nombre_de_la_función:
func _ready() -> void:
nombre_de_la_señal.connect("nombre_de_la_función")
func nombre_de_la_función() -> void:
print("Pasó algo")
Ahora, para que se ejecute la función conectada a la señal hace falta emitir la señal. Eso puede ocurrir de múltiples formas, por ejemplo tras comprobar que se cumple alguna condición:
func _physics_process(delta) -> void:
if algo_pasó == true:
cosa_que_pasa.emit()
De modo que, poniéndolo un poco en el contexto de Fumigate! (el juego que estoy desarrollando para aprender Godot), podríamos tener algo así en el script de la planta:
extends Node2D
var plants : int = 10
var plant_health : int = 100
signal plan_destroyed
func _ready() -> void:
plant_destroyed.connect(decrease_plants)
func _physics_process(delta) -> void:
if plant_health <= 0:
plant_destroyed.emit()
func decrease_plants() -> void:
plants -= 1
Usando el Bus de eventos
Todo esto funciona bien porque la señal, la emisión y la recepción ocurren en el mismo script. Cuando esto no es posible se puede utilizar un autoload, que suelo llamar event_bus.gd. Los autoloads en Godot son scripts que se cargan siempre, independientemente de cual sea la escena principal, por lo que sus contenidos están disponibles para cualquier escena que se esté ejecutando. De esa forma, si definimos la señal en el script event_bus, podríamos emitirla desde cualquier escena y conectarla a cualquier función, independientemente de en qué script esté definida. Como no necesitan ninguna finalidad adicional extienden a Node
Para ello en event_bus.gd
definiríamos la señal al igual que vimos antes:
extend Node
signal coin_collected
Y ahora para conectarla, lo único que tendríamos que tener en cuenta es que hay que añadir el nombre del event_bus a la señal para poder utilizarla, es decir:
func _ready() -> void:
EventBus.plant_destroyed.connect(decrease_plants)
func decrease_plants() -> void:
plants -= 1
Fíjate que el script está escrito con el nombre en minúsculas y separando las palabras con un gion bajo, mientras que en el script lo referencio poniedo cada palabra con mayúscula inicial y sin separar:
event_bus.gd -> EventBus
Después, para finalizar, tendríamos que emitir la señal teniendo en cuenta que también tenemos que incluir el nombre del eventbus:
func _physics_process(delta) -> void:
if plant_health <= 0:
EventBus.plan_destroyed.emit()
Lo más interesante de este método es que puedes conectar tantos scripts a la señal como necesites, por ejemplo, podrías conectar la señal a un macador que se actualizara con el número de plantas que quedan o cualquier otra cosa que se te ocurra.
Más cosas de las señales
Para un futura actualización de este artículo hablaré de cómo se pueden pasar valores con las señales, que es permite darle más funcionalidad y del uso de las funciones lambda para conectar las señales, que permite ahorrarnos el paso extra de definir una nueva función para ejecutarla cuando se recibe la señal.
De momento eso es todo. Un saludo.