Atrás
Cómo mejoramos la plantilla de carrera de circuito en Fortnite con persistencia de Verse y más
El equipo de Fortnite
La llegada de la persistencia de Verse brindó una oportunidad para mejorar la plantilla ''Diseña una carrera de circuito'' del modo Creativo de Fortnite en UEFN. Desde actualizaciones como agregar cinemáticas y creación de terrenos hasta cómo utilizar menos los activadores y más Verse, nos emociona contarte cómo fue el desarrollo de esta plantilla. Te damos la bienvenida a la carrera de circuito con persistencia de Verse.
La plantilla ''Diseña una carrera de circuito'' se había creado originalmente para usar los recursos de pista de carreras y crear una experiencia de competencia motorizada única con algunas funciones de comodidad convenientes.
En esta última actualización, optamos por un enfoque integral, y reemplazamos y mejoramos muchas de las características del mapa. Echemos un vistazo a las actualizaciones que llevamos a cabo para aprovechar al máximo la capacidad de UEFN.
El ciclo de día y noche del proyecto original se reemplazó con la iluminación más avanzada del Capítulo 4 de Batalla campal de Fortnite. Este nuevo ciclo nos permitió usar Lumen, crear sombras más suaves y una iluminación global realista. También agregamos detalles como una cascada e hicimos la pista más interesante en cuestión del aspecto visual.
La solución fue la persistencia de Verse, que permitió registrar información entre sesiones de juego. Esta funcionalidad nos permitió monitorear las estadísticas totales del jugador y crear tablas de posiciones locales.
Nuestro objetivo era actualizar el mejor tiempo por vuelta de un jugador al terminar una vuelta, así como registrar sus puntos y victorias al terminar una carrera.
La actualización de tiempos por vuelta fue algo directo, ya que podíamos esperar a que ''LapCompletedEvent'' del dispositivo de gestión de carreras detectara el momento en que un jugador terminaba una vuelta. Al inicio de cada carrera, se activa un dispositivo de cronómetro por jugador. La función ''WaitForPlayerToFinishLap'' espera a que el jugador termine una vuelta, calcula el tiempo que le tomó, actualiza la tabla de estadísticas y reinicia el cronómetro para registrar la siguiente vuelta.
Para resolver este problema, usamos la función ''ArraySync''. ''ArraySync'' llama a una función asíncrona para cada elemento en la matriz asignada y espera a que todas las funciones se completen. Al pasar una matriz de jugadores y la función ''WaitForPlayerToFinishRace'', es posible llamar a la función para cada uno y esperar hasta que haya terminado para todos.
Esto permite asignar puntos y la victoria a los jugadores con base en su posición al terminar la carrera y después actualizar las estadísticas correspondientes en la tabla. Al término de ''ArraySync'', sabemos que todos los jugadores ya finalizaron la carrera y es posible terminar el juego.
Como ya existían estadísticas persistentes para cada jugador, podíamos recuperarlas y mostrarlas en carteles. En la zona de espera previa a la partida, agregamos dispositivos de referencia individuales de cartel y jugador, de modo que mostraran tanto las estadísticas como los atuendos usados. No obstante, la clasificación con base en el desempeño era un desafío.
Para lograrlo, implementamos el algoritmo ''Merge Sort''. ''Merge Sort'' es un algoritmo de división común que separa una matriz en dos, clasifica cada submatriz y las une de vuelta de forma recursiva. También agregamos la capacidad de incluir una función de comparación al algoritmo, además de que creamos funciones de comparación para cada una de las estadísticas de jugador.
Cuando necesitábamos clasificar a los jugadores, podíamos recuperar las estadísticas de cada uno desde su tabla de estadísticas, agregarlas a la matriz e incluirlas junto con una función de comparación al algoritmo ''Merge Sort''. Al seleccionar la función de comparación incluida, podíamos recuperar la matriz de jugadores clasificada de acuerdo a cualquier estadística. Tanto el algoritmo ''Merge Sort'' como un archivo de prueba se encuentran incluidos en la nueva plantilla, de modo que puedas poner a prueba el algoritmo y ajustarlo a tus propias funciones.
Con los mecanismos de clasificación en su lugar, terminamos las tablas de posiciones. En la primera ronda de la partida, los jugadores aparecen en una zona de espera previa con sus referencias y carteles. Clasificamos estas referencias de acuerdo con los puntos totales de cada jugador y mostramos cada una de sus estadísticas en el cartel frente a ellos. Esto permite que los jugadores puedan echar un vistazo a la competencia antes de la carrera y sepan de quién deben cuidarse si quieren ganar.
¡Sigue jugando y quizá llegues a la cima de la tabla de posiciones!

Para lograrlo, creamos una segunda variable de mapa débil de jugador llamada ''CircuitInfo''. Esta variable almacena toda la información que necesitábamos restablecer al término de la partida o cuando un jugador sale de ella.
Usamos una segunda variable de mapa débil para precisar qué información se debe restablecer (como los datos del circuito) y cuál debe persistir (como las estadísticas de los jugadores).
La siguiente información se registra en la variable de mapa débil ''CircuitInfo'' de cada jugador:
En la función ''OnBegin'' del dispositivo de Verse (que corre al inicio de cada ronda), sabemos en qué ronda estamos al iterar por todos los jugadores activos y obtener el valor más alto de la última ronda completada desde la información persistente. Esto solo se hace una vez por ronda y la información se registra en una variable de mapa débil de sesión, de modo que todo el código de Verse del proyecto pueda acceder a este valor en cualquier momento sin necesidad de volver a calcularlo.
Cuando un jugador termina la carrera, lo que se detecta al esperar ''RaceCompletedEvent'' del dispositivo de gestión de carreras, se registra el orden de finalización y la ronda actual en la variable de mapa débil ''CircuitInfo'' asociada con el jugador. Esta información se restablece bajo dos condiciones:
En la plantilla actualizada, cambiamos el activador de pulso por Sequencer para lograr la cinemática de apertura. Con Sequencer, logramos agregar diferentes cámaras, elementos de una pantalla de visualización frontal (HUD) y una vista de alineación dinámica que se ajusta con base en el número de jugadores activos.
Conectamos los dispositivos a la secuencia en momentos importantes, de forma similar a cómo se configuró el activador de pulso, lo que nos permitió determinar cuándo mostrar la puntuación del siguiente jugador o cuándo cortar la introducción.

Esperamos poder actualizar esta plantilla conforme aparezcan nuevas funciones que mejoren su diseño. Mientras tanto, descarga la plantilla, explora sus componentes e integra su funcionalidad a tus proyectos desde la pestaña de plantillas de UEFN. ¡Tenemos muchas ganas de ver tus carreras, tablas de posiciones y más!
La plantilla ''Diseña una carrera de circuito'' se había creado originalmente para usar los recursos de pista de carreras y crear una experiencia de competencia motorizada única con algunas funciones de comodidad convenientes.
En esta última actualización, optamos por un enfoque integral, y reemplazamos y mejoramos muchas de las características del mapa. Echemos un vistazo a las actualizaciones que llevamos a cabo para aprovechar al máximo la capacidad de UEFN.
Diseño visual
El modo terreno de UEFN nos permitió volver a las raíces de las carreras y crear una pista todoterreno con las nuevas herramientas de edición de terrenos. Las montañas creadas con recursos de rocas en el fondo de la isla original se cambiaron por un diseño de terreno más natural.El ciclo de día y noche del proyecto original se reemplazó con la iluminación más avanzada del Capítulo 4 de Batalla campal de Fortnite. Este nuevo ciclo nos permitió usar Lumen, crear sombras más suaves y una iluminación global realista. También agregamos detalles como una cascada e hicimos la pista más interesante en cuestión del aspecto visual.
Persistencia de Verse: hacia una mejor tabla de posiciones
En el mapa original, una torre usaba el dispositivo de referencia del jugador para mostrar a la persona que estuviera en primer lugar y cuántos puntos tenía. Sin embargo, la información no persistía entre sesiones.La solución fue la persistencia de Verse, que permitió registrar información entre sesiones de juego. Esta funcionalidad nos permitió monitorear las estadísticas totales del jugador y crear tablas de posiciones locales.
Registro de estadísticas del jugador
Desarrollamos una clase de tabla de estadísticas del jugador persistente que registra las victorias totales, el mejor tiempo por vuelta y los puntos obtenidos por terminar una carrera. También usamos un mapa débil persistente llamado ''PlayerStatsMap'' para conectar a los jugadores con sus tablas de estadísticas, lo que permite que persistan a través de diferentes rondas y sesiones. Luego creamos una clase de gestión de estadísticas para iniciar, recuperar y actualizar estas estadísticas para cada jugador.Nuestro objetivo era actualizar el mejor tiempo por vuelta de un jugador al terminar una vuelta, así como registrar sus puntos y victorias al terminar una carrera.
La actualización de tiempos por vuelta fue algo directo, ya que podíamos esperar a que ''LapCompletedEvent'' del dispositivo de gestión de carreras detectara el momento en que un jugador terminaba una vuelta. Al inicio de cada carrera, se activa un dispositivo de cronómetro por jugador. La función ''WaitForPlayerToFinishLap'' espera a que el jugador termine una vuelta, calcula el tiempo que le tomó, actualiza la tabla de estadísticas y reinicia el cronómetro para registrar la siguiente vuelta.
Registro de victorias y puntos
Llevar un conteo de victorias y puntos resultó ser algo más complejo. Sabíamos que podíamos usar la función similar ''WaitForPlayerToFinishRace'' para esperar a ''RaceCompletedEvent'' del dispositivo de gestión de carreras para conocer el momento en que un jugador terminara. Sin embargo, queríamos que la partida terminara cuando todos los jugadores lo hicieran, pero el dispositivo de gestión de carreras no tenía forma de registrar esto o de terminar el juego.Para resolver este problema, usamos la función ''ArraySync''. ''ArraySync'' llama a una función asíncrona para cada elemento en la matriz asignada y espera a que todas las funciones se completen. Al pasar una matriz de jugadores y la función ''WaitForPlayerToFinishRace'', es posible llamar a la función para cada uno y esperar hasta que haya terminado para todos.
Esto permite asignar puntos y la victoria a los jugadores con base en su posición al terminar la carrera y después actualizar las estadísticas correspondientes en la tabla. Al término de ''ArraySync'', sabemos que todos los jugadores ya finalizaron la carrera y es posible terminar el juego.
Muestra de resultados
Registrar las estadísticas es una cosa, pero también necesitábamos una forma de mostrárselas a los jugadores. Queríamos crear tablas de posiciones que fueran visibles y clasificaran a los jugadores de acuerdo con sus puntos totales para destacar a los mejores.Como ya existían estadísticas persistentes para cada jugador, podíamos recuperarlas y mostrarlas en carteles. En la zona de espera previa a la partida, agregamos dispositivos de referencia individuales de cartel y jugador, de modo que mostraran tanto las estadísticas como los atuendos usados. No obstante, la clasificación con base en el desempeño era un desafío.
Para lograrlo, implementamos el algoritmo ''Merge Sort''. ''Merge Sort'' es un algoritmo de división común que separa una matriz en dos, clasifica cada submatriz y las une de vuelta de forma recursiva. También agregamos la capacidad de incluir una función de comparación al algoritmo, además de que creamos funciones de comparación para cada una de las estadísticas de jugador.
Cuando necesitábamos clasificar a los jugadores, podíamos recuperar las estadísticas de cada uno desde su tabla de estadísticas, agregarlas a la matriz e incluirlas junto con una función de comparación al algoritmo ''Merge Sort''. Al seleccionar la función de comparación incluida, podíamos recuperar la matriz de jugadores clasificada de acuerdo a cualquier estadística. Tanto el algoritmo ''Merge Sort'' como un archivo de prueba se encuentran incluidos en la nueva plantilla, de modo que puedas poner a prueba el algoritmo y ajustarlo a tus propias funciones.
Con los mecanismos de clasificación en su lugar, terminamos las tablas de posiciones. En la primera ronda de la partida, los jugadores aparecen en una zona de espera previa con sus referencias y carteles. Clasificamos estas referencias de acuerdo con los puntos totales de cada jugador y mostramos cada una de sus estadísticas en el cartel frente a ellos. Esto permite que los jugadores puedan echar un vistazo a la competencia antes de la carrera y sepan de quién deben cuidarse si quieren ganar.
¡Sigue jugando y quizá llegues a la cima de la tabla de posiciones!

Orden de la carrera en la línea de salida
La versión del modo Creativo de Fortnite del mapa coloca a los jugadores en forma aleatoria al inicio de cada partida. Para motivarlos a que busquen quedar más alto en la tabla de posiciones, hicimos que el orden de la línea de salida dependiera de su lugar al terminar la ronda previa.Registro de información de rondas
A diferencia de los datos persistentes de la tabla de posiciones, necesitábamos que el orden de la carrera y la información del circuito persistieran en todas las rondas, pero no en todas las sesiones de juego. Una variable de mapa débil de sesión en Verse restablece los datos en cada ronda, por lo que debemos guardar la información de cada jugador y restablecerla al término de la partida.Para lograrlo, creamos una segunda variable de mapa débil de jugador llamada ''CircuitInfo''. Esta variable almacena toda la información que necesitábamos restablecer al término de la partida o cuando un jugador sale de ella.
Usamos una segunda variable de mapa débil para precisar qué información se debe restablecer (como los datos del circuito) y cuál debe persistir (como las estadísticas de los jugadores).
La siguiente información se registra en la variable de mapa débil ''CircuitInfo'' de cada jugador:
- Orden de la línea de meta
- Última ronda terminada
Lógica específica para cada ronda
Necesitábamos saber en qué ronda estábamos para aplicar una lógica específica para ella (como clasificar a los jugadores de acuerdo al orden en el que terminaron previamente si no es la primera ronda) y cuándo restablecer los datos de los jugadores. Actualmente no hay una API para identificar la ronda actual, por lo que tenemos que registrarla en los datos persistentes de cada jugador.En la función ''OnBegin'' del dispositivo de Verse (que corre al inicio de cada ronda), sabemos en qué ronda estamos al iterar por todos los jugadores activos y obtener el valor más alto de la última ronda completada desde la información persistente. Esto solo se hace una vez por ronda y la información se registra en una variable de mapa débil de sesión, de modo que todo el código de Verse del proyecto pueda acceder a este valor en cualquier momento sin necesidad de volver a calcularlo.
Cuando un jugador termina la carrera, lo que se detecta al esperar ''RaceCompletedEvent'' del dispositivo de gestión de carreras, se registra el orden de finalización y la ronda actual en la variable de mapa débil ''CircuitInfo'' asociada con el jugador. Esta información se restablece bajo dos condiciones:
- El jugador abandona la partida. Nos suscribimos a ''PlayerRemovedEvent'' de playspace para saber cuándo salen y llamar a la función ''ResetCircuitInfo''.
- Al inicio de cada ronda, contabilizamos la última ronda terminada. Si se trata de la ronda final, invocamos ''ResetCircuitInfo'' para restablecer los datos de los jugadores. Sabemos cuántas rondas hay al contar con una propiedad editable en el dispositivo de Verse que está configurada con el número de rondas totales de una partida. Esto se encuentra en el creador de islas para garantizar que coincida con la configuración de la ronda.
Cuándo usar mapas débiles de sesión o mapas débiles de jugador
La nueva plantilla de carrera de circuito es un gran ejemplo de las diferencias entre un mapa débil de sesión y un mapa débil de jugador y las razones para usarlos en tu código:- Las variables de mapa débil de sesión son útiles para instancias únicas y el almacenamiento de datos de la ronda actual que no deseas contabilizar una y otra vez.
- Las variables de mapa débil de jugador están diseñadas para la información que debe persistir en múltiples rondas y partidas, pero que debe asociarse a jugadores individuales.
De un activador de pulso a una secuencia de apertura
En la versión original de la plantilla de carrera de circuito, usamos un dispositivo conocido como activador de pulso para la parte de ''En sus marcas... listos... fuera''. El activador de pulso reproducía una secuencia de eventos durante un período de tiempo determinado al hacer que otros activadores mostraran texto y encendieran luces en la línea de salida.En la plantilla actualizada, cambiamos el activador de pulso por Sequencer para lograr la cinemática de apertura. Con Sequencer, logramos agregar diferentes cámaras, elementos de una pantalla de visualización frontal (HUD) y una vista de alineación dinámica que se ajusta con base en el número de jugadores activos.
Conectamos los dispositivos a la secuencia en momentos importantes, de forma similar a cómo se configuró el activador de pulso, lo que nos permitió determinar cuándo mostrar la puntuación del siguiente jugador o cuándo cortar la introducción.

¿Qué sigue?
Con el progreso de UEFN, esperamos que esta plantilla también evolucione. Nuestro objetivo es empoderarte para que sigas generando contenido más sofisticado con UEFN y aproveches las funciones más recientes para crear islas divertidas y cautivadoras.Esperamos poder actualizar esta plantilla conforme aparezcan nuevas funciones que mejoren su diseño. Mientras tanto, descarga la plantilla, explora sus componentes e integra su funcionalidad a tus proyectos desde la pestaña de plantillas de UEFN. ¡Tenemos muchas ganas de ver tus carreras, tablas de posiciones y más!