En esta sección, se mostrará cómo establecer y configurar equipos y clases para jugadores.
Dispositivos utilizados:
Ajustes de equipo e inventario
Utiliza dispositivos de ajustes de equipo e inventario para establecer los nombres y los colores de los equipos para la visualización del marcador.
Ubica un dispositivo para cada equipo en un área invisible para los jugadores. Para configurar el equipo de utilería, establece las Opciones de usuario para que coincidan con la tabla que aparece a continuación.
| Opción | Valor | Explicación |
|---|---|---|
| Nombre del equipo | Utilería | Establece un texto que será usado para identificar al equipo en el marcador y elementos del HUD. |
| Color del equipo | Azul cielo | Asigna un color al equipo seleccionado que se usa en el marcador, el HUD y en ciertos dispositivos. |
| Equipo | Índice de equipo: 1 | Especifica a qué equipo se aplican los ajustes de este dispositivo. |
Para configurar el equipo de caza, establece las Opciones de usuario de otros dispositivos para que coincidan con la tabla que aparece a continuación.
| Opción | Valor | Explicación |
|---|---|---|
| Nombre del equipo | Cazadores | Establece un texto que será usado para identificar al equipo en el marcador y elementos del HUD. |
| Color del equipo | Naranja | Asigna un color al equipo seleccionado que se usa en el marcador, el HUD y en ciertos dispositivos. |
| Equipo | Índice de equipo: 2 | Especifica a qué equipo se aplican los ajustes de este dispositivo. |
Diseñador de clase
Utiliza un diseñador de clase para modificar los equipos que creaste recién.
Ubica dos dispositivos Diseñador de clase, uno para cada equipo, en un área invisible para los jugadores. Para configurar el equipo de utilería, establece las Opciones de usuario para que coincidan con la tabla que aparece a continuación.
| Opción | Valor | Explicación |
|---|---|---|
| Nombre de clase | Utilería | Determina el nombre de esta clase. |
| Descripción de clase | Escóndete de los cazadores. Sobrevive. | Define la descripción de esta clase. |
| Identificador de clase | Ranura de clase: 1 | Define el identificador único para esta clase. |
| Vida máx. | 1 | Determina el valor máximo de vida que los jugadores pueden alcanzar durante la partida. La utilería se eliminará con un golpe. |
| Lista de elementos | Objetomático | Establece la lista de elementos que tendrá esta clase. |
| Equipar objeto otorgado | Primer objeto | Determina qué elemento de la lista se equipará. |
Para personalizar el equipo de caza, configura las Opciones de usuario del otro dispositivo para que coincidan con las de la tabla que aparece a continuación.
| Opción | Valor | Explicación |
|---|---|---|
| Nombre de clase | Cazador | Determina el nombre de esta clase. |
| Descripción de clase | Encuentra utilería. Elimínalos. | Define la descripción de esta clase. |
| Identificador de clase | Ranura de clase: 2 | Define el identificador único para esta clase. |
| Lista de elementos | Pistola linterna | Establece la lista de elementos que tendrá esta clase. |
| Equipar objeto otorgado | Primer objeto | Determina qué elemento de la lista se equipará. |
Selector de clase
Empareja el Diseñador de clase con el Selector de clase para administrar los equipos y las clases personalizadas que creaste.
Junto con Verse, los ajustes de este dispositivo hacen que los jugadores en la Ranura de Clase 1 se transfieran a la Ranura de clase 2 cuando reaparecen.
Coloca los dos seleccionadores de clase, uno para cada equipo, en un área invisible para los jugadores. Para administrar el equipo de utilería, utiliza los ajustes de la tabla que aparecen a continuación para configurar las Opciones de usuario para este dispositivo.
| Opción | Valor | Explicación |
|---|---|---|
| Clase a la que cambiar | Ranura de clase: 1 | Determina a qué clase debe cambiar el jugador. |
| Visible durante la partida | Falso | Este dispositivo no será visible durante el juego. |
| Sonido de zona | Falso | Determina si el selector de clase debería reproducir efectos de sonido cuando los jugadores entran en la zona. |
| Equipo al que cambiar | Índice de equipo: 1 | Determina a qué equipo se cambiará el jugador. |
| Descartar objetos al cambiar | Verdadero | Determina si los objetos se eliminarán del inventario del jugador cuando se aplique el cambio. |
| Volumen visible en la partida | Falso | Determina si el volumen del dispositivo será visible durante la partida. |
| Mostrar efectos visuales al activar | Falso | Determina si el dispositivo debe crear un efecto especial al cambiar la clase o el equipo de un jugador. |
Para administrar el equipo de caza, utiliza los ajustes de la tabla que aparecen a continuación para configurar las Opciones de usuario para este dispositivo.
| Opción | Valor | Explicación |
|---|---|---|
| Clase a la que cambiar | Ranura de clase: 2 | Determina a qué clase debe cambiar el jugador. |
| Equipo al que cambiar | Índice de equipo: 2 | Determina a qué equipo se cambiará el jugador. |
Cómo crear una funcionalidad de equipo con Verse
Hay dos equipos en este juego de caza de objetos: cazadores y objetos. Necesitas poder hacer algunas de las mismas cosas para ambos equipos para que el juego funcione. Por ejemplo:
-
añadir jugadores a un equipo;
-
eliminar jugadores de un equipo;
-
mostrar información a los jugadores acerca de su equipo.
A fin de crear esta funcionalidad para ambos equipos sin duplicar código, vas a crear una clase con el especificador <abstract>. Las clases con el especificador abstract están destinadas a tener una funcionalidad parcial que sus subclases heredan y construyen. Primero, crearás una clase abstracta llamada base_team y le otorgarás la funcionalidad que compartirán los equipos utilería y cazador.
En este documento, se incluyen fragmentos de Verse que muestran cómo ejecutar las mecánicas del juego que son necesarias en este juego. Sigue los pasos que aparecen a continuación y copia la secuencia completa de comandos en el paso 6 de este tutorial.
Crea un archivo de Verse nuevo en tu proyecto llamado base_team.verse. Este no será un dispositivo de Verse, así que puedes crearlo como un archivo vacío de Verse.
using { /Fortnite.com/Characters }
using { /Fortnite.com/Devices }
using { /Fortnite.com/UI }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /UnrealEngine.com/Temporary/UI }
using { /Verse.org/Colors }
using { /Verse.org/Simulation }
log_team := class(log_channel){}
# Esta clase define los dispositivos necesarios para los distintos equipos en esta experiencia.
# Esta clase es abstracta, así que no puede utilizarse por sí sola. Tiene que heredarse de otra clase.
base_team := class<abstract>:
Logger:log = log{Channel:=log_team}
@editable # Usado para establecer un jugador para el equipo.
ClassSelector:class_and_team_selector_device = class_and_team_selector_device{}
@editable # Usado para otorgar puntaje a los agentes en el equipo.
ScoreManager:score_manager_device = score_manager_device{}
@editable # Usado para mostrar el título de asignación del equipo.
TeamTitle:hud_message_device = hud_message_device{}
@editable # Usado para mostrar la descripción de asignación del equipo.
TeamDescription:hud_message_device = hud_message_device{}
@editable # Usado para suscribirse a los eventos eliminados del equipo miembro (equipo utilería) o enemigo (equipo cazador).
TeamManager:team_settings_and_inventory_device = team_settings_and_inventory_device{}
# Esta es una matriz de agentes en el equipo.
var TeamAgents<private>:[]agent = array{}
# Este evento se señala cuando la matriz TeamAgents se vacía (lo que marca el fin de la ronda).
TeamEmptyEvent:event() = event(){}
# Devuelve la matriz TeamAgents actual.
# Esto es obligatorio porque la matriz TeamAgents es privada, por eso las otras clases no pueden acceder a ella directamente.
GetAgents()<decides><transacts>:[]agent =
TeamAgents
# Devuelve el tamaño de la matriz TeamAgents
# Esto requiere una función porque la matriz TeamAgents es privada, por eso las otras clases no pueden acceder a ella directamente.
Count()<transacts>:int =
TeamAgents.Length
# Devuelve un índice en la matriz TeamAgents de un agente; en caso contrario, falla.
FindOnTeam(Agent:agent)<decides><transacts>: int =
Index := TeamAgents.Find[Agent]
# Establece el agente para el equipo y notifica al jugador.
InitializeAgent(Agent:agent):void =
AddAgentToTeam(Agent)
ClassSelector.ChangeTeamAndClass(Agent)
DisplayTeamInformation(Agent)
# Añade un agente al TeamAgents.
AddAgentToTeam(AgentToAdd:agent):void =
if (not FindOnTeam[AgentToAdd]):
Logger.Print("Se está añadiendo al agente al equipo.")
set TeamAgents += array{AgentToAdd}
# Activa los dispositivos de mensaje del HUD para mostrarle al jugador en qué equipo está
DisplayTeamInformation(Agent:agent):void =
TeamTitle.Show(Agent)
TeamDescription.Show(Agent)
# Cuando un agente deja una partida, elimínalo de la matriz TeamAgents y corrobora el fin de la ronda.
EliminateAgent(Agent:agent)<suspends>:void =
Sleep(0.0) # Retraso 1 tic de juego para asegurarte de que el jugador reaparezca antes de continuar.
RemoveAgentFromTeam(Agent)
# Elimina un agente de TeamAgents.
# Si el agente eliminado fue el último, marca TeamEmptyEvent.
RemoveAgentFromTeam(AgentToRemove:agent):void =
set TeamAgents = TeamAgents.RemoveAllElements(AgentToRemove)
Logger.Print("Agentes restantes en el equipo: {Count()}.")
if (Count() < 1):
Logger.Print("No quedan agentes en el equipo. Fin de la ronda.")
TeamEmptyEvent.Signal()
Ahora que tienes esta clase, puedes crear las clases para el equipo utilería y el equipo cazador. Debido a que cada uno de estos se heredará del base_team, hay algunas ventajas:
-
El código para implementar cada equipo es mucho más corto porque ya tienen su información y sus funciones comunes definidas en
base_team. -
Es más fácil entender qué código es específico para los equipos cazador y utilería porque están en sus propias clases, en lugar de estar mezclados con el código común.
-
Agregar más equipos al modo de juego es mucho más fácil. Cualquier equipo nuevo se hereda del
base_teamy el código que hace al equipo nuevo diferente está en su propia clase.
Recuerda que no se puede crear una instancia de una clase con el especificador <abstract>. Debes crear una clase que herede de la clase abstracta e instanciar esa clase.
Equipo cazador
Primero, crea la clase para el equipo cazador. Crea un archivo de Verse nuevo en tu proyecto llamado hunter_team.verse. Este no será un dispositivo de Verse, así que puedes crearlo como un archivo vacío de Verse.
Declara una clase denominada hunter_team. Debe ser <concrete> y también heredar de base_team.
hunter_team := class<concrete>(base_team):
Hacer una clase <concrete> significa que todos los campos de la clase deben tener un valor predeterminado. Consulta Especificadores y atributos para obtener más información.
Debajo se encuentra el código completo para la secuencia de comandos hunter_team.verse.
La clase hunter_team tiene dos funciones con el mismo nombre que las funciones de la clase base_team. Esto se permite porque ambos tienen el especificador <override>. Esto significa que cuando se llama a estas funciones en una instancia de hunter_team, se utiliza la versión en la clase hunter_team.
Por ejemplo, en el siguiente código, se utilizará la versión de InitializeAgent() definida en el hunter_team porque anula la función del mismo nombre en el base_team. Compara eso con la llamada a Count(), que utilizará la versión definida en base_team porque no hay una función de anulación.
HunterTeam:hunter_team = hunter_team{}
# Utiliza las funciones de hunter_team
HunterTeam.InitializeAgent(StartingHunterAgent)
# Utiliza las funciones de base_team
HunterTeam.Count()
Las dos funciones anuladas también usan (super:). Esto les permite llamar a las versiones de las funciones definidas en base_team porque base_team es la súper clase de hunter_team. En el caso de InitializeAgent() y EliminateAgent(), ambos utilizan Logger.Print() para imprimir algo en el registro. Luego, llaman a sus respectivas funciones del base_team. Esto significa que las funciones trabajan de la misma manera que las versiones en el base_team, excepto las llamadas a Logger.Print().
Consulta Subclase para obtener más información sobre <override> y (super:).
Equipo utilería
Ahora crea la clase para el equipo utilería. Crea un archivo de Verse nuevo en tu proyecto llamado prop_team.verse. Este no será un dispositivo de Verse, así que puedes crearlo como un archivo vacío de Verse.
Tienes que administrar más para miembros del equipo utilería. Tienen efectos de latido de corazón que deben iniciarse y pararse con base en un temporizador y a qué tan lejos se mueven. También deben moverse al equipo cazador cuando son eliminados.
Para administrar a los miembros del equipo utilería, utilizarás el método RunPropGameLoop(). Puedes pensar este método como el administrador para la travesía completa de utilería a través del juego. Desde el momento en el que aparecen hasta el momento en el que son eliminados o que abandonan el juego, este método estará en ejecución para cada miembro del equipo utilería.
# Si el agente de utilería se deja de mover, entonces corre a ver si el agente de utilería se mueve más allá de MinimumMoveDistance, el cronómetro de latido de corazón se completa o el agente de utilería se elimina.
RunPropGameLoop(PropAgent:agent)<suspends>:void =
Logger.Print("Se está iniciando el bucle de juego de agente de utilería.")
# Muévete en bucle a través del comportamiento de utilería hasta que el agente de utilería se elimine o hasta que el jugador abandone la sesión.
race:
PropAgent.AwaitNoLongerAProp()
loop:
# Espera hasta que el agente de utilería se mueva menos que la distancia mínima; luego, avanza.
PropAgent.AwaitStopMoving(MinimumMoveDistance)
# Hasta que el agente de utilería se mueva más allá que la distancia mínima, haz la cuenta regresiva del latido de corazón y, luego, reprodúcelo indefinidamente.
race:
PropAgent.AwaitStartMoving(MinimumMoveDistance)
block:
CountdownTimer(PropAgent)
PropAgent.StartHeartbeat()
Sleep(0.0) # Una vez que la carrera se completa (el agente de utilería se mueve), inicia el bucle nuevamente.
RunPropGameLoop() tiene un parámetro, PropAgent. Esta es una constante que representa a un jugador en el equipo de utilería. También tiene el especificador <suspends>, lo que significa que tarda en finalizar. En este caso, no terminará hasta que el PropAgent que se pasa ya no esté en el equipo de utilería.
Toda la funcionalidad de este método se contiene dentro de una expresión de carrera. Esto significa que el método no estará completo hasta que una de las expresiones dentro de esta carrera esté completa. Esas expresiones son las siguientes:
-
PropAgent.AwaitNoLongerAProp() -
loop
La expresión bucle dentro de esta carrera no terminará nunca. Es infinito a propósito. Esto significa que AwaitNoLongerAProp() es el método que siempre ganará la carrera y completará el método. Utilizar la carrera de esta manera es como decirle a tu programa que ejecute un determinado conjunto de códigos una y otra vez hasta que algo suceda. Consulta Race para obtener más información sobre esta expresión poderosa.
Con este código, AwaitNoLongerAProp() gana la carrera.
# Haz un bucle hasta que el agente de utilería no forme parte de la matriz PropAgents. La extracción sucede si el agente de utilería se elimina y se convierte en un cazador o si el jugador abandona la sesión.
(PropAgent:agent).AwaitNoLongerAProp()<suspends>:void =
loop:
if (not FindOnTeam[AgentToAdd]):
Logger.Print("Se cancela el comportamiento del agente de utilería.")
break
Sleep(0.0) # Avanza al siguiente tic de juego.
Este método constantemente verifica si el PropAgent está en el equipo de utilería. Comienza con un bucle que se ejecuta hasta que not FindOnTeam[PropAgent] tiene éxito y luego se rompe, lo que completa el método. Consulta Bucle e interrupción para obtener más información sobre el tema.
FindOnTeam[] es una función con falla que se declara en base_team. Tiene éxito si se encuentra el PropAgent en el equipo de utilería. Pero necesitas usar el operador not porque quieres salir del bucle solo cuando el PropAgent no se encuentra en el equipo de utilería. Consulta Operadores para obtener más información sobre not.
Por último, es necesario añadir un Sleep(0.0) al final del loop. Esto garantiza que el loop se ejecute una vez y luego avance a la siguiente actualización de simulación. No necesitas ejecutar esta comprobación con más frecuencia, por lo que se añade Sleep(0.0) para mejorar el rendimiento. Consulta la Referencia de la API de Verse de Dormir a fin de obtener más información.
Ahora que sabes cómo funciona AwaitNoLongerAProp(), puedes escribir el bucle infinito que corre con él en RunPropGameLoop().