Como os contaba hace algo más de un mes empezaba una aventurilla personal para aprender a hacer videojuegos, habiendo elegido para ello el motor Godot Engine. La verdad es que la herramienta tiene infinitas facilidades de uso y tanto la documentación como el soporte de la comunidad es directamente exquisito. Hay mil y un recursos para salir de casi cualquier atolladero. Sin embargo, recientemente me crucé con uno que me costó bastante solventar de una manera más o menos buena.

El caso

En Nincatzu existen unas torretas que van disparando shurikens voladores cada cierto tiempo. Como parte del desarrollo para empezar a incorporar sonido al juego, quería que dichas torretas produjesen un sonido con cada disparo, pero solo cuando las torretas estuviesen lo suficientemente cerca del jugador (no quería que el jugador estuviese escuchando disparos todo el rato de una torreta que esté al final del nivel, por ejemplo).

Torreta no visible que reproduce sonidos en rojo

Mi primer intento fue usar lo que te dice que uses literalmente todo internet: Un VisibilityNotifier2D, un nodo capaz de emitir señales cuando su nodo padre entra o sale de la "pantalla" o de un viewport. No me funcionó. En un pequeño escenario con dos torretas, una de ellas fuera de escena, se escuchaba claramente a las dos torretas disparando aunque una de ellas no se veía por ningún lado.

Resulta que, por cuestiones de diseño y rendimiento, el nodo VisibilityNotifier2D está pensado para ser rápido, no preciso, y por tanto puede disparar las señales aunque el nodo no esté realmente en pantalla, que es lo que estaba pasando en mi caso. El nodo disparaba la señal porque el mapa era muy pequeño, y aunque la segunda torreta no estaba visible, estaba lo suficientemente cerca como para activarlo. Esto lo pude comprobar replicandolo en un proyecto en limpio para descartar efectos colaterales que pudiesen darse en un proyecto con algo más de chicha.

Soluciones

Después de tirarme varios días buscando soluciones en la documentación oficial, o alguna manera de trucar o configurar el VisibilityNotifier2D me rendí y me atreví a escribir un post en el subreddit oficial de Godot para ver si alguien podía ayudarme, y la verdad es que quedé encantado con la ayuda ofrecida por la comunidad, aunque finalmente nadie pudiese darme una solución realmente completa y funcional.

Aunque el sprite está fuera del viewport, se acerca lo suficiente para activar el VisibilityNotifier2D

La solución a la que llegue finalmente es bastante sencilla y rudimentaria: Dentro de la escena del jugador, el cual contiene la camara principal del juego, añadí un Area2D con un CollisionShape2D con un tamaño un poco mayor al de la pantalla. Por un poco mayor no puedo decir un número fijo, la fuí ajustando a base de prueba y error. Conecté las señales body_entered y body_exited a unos manejadores que lo que hacen es, comprobar que el objeto que entra tiene un método llamado set_on_screen, set_is_visible o algo así y que tiene la siguente firma:

func set_on_screen(onScreen: bool) -> void:
    self.is_on_screen = onScreen

Dicha función vive en los nodos de las torretas, y lo único que hacen es setear un flag booleano que marca si la torreta está visible o no. Comprobando ese booleano se si tengo que reproducir el sonido de disparo o no, algo así:

func _on_turret_shoot() -> void:
    if self.is_on_screen:
        $SoundPlayer.play()

Se que esto mismo se puede hacer de una forma más elegante usando señales, pero esta solución me pareció más rápida de implementar y me funcionó bastante bien para mi caso concreto. Al final dejo el uso del VisibilityNotifier2D para otros casos donde pueda ser más útil, ya que para mi caso es completamente inútil.

Conclusiones

Al final, el nodo que te da Godot funciona, y seguro que está hiper optimizado, pero como siempre, toda solución depende del contexto y en mi caso dicho nodo no me sirve ya que mis escenarios son casi todos muy pequeños. En otro tipo de juegos con mapas grandes y muchos objetos saliendo de pantalla (un juego de disparos con vista cenital o algo así) seguro que la solución óptima es el VisibilityNotifier2D. Estaría bien poder configurar el grado de sensibilidad del nodo, pero por el momento no parece que dicha opción vaya a llegar. No tengo ni idea ni números ni nada que desmuestren el impacto en el rendimiento de mi solución, no me preocupa demasiado, a día de hoy la pantalla más compleja solo tiene 3 torretas. Insisto en esto, para mi caso, de momento esta es la mejor solución que encontré, no digo que está sea la solución para cualquier problema, seguramente solo sea solución para MI problema en concreto. Como siempre, depende del contexto.