Esta sección te mostrará cómo establecer y personalizar equipos y clases para los jugadores.
Dispositivos utilizados:
-
Dos dispositivos Ajustes de equipo e inventario
-
Dos dispositivos Diseñador de clase
-
Dos dispositivos Selector de clase
Ajustes de equipo e inventario
Utiliza dispositivos Ajustes de equipo e inventario para establecer los nombres de los equipos y los colores de la pantalla de la tabla de puntuación.
Coloca un dispositivo para cada equipo en una zona oculta para los jugadores. Para configurar el equipo de elementos, configura las opciones de usuario como se indica en la siguiente tabla.
| Opción | Valor | Explicación |
|---|---|---|
| Nombre del equipo | Elementos | Fija una línea de texto que se usará para identificar al equipo en la tabla de puntuación y elementos HUD. |
| Color del equipo | Azul cielo | Asigna un color al equipo seleccionado que se usará en la tabla de puntuación, 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 cazadores, configura las opciones de usuario del otro dispositivo como se indica en la siguiente tabla.
| Opción | Valor | Explicación |
|---|---|---|
| Nombre del equipo | Cazadores | Fija una línea de texto que se usará para identificar al equipo en la tabla de puntuación y elementos HUD. |
| Color del equipo | Naranja | Asigna un color al equipo seleccionado que se usará en la tabla de puntuación, 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 acabas de crear.
Coloca dos dispositivos Diseñador de clase, uno para cada equipo, en una zona oculta para los jugadores. Para personalizar el equipo de elementos, configura las opciones de usuario como se indica en la siguiente tabla.
| Opción | Valor | Explicación |
|---|---|---|
| Nombre de la clase | Elemento | Establece 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 | Espacio de clase: 1 | Establece el identificador único de esta clase. |
| Salud máxima | 1 | Establece la salud máxima que pueden alcanzar los jugadores durante la partida. Los elementos se eliminarán con un solo golpe. |
| Lista de objetos | Elemen-tal | Establece la lista de objetos que tendrá esta clase. |
| Equipar el objeto otorgado | Primer objeto | Establece qué objeto de la lista se equipará. |
Para personalizar el equipo de cazadores, configura las opciones de usuario del otro dispositivo como se indica en la siguiente tabla.
| Opción | Valor | Explicación |
|---|---|---|
| Nombre de la clase | Cazador | Establece el nombre de esta clase. |
| Descripción de clase | Encuentra elementos Elimínalos. | Define la descripción de esta clase. |
| Identificador de clase | Espacio de clase: 2 | Establece el identificador único de esta clase. |
| Lista de objetos | Pistola linterna | Establece la lista de objetos que tendrá esta clase. |
| Equipar el objeto otorgado | Primer objeto | Establece qué objeto de la lista se equipará. |
Selector de clase
Empareja el diseñador de clases con el selector de clases para gestionar las clases y los equipos personalizados que crees.
Junto con Verse, la configuración de este dispositivo hace que los jugadores del espacio de clase 1 se transfieran al espacio de clase 2 al reaparecer.
Coloca dos selectores de clase, uno por cada equipo, en una zona oculta para los jugadores. Para gestionar el equipo de elementos, utiliza los ajustes de la siguiente tabla para configurar las opciones de usuario de este dispositivo.
| Opción | Valor | Explicación |
|---|---|---|
| Clase a la que cambiar | Espacio de clase: 1 | Determina a qué clase debe cambiar el jugador. |
| Visible durante la partida | Desactivado | Este dispositivo no será visible durante la partida. |
| Sonido en la zona | Desactivado | Establece si el selector de clase debería reproducir los efectos de sonido cuando un jugador entra en la zona. |
| Equipo al que cambiar | Índice de equipo: 1 | Determina a qué equipo cambiará el jugador. |
| Descartar objetos al cambiar | Activado | Establece si los objetos se deben eliminar del inventario del jugador cuando se aplique el cambio. |
| Volumen visible en partida | Desactivado | Establece si el volumen del dispositivo será visible durante la partida. |
| Muestra efectos visuales al activarse | Desactivado | Establece si el dispositivo creará un efecto visual al cambiar la clase o el equipo de un jugador. |
Para gestionar el equipo de cazadores, utiliza los ajustes de la siguiente tabla para configurar las opciones de usuario de este dispositivo.
| Opción | Valor | Explicación |
|---|---|---|
| Clase a la que cambiar | Espacio de clase: 2 | Determina a qué clase debe cambiar el jugador. |
| Equipo al que cambiar | Índice de equipo: 2 | Determina a qué equipo cambiará el jugador. |
Cómo crear funcionalidad de equipo con Verse
Hay dos equipos en este juego de Caza de elementos: cazadores y elementos. Tienes que ser capaz de hacer ciertas cosas igual para ambos equipos si quieres que el juego funcione. Por ejemplo:
-
Añadir jugadores a un equipo.
-
Eliminar jugadores de un equipo.
-
Mostrar información a los jugadores sobre su equipo.
Para 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 pensadas para tener una funcionalidad parcial que sus subclases heredan y sobre la que se basan. Primero, crearás una clase abstracta nombradabase_team y le darás la funcionalidad que compartirán los equipos de elementos y cazadores.
Este documento incluye fragmentos de Verse que muestran cómo ejecutar las mecánicas de juego necesarias. Sigue los pasos que se indican a continuación y copia la secuencia de comandos completa del paso 6 de este tutorial.
Crea un nuevo archivo de Verse en el proyecto y nómbralo base_team.verse. No será un dispositivo de Verse, así que puedes crearlo como un archivo de Verse vacío.
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 diferentes equipos de la experiencia.
# Esta clase es abstracta por lo que no se puede utilizar por sí sola. Tiene que ser heredada por otra clase.
base_team := class<abstract>:
Logger:log = log{Channel:=log_team}
@editable # Se utiliza para asignar un jugador al equipo.
ClassSelector:class_and_team_selector_device = class_and_team_selector_device{}
@editable # Se utiliza para otorgar puntuación a los agentes del equipo.
ScoreManager:score_manager_device = score_manager_device{}
@editable # Se utiliza para mostrar el título de la asignación del equipo.
TeamTitle:hud_message_device = hud_message_device{}
@editable # Se utiliza para mostrar la descripción de la asignación del equipo.
TeamDescription:hud_message_device = hud_message_device{}
@editable # Se utiliza para suscribirse a eventos eliminados de miembros del equipo (equipo de elementos) o enemigos (equipo de cazadores).
TeamManager:team_settings_and_inventory_device = team_settings_and_inventory_device{}
# Esto es una matriz de agentes en el equipo.
var TeamAgents<private>:[]agent = array{}
# Este evento recibe una señal cuando la matriz TeamAgents se vacía (indicando el final de la ronda).
TeamEmptyEvent:event() = event(){}
# Devuelve la matriz actual de TeamAgents.
# Esto es necesario porque la matriz TeamAgents es privada, por lo que 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 lo que 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 devuelve un error.
FindOnTeam(Agent:agent)<decides><transacts>: int =
Index := TeamAgents.Find[Agent]
# Asigna el agente al equipo y notifica al jugador.
InitializeAgent(Agent:agent):void =
AddAgentToTeam(Agent)
ClassSelector.ChangeTeamAndClass(Agent)
DisplayTeamInformation(Agent)
# Añade un agente a TeamAgents.
AddAgentToTeam(AgentToAdd:agent):void =
if (not FindOnTeam[AgentToAdd]):
Logger.Print("Añadiendo agente al equipo.")
set TeamAgents += array{AgentToAdd}
# Activa los dispositivos Mensaje del HUD para mostrar al jugador en qué equipo está.
DisplayTeamInformation(Agent:agent):void =
TeamTitle.Show(Agent)
TeamDescription.Show(Agent)
# Cuando un agente abandona la partida, elimínalo de la matriz TeamAgents y comprueba el final de la ronda.
EliminateAgent(Agent:agent)<suspends>:void =
Sleep(0.0) # Retrasa 1 tic de juego para asegurarte de la reaparición del jugador antes de proceder.
RemoveAgentFromTeam(Agent)
# Elimina un agente de TeamAgents.
# Si el agente eliminado era el último, emite la señal TeamEmptyEvent.
RemoveAgentFromTeam(AgentToRemove:agent):void =
set TeamAgents = TeamAgents.RemoveAllElements(AgentToRemove)
Logger.Print("Quedan {Count()} agente(s) en el equipo.")
if (Count() < 1):
Logger.Print("No quedan agentes en el equipo. Finalizando la ronda.")
TeamEmptyEvent.Signal()
Ahora que tienes esta clase, puedes crear las clases para el equipo de elementos y de cazadores. Como cada uno de ellos heredará de base_team, existen varias ventajas:
-
El código para implementar cada equipo es mucho más corto porque sus funciones y datos comunes ya están definidos en
base_team. -
Es más fácil entender qué código es específico de los equipos de elementos y cazadores porque están en sus propias clases en lugar de estar mezclados con el código común.
-
Añadir más equipos al modo de juego es mucho más fácil. Cualquier equipo nuevo hereda de
base_teamy el código que hace diferente al equipo nuevo está en su propia clase.
Recuerda que no puedes crear una instancia de una clase con el especificador <abstract>. Debes crear una clase que herede de la clase «abstract» e instanciar esa clase.
Equipo de cazadores
Primero, crea la clase para el equipo de cazadores. Crea un nuevo archivo de Verse en el proyecto llamado hunter_team.verse. No será un dispositivo de Verse, así que puedes crearlo como un archivo de Verse vacío.
Declara una clase nombrada hunter_team. Debe ser <concrete> y también heredar de base_team.
hunter_team := class<concrete>(base_team):
Convertir una clase en <concrete> significa que todos los campos de la clase deben tener un valor predeterminado. Consulta Especificadores y atributos para obtener más información.
A continuación se muestra el código completo de la secuencia de comandos de hunter_team.verse.
La clase hunter_team tiene dos funciones con el mismo nombre que las funciones de la clase base_team. Esto está permitido 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 de la clase hunter_team.
Por ejemplo, en el código siguiente, se utilizará la versión de InitializeAgent() definida en hunter_team porque anula la función del mismo nombre en base_team. Compáralo con la llamada a Count(), que utilizará la versión definida en base_team porque no hay función de anulación.
HunterTeam:hunter_team = hunter_team{}
# Utiliza la función de hunter_team.
HunterTeam.InitializeAgent(StartingHunterAgent)
# Utiliza la función de base_team.
HunterTeam.Count()
Las dos funciones anuladas también utilizan (super:). Esto les permite llamar a la versión de las funciones definidas en base_team porque base_team es la superclase de hunter_team. En el caso de InitializeAgent() y EliminateAgent(), ambas usan Logger.Print() para imprimir en el registro. Luego llaman a sus respectivas funciones desde base_team. Esto significa que las funciones tienen exactamente el mismo comportamiento que las versiones en base_team, excepto las llamadas a Logger.Print().
Consulta Subclase para obtener más información sobre <override> y (super:).
Equipo de elementos
Ahora crea la clase para el equipo de elementos. Crea un nuevo archivo de Verse en el proyecto y nómbralo prop_team.verse. No será un dispositivo de Verse, así que puedes crearlo como un archivo de Verse vacío.
Tienes que gestionar más para los miembros del equipo de elementos. Tienen efectos de latido que deben iniciarse y detenerse en función de un cronómetro y de lo lejos que se muevan. También deben ser trasladados al equipo de cazadores cuando sean eliminados.
Para gestionar los miembros del equipo de elementos, utilizarás el método RunPropGameLoop(). Piensa en este método como el gestor de todo el recorrido de un elemento por el juego. Desde el momento en que aparecen hasta el momento en que son eliminados o abandonan la partida, este método se ejecutará para cada miembro del equipo de elementos.
# Si el agente de elementos deja de moverse, entonces corre para ver si el agente de elementos se mueve más allá de la MinimumMoveDistance, si se completa el cronómetro de latido o si el agente de elementos es eliminado.
RunPropGameLoop(PropAgent:agent)<suspends>:void =
Logger.Print("Iniciando bucle de juego del agente de elementos.")
# Realiza un bucle eterno por el comportamiento del elemento hasta que el agente de elementos sea eliminado o el jugador abandone la sesión.
race:
PropAgent.AwaitNoLongerAProp()
loop:
# Espera a que el agente de elementos se mueva menos de la distancia mínima y luego avanza.
PropAgent.AwaitStopMoving(MinimumMoveDistance)
# Hasta que el agente de elementos se desplace más allá de la distancia mínima, inicia la cuenta atrás hasta el latido y luego reproduce el latido indefinidamente.
race:
PropAgent.AwaitStartMoving(MinimumMoveDistance)
block:
CountdownTimer(PropAgent)
PropAgent.StartHeartbeat()
Sleep(0.0) # Una vez finalizada la carrera (el agente de elementos se mueve), vuelve a iniciar el bucle.
RunPropGameLoop() tiene un parámetro, PropAgent. Es una constante que representa a un jugador del equipo de elementos. También tiene el especificador <suspends>, lo que significa que tarda en terminar. En este caso, no terminará hasta que el PropAgent que se pase deje de estar en el equipo de elementos.
Toda la funcionalidad de este método está contenida en una expresión race. Esto significa que el método no se completará hasta que finalice una de las expresiones dentro de esta carrera. Las expresiones son:
-
PropAgent.AwaitNoLongerAProp() -
loop
La expresión loop 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 forma es como decirle a tu programa que ejecute un conjunto de código determinado una y otra vez hasta que ocurra algo. Consulta Race para obtener más información sobre esta potente expresión.
Con este código, AwaitNoLongerAProp() gana la carrera.
# Realiza un bucle hasta que el agente de elementos ya no forme parte de la matriz PropAgents. La eliminación se produce si el agente de elementos es eliminado y convertido en cazador o si el jugador abandona la sesión.
(PropAgent:agent).AwaitNoLongerAProp()<suspends>:void =
loop:
if (not FindOnTeam[PropAgent]):
Logger.Print("Cancelando el comportamiento del agente de elementos.")
break
Sleep(0.0) # Avanza al siguiente tic de juego.
Este método comprueba constantemente si PropAgent está en el equipo de elementos. Comienza con un bucle que se ejecuta hasta que not FindOnTeam[PropAgent] tiene éxito y luego se interrumpe para completar el método. Consulta Expresiones loop y break para obtener más información.
FindOnTeam[] es una función falible que se declara en base_team. Es correcta si PropAgent se encuentra en el equipo de elementos. Sin embargo, debes utilizar el operador not para salir del bucle una vez que el PropAgent no se encuentre en el equipo de elementos. Consulta Operadores para obtener más información sobre not.
Por último, tienes que añadir un Sleep(0.0) al final del bucle. Esto garantiza que el bucle se ejecute una vez y luego avance a la siguiente actualización de la simulación. No hace falta ejecutar esta comprobación más a menudo, por lo que la función Sleep(0.0) se añade para mejorar el rendimiento. Consulta la página Referencia de la API de Verse para obtener más información sobre Sleep.
Ahora que sabes cómo funciona AwaitNoLongerAProp(), puedes escribir el bucle infinito que compite con él en RunPropGameLoop().