Uso avanzado de Salas y Portales

Gameplay callbacks (retroalimentación de jugabilidad)

Aunque la ocultación de objetos reduce en gran medida la cantidad de objetos que deben renderizarse, existen otros costos asociados al mantenimiento de objetos en un juego además de la renderización final. Por ejemplo, en Godot, los objetos animados seguirán animándose independientemente de si aparecen en pantalla o no. Esto puede consumir mucha capacidad de procesamiento, especialmente para objetos que utilizan deformación de software (donde el cálculo de deformación se realiza en la CPU).

No temas, las habitaciones y los portales pueden resolver estos problemas, y más.

Al construir nuestro sistema de habitaciones para el nivel de nuestro juego, no solo tenemos la información necesaria para la ocultación de objetos, sino que también hemos creado de manera conveniente la información requerida para saber qué habitaciones se encuentran en el área de "jugabilidad" local del jugador (o la cámara). Si lo piensas, en muchos casos, no es necesario realizar una gran cantidad de simulaciones en objetos que no tienen relación con la jugabilidad.

El área de jugabilidad no se limita solo a los objetos que puedes ver frente a ti. ¡Los monstruos de IA que están detrás tuyo aún necesitan atacarte cuando les das la espalda! En Godot, el área de jugabilidad se define como el conjunto potencialmente visible (PVS) de salas desde la sala en la que te encuentras actualmente. Es decir, si hay alguna parte de una sala que pueda ser vista desde cualquier parte de la sala en la que te encuentras (incluso desde una esquina), se considera dentro del PVS y, por lo tanto, dentro del área de jugabilidad.

Esto funciona porque si un monstruo se encuentra en un área que está completamente fuera de tu vista o de la vista del propio monstruo, es menos probable que te importe lo que esté haciendo.

¿Cómo sabe un monstruo si está dentro del área de jugabilidad?

Este problema se resuelve porque el sistema de portales contiene un subsistema llamado el Monitor de Jugabilidad que se puede activar y desactivar desde el RoomManager. Cuando se activa, cualquier objeto en movimiento (roaming) que se mueva dentro o fuera del área de jugabilidad (ya sea que se mueva por sí mismo o que la cámara se mueva) recibirá llamadas de retorno para informarles de este cambio.

Puedes elegir recibir estas llamadas de retorno como señales o como notificaciones.

Las notificaciones se pueden manejar en GDScript o en otros lenguajes de script:

func _notification(what):
        match what:
                NOTIFICATION_ENTER_GAMEPLAY:
                        print("notification enter gameplay")
                NOTIFICATION_EXIT_GAMEPLAY:
                        print("notification exit gameplay")

Las señales se envían de la misma manera que cualquier otra señal. Se pueden adjuntar a funciones utilizando el inspector del editor. Las señales se llaman gameplay_entered (ingreso a la jugabilidad) y gameplay_exited (salida de la jugabilidad).

De hecho, no solo recibes estas llamadas de retorno para objetos "ROAMING". Además, las salas (Rooms) y los grupos de salas (RoomGroups), que se pueden utilizar para formar grupos de salas, también pueden recibir estas llamadas. Por ejemplo, puedes utilizar esto para activar el comportamiento de la IA cuando el jugador alcanza ciertos puntos en un nivel.

VisbilityNotifiers / VisibilityEnablers

Las llamadas de jugabilidad tienen una función adicional muy útil. De forma predeterminada en Godot, las animaciones y la física se procesan independientemente de si un objeto está dentro de la vista. Esto puede afectar el rendimiento, especialmente al utilizar deformaciones de software.

La solución del motor a este problema es el nodo VisibilityNotifier y su variante un poco más fácil de usar, el nodo VisibilityEnabler. VisibilityEnabler se puede utilizar para desactivar la animación y poner en pausa la física cuando un objeto está fuera del frustum de vista. Para hacer esto, simplemente coloca un nodo VisibilityEnabler en tu subescena (por ejemplo, un monstruo) y él se encargará del resto. Consulta la documentación de VisibilityEnabler para obtener todos los detalles.

../../../_images/visibility_enabler.png

¿Y si el VisibilityEnabler pudiera desactivar los objetos cuando fueran ocultados por la culling? Bueno, resulta que el VisibilityEnabler puede hacerlo. Todo lo que tienes que hacer es activar el Monitor de Jugabilidad en el RoomManager y el resto sucederá automáticamente.

RoomGroups (grupos de salas)

Un RoomGroup es un nodo especial que te permite manejar un grupo de salas a la vez, en lugar de tener que escribir código para cada una de ellas individualmente. Esto es especialmente útil en conjunto con las llamadas de jugabilidad. El uso más importante de los RoomGroups es delinear entre áreas "interiores" y "exteriores".

../../../_images/roomgroups.png

Por ejemplo, cuando estás en exteriores, es posible que desees utilizar una DirectionalLight para representar el sol. Cuando el RoomGroup de exteriores recibe una llamada de retorno de ingresar a la jugabilidad, puedes encender la luz, y puedes apagarla cuando el RoomGroup salga de la jugabilidad. Con la luz apagada, el rendimiento aumentará ya que no es necesario renderizarla en interiores.

Este es un ejemplo de un script simple para un RoomGroup que enciende y apaga una DirectionalLight. Ten en cuenta que también puedes utilizar señales para las llamadas de retorno (la elección es tuya):

../../../_images/roomgroup_notification.png

Truco

Puedes aplicar la misma técnica para activar y desactivar efectos climáticos, skyboxes y mucho más.

Salas Internas

Hay un truco más que los Grupos de salas tienen bajo la manga. Un deseo muy común es tener un nivel de juego con un entorno mixto, tanto al aire libre como en interiores. Ya hemos mencionado que las salas pueden ser utilizadas para representar tanto salas dentro de un edificio como áreas de paisaje, como un cañón.

¿Qué sucede si deseas tener una casa en una 'habitación' de terreno?

Con la funcionalidad descrita hasta ahora, es posible hacerlo: necesitarías colocar portales alrededor del exterior de la casa, formando salas innecesarias sobre la casa. Esto se ha hecho en muchos juegos. Pero, ¿qué pasaría si hubiera una manera más sencilla?

Resulta que hay una manera más sencilla de manejar este escenario. Godot admite salas dentro de salas (las llamaremos "salas internas"). Es decir, puedes colocar una casa dentro de una sala de terreno, o incluso un edificio o conjunto de edificios, ¡e incluso tener portales de salida en diferentes salas de terreno!

Para crear salas internas, no es necesario colocar una sala dentro de otra en el árbol de escenas; de hecho, recibirás una advertencia si intentas hacerlo. En su lugar, créalas como salas regulares. Las salas internas deben agruparse junto con un RoomGroup como padre. Si miras en el inspector del RoomGroup, hay una Prioridad del Grupo de salas que tiene el valor predeterminado de 0.

Si deseas que una sala o conjunto de salas sea interna, establece la prioridad con un valor más alto que la sala exterior (envolvente), utilizando RoomGroup.

El sistema utiliza la configuración de prioridad para otorgar prioridad a la sala interna al decidir en qué sala se encuentra una cámara u objeto. Una prioridad más alta siempre gana. Todo lo demás funciona de manera similar en su mayoría.

Las únicas diferencias son:

  • Los portales entre salas internas y salas exteriores siempre deben colocarse en la habitación interna.

  • Los portales de las salas internas no se consideran como parte del límite de las salas externas.

  • Los objetos "STATIC" y "DYNAMIC" de las salas externas no se extenderán hacia las salas internas. Si deseas que los objetos crucen estos portales, colócalos en la sala interna. Esto es para evitar que objetos grandes como secciones de terreno se extiendan hacia edificios completos y se rendericen cuando no es necesario.

Ejemplo de habitación interior

La tienda de campaña es una sala sencilla dentro de una sala de terreno (que contiene el suelo, los árboles, etc.).

../../../_images/tent.png

Nota

Para utilizar habitaciones internas para edificios, generalmente es una buena idea separar la malla interior del edificio de la malla exterior. El exterior puede colocarse en la habitación externa (para que sea visible desde el exterior, pero no desde el interior), y el interior debe colocarse en la habitación interna (para que solo sea visible desde el interior o a través del portal).

../../../_images/tent_terrain.png

Esto es perfecto para mejorar el rendimiento en juegos de mundo abierto. A menudo, los edificios pueden ser escenas (que incluyen las salas y portales) que se pueden reutilizar. Cuando se ven desde el exterior, los interiores en su mayoría se omiten, y cuando se ven desde el interior, se omiten otros edificios y la mayor parte del exterior. Lo mismo ocurre con otros jugadores y objetos que están dentro y fuera de los edificios.

La escena es "Diorama Eco Scene" de Odo, con pequeños cambios con fines ilustrativos. CC Attribution <https://creativecommons.org/licenses/by/4.0/>

Escena de salas internas

Permítenos examinar en detalle otro ejemplo práctico para un mundo abierto. Queremos colocar casas (como salas internas) en una isla, pero cada casa debe ser una escena independiente que contenga tanto el interior como la malla externa de la casa.

../../../_images/house_scene.png

Hemos creado un nodo de sala (que se convertirá en la sala interna) en el cual hemos colocado las mallas del interior. También hemos creado un portal sin enlaces (por lo que se utilizará el enlace automático). La malla exterior no se encuentra dentro de la sala. Será colocada automáticamente y pretendemos que se coloque dentro de la sala externa.

Sin embargo, hay un problema. El algoritmo de auto posicionamiento ingenuo buscará el centro de la malla exterior e intentará colocarla dentro de la sala interna. Queremos evitar esto de alguna manera, ya que la idea de la malla exterior es que se renderice desde el exterior, por lo que debe estar en la sala externa para que todo funcione correctamente.

Para solucionar este problema, existe una configuración especial que te permite expresar una preferencia por el auto posicionamiento en una sala externa. Cada objeto tiene una configuración de Prioridad de Auto posicionamiento. Cuando se establece en "0", no hay preferencia (el objeto se colocará en la sala de mayor prioridad).

Sin embargo, si establecemos esta prioridad de auto posicionamiento en "-1", por ejemplo, el auto posicionamiento siempre elegirá una sala con prioridad "-1" (si hay una presente en esa ubicación). Entonces, si establecemos la prioridad de la sala externa en "-1", siempre colocará nuestro exterior en nuestra sala "exterior".

../../../_images/autoplace_priority.png

Esto nos brinda un útil nivel adicional de control para este tipo de situaciones y hace que todo el sistema sea mucho más flexible.

Nota

Como la prioridad de autoposicionamiento predeterminada es "0", no puedes forzar efectivamente los objetos en RoomGroups con prioridad "0". Sin embargo, hay muchas opciones de valores de prioridad disponibles, por lo que esto no debería ser un problema en la práctica.

La escena final se ve algo así, con casas instanciadas donde quieras en una gran sala externa.

../../../_images/island.png

Los exteriores de las casas se colocarán en la sala externa y, por lo tanto, siempre podrán verse desde el exterior. Los interiores solo se renderizarán cuando haya una vista a través de los portales de entrada visibles.