Código completo
El siguiente es el código completo para un juego de infiltración de tres equipos que equilibra a los jugadores de forma asimétrica para crear experiencias de juego dinámicas.
triad_infiltration_game.verse
using { /Fortnite.com/Devices }
using { /Fortnite.com/FortPlayerUtilities }
using { /Verse.org/Simulation }
using { /Verse.org/Random }
using { /UnrealEngine.com/Temporary/Diagnostics }
triad_infiltration_log_channel := class(log_channel){}
triad_infiltration := class(creative_device):
Logger:log = log{Channel := triad_infiltration_log_channel}
# Para evitar que los jugadores no puedan unirse a un equipo, debes establecer el número máximo
# de jugadores en la configuración de la isla en la suma de todas las variables Maximum(Team).
# Número máximo de jugadores en el equipo de infiltrados.
@editable
MaximumInfiltrators:int = 2
# Número máximo de jugadores en el equipo de atacantes.
@editable
MaximumAttackers:int = 4
# Número máximo de jugadores en el equipo de defensores.
@editable
MaximumDefenders:int = 4
# Matriz de teletransportadores que teletransportan jugadores a la zona de aparición de su equipo una vez que se inicia el juego.
@editable
Teleporters:[]teleporter_device = array{}
# Referencia a la secuencia de comandos de invisibility_manager que controla la invisibilidad del infiltrado.
@editable
InvisibilityManager:invisibility_manager = invisibility_manager{}
# Matriz de repartidores de armas para cada equipo.
@editable
var WeaponGranters:[]item_granter_device = array{}
# Matriz de generadores de jugadores para cada equipo.
@editable
PlayersSpawners:[]player_spawner_device = array{}
# Referencia al equipo de infiltrados.
var MaybeInfiltrators:?team = false
# Referencia al equipo de atacantes.
var MaybeAttackers:?team = false
# Referencia al equipo de defensores.
var MaybeDefenders:?team = false
# Matriz de todos los equipos del juego.
var AllTeams:[]team = array{}
# Mapa de los equipos con su número máximo de jugadores.
var TeamsAndTotals:[team]int = map{}
OnBegin<override>()<suspends>:void =
# Obtiene todos los equipos.
set AllTeams = GetPlayspace().GetTeamCollection().GetTeams()
var AllPlayers:[]player = GetPlayspace().GetPlayers()
# Guarda los equipos para consultarlos luego.
set MaybeInfiltrators = option{AllTeams[0]}
set MaybeAttackers = option{AllTeams[1]}
set MaybeDefenders = option{AllTeams[2]}
if:
Infiltrators := MaybeInfiltrators?
Attackers := MaybeAttackers?
Defenders := MaybeDefenders?
Logger.Print("Se encontró a los tres equipos.")
set TeamsAndTotals[Infiltrators] = MaximumInfiltrators
set TeamsAndTotals[Attackers] = MaximumAttackers
set TeamsAndTotals[Defenders] = MaximumDefenders
Logger.Print("Se utilizó la variable set en los tres equipos en TeamsAndTotals.")
then:
#Se suscribe a PlayerAddedEvent para permitir el equilibrio de equipos cuando un jugador nuevo se une al juego.
GetPlayspace().PlayerAddedEvent().Subscribe(OnPlayerAdded)
for(PlayerSpawner:PlayersSpawners):
PlayerSpawner.SpawnedEvent.Subscribe(OnPlayerSpawn)
BalanceTeams()
Logger.Print("Equipos equilibrados. Se está llamando a la secuencia de comandos de invisibilidad.")
InvisibilityManager.StartInvisibilityManager(AllTeams, AllPlayers, Infiltrators)
Sleep(0.25)
TeleportPlayersToStartLocations()
else:
Logger.Print("No se pudo encontrar a todos los equipos. Asegúrate de asignar los equipos correctos en los ajustes de la isla.")
# Otorga a los jugadores un arma basada en el índice de su equipo en la matriz Equipos
# al indexar el array de WeaponGranters
GrantTeamWeapon(InPlayer:player):void=
if(CurrentTeam := GetPlayspace().GetTeamCollection().GetTeam[InPlayer]):
for(TeamIndex -> PlayerTeam:AllTeams, PlayerTeam = CurrentTeam):
if(WeaponGranter := WeaponGranters[TeamIndex]):
WeaponGranter.GrantItem(InPlayer)
Logger.Print("Al jugador a del equipo de le otorgó {TeamIndex + 1} un arma.")
# Se ejecuta cuando se genera un jugador en una plataforma de aparición.
# Llama a GrantTeamWeapon a través del SpawnedAgent que se brindó.
OnPlayerSpawn(SpawnedAgent:agent):void=
if(SpawnedPlayer := player[SpawnedAgent]):
Logger.Print("Se está intentando otorgar un arma al jugador generado.")
GrantTeamWeapon(SpawnedPlayer)
# Controla al nuevo jugador que se une al juego.
OnPlayerAdded(InPlayer:player):void=
Logger.Print("Se unió un nuevo jugador, se le está asignando un equipo.")
FortTeamCollection := GetPlayspace().GetTeamCollection()
# Asigna el jugador nuevo al equipo más pequeño, de manera asimétrica.
BalancePlayer(InPlayer)
for:
TeamIndex -> PlayerTeam:AllTeams
PlayerTeam = FortTeamCollection.GetTeam[InPlayer]
TeamTeleporter := Teleporters[TeamIndex]
Transform := TeamTeleporter.GetTransform()
do:
InPlayer.Respawn(Transform.Translation, Transform.Rotation)
Logger.Print("Se teletransportó al jugador generado a su ubicación de inicio.")
# Si el jugador era un infiltrado, llama a OnInfiltratorJoined en
# InvisibilityManager.
if(PlayerTeam = MaybeInfiltrators?):
InvisibilityManager.OnInfiltratorJoined(InPlayer)
# Equilibra a todos los jugadores en todos los equipos del juego.
BalanceTeams():void=
Logger.Print("Equilibrando equipos")
var AllPlayers:[]player := GetPlayspace().GetPlayers()
set AllPlayers = Shuffle(AllPlayers)
Logger.Print("El largo de AllPlayers es {AllPlayers.Length}.")
for (TeamPlayer:AllPlayers):
BalancePlayer(TeamPlayer)
# Para cada jugador, itera a través de la lista de equipos y los asigna al
# equipo con la menor cantidad de jugadores o al equipo inicial en caso de empate.
BalancePlayer(InPlayer:player):void=
Logger.Print("Equilibrando jugador.")
var TeamToAssign:?team = false
set TeamToAssign = FindTeamWithLargestDifference()
if (AssignedTeam := TeamToAssign?, GetPlayspace().GetTeamCollection().AddToTeam[InPlayer, AssignedTeam]):
Logger.Print("Se asignó un jugador a un nuevo equipo.")
else:
Logger.Print("Este jugador ya pertenecía al equipo más pequeño.")
# Identifica al equipo con la mayor diferencia en su número de jugadores a partir del
# número máximo de jugadores.
FindTeamWithLargestDifference():?team =
Logger.Print("Se está intentando encontrar el equipo con menor número de jugadores.")
var TeamToAssign:?team = false
var LargestDifference:int = 0
for:
CandidateTeamIndex -> CandidateTeam:AllTeams
CurrentTeamSize := GetPlayspace().GetTeamCollection().GetAgents[CandidateTeam].Length
MaximumTeamSize := TeamsAndTotals[CandidateTeam]
do:
Logger.Print("Revisando un equipo...")
Logger.Print("El tamaña máximo del equipo {CandidateTeamIndex + 1} es {MaximumTeamSize}.")
DifferenceFromMaximum := MaximumTeamSize - CurrentTeamSize
Logger.Print("La diferencia con el máximo es {DifferenceFromMaximum}.")
if(LargestDifference < DifferenceFromMaximum):
set LargestDifference = DifferenceFromMaximum
set TeamToAssign = option{CandidateTeam}
Logger.Print("Se econtró un equipo {CandidateTeamIndex + 1} con una diferencia de {DifferenceFromMaximum}.")
return TeamToAssign
# Teletransporta jugadores a la zona de aparición de su equipo una vez que finaliza la tarea de equilibrio.
TeleportPlayersToStartLocations():void=
Logger.Print("Se está teletransportando a los jugadores a las posiciones de inicio.")
for:
TeamIndex -> PlayerTeam:AllTeams
TeamPlayers := GetPlayspace().GetTeamCollection().GetAgents[PlayerTeam]
TeamTeleporter := Teleporters[TeamIndex]
do:
for(TeamPlayer:TeamPlayers):
TeamTeleporter.Teleport(TeamPlayer)
Logger.Print("Se teletransportó a este jugador a su posición de inicio.")
invisibility.verse
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
triad_invisibility_log_channel := class(log_channel){}
invisibility_manager := class(creative_device):
Logger:log = log{Channel := triad_invisibility_log_channel}
# Matriz de generadores de jugadores para el equipo de infiltrados.
@editable
PlayersSpawners:[]player_spawner_device = array{}
# Determina si la visibilidad de los infiltrados se comparte con los compañeros de equipo.
@editable
IsVisibilityShared:logic = true
# Por cuánto tiempo son visibles los infiltrados después del daño.
@editable
VulnerableSeconds:float = 3.0
# Qué tan rápido parpadean los infiltrados después de recibir daño.
@editable
FlickerRateSeconds:float = 0.4
# Matriz de todos los equipos del juego.
var AllTeams:[]team = array{}
# Mapa de jugadores con la cantidad de segundos que les quedan para seguir parpadeando.
var PlayerVisibilitySeconds:[agent]float = map{}
OnBegin<override>()<suspends>:void=
# Espera a que los equipos estén equilibrados antes de suscribirte a eventos que hagan invisibles a los jugadores.
Logger.Print("Esperando a que los equipos estén equilibrados...")
# Inicia la lógica del administrador de invisibilidad. Se llama desde la clase triad_infiltration una vez finalizado el equilibrio de equipos.
StartInvisibilityManager<public>(GameTeams:[]team, AllPlayers:[]player, Infiltrators:team):void=
Logger.Print("Invisibility script started!")
set AllTeams = GameTeams
for(PlayerSpawner:PlayersSpawners):
PlayerSpawner.SpawnedEvent.Subscribe(OnPlayerSpawn)
# Para cada jugador, si apareció en el equipo de infiltrados, crea una función OnInfiltratorDamaged para ese
# jugador. A continuación, hace invisible a su personaje.
for(TeamPlayer:AllPlayers):
if:
FortCharacter:fort_character = TeamPlayer.GetFortCharacter[]
CurrentTeam:team := GetPlayspace().GetTeamCollection().GetTeam[TeamPlayer]
Logger.Print("Got this player's current team")
Infiltrators = CurrentTeam
set PlayerVisibilitySeconds[TeamPlayer] = 0.0
Logger.Print("Added player to PlayerVisibilitySeconds")
then:
spawn{OnInfiltratorDamaged(TeamPlayer)}
Logger.Print("Jugador generado como infiltrado, por lo que se ha hecho invisible.")
FortCharacter.Hide()
else:
Logger.Print("Este jugador no es un infiltrado.")
# Hace que parpadee la visibilidad de un agente ocultando y mostrando de manera repetida a su fort_character.
FlickerCharacter(InCharacter:fort_character)<suspends>:void=
Logger.Print("FlickerCharacter() invoked")
# Cómo hacer un bucle de ocultar y mostrar el personaje para crear un efecto de parpadeo.
loop:
InCharacter.Hide()
Sleep(FlickerRateSeconds)
InCharacter.Show()
Sleep(FlickerRateSeconds)
# En cada bucle, disminuye la cantidad de tiempo que el personaje parpadea en FlickerRateSeconds.
# Si el tiempo restante llega a 0, interrumpe el bucle.
if:
TimeRemaining := set PlayerVisibilitySeconds[InCharacter.GetAgent[]] -= FlickerRateSeconds * 2
TimeRemaining <= 0.0
then:
InCharacter.Hide()
break
# Hace parpadear la visibilidad de un agente cada vez que este recibe daño.
OnInfiltratorDamaged(InAgent:agent)<suspends>:void=
Logger.Print("Attempting to start flickering this character")
TeamCollection := GetPlayspace().GetTeamCollection()
if (FortCharacter := InAgent.GetFortCharacter[]):
loop:
if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]):
# Configura los segundos de PlayerVisibility de cada compañero de equipo y genera un FlickerEvent.
for(Teammate:TeamAgents):
Logger.Print("Calling StartOrResetFlickering on a Teammate")
StartOrResetFlickering(Teammate)
else:
# Solo hace parpadear al personaje dañado.
Logger.Print("Calling StartOrResetFlickering on InAgent")
StartOrResetFlickering(InAgent)
FortCharacter.DamagedEvent().Await()
# Inicia un nuevo evento de parpado si el agente estaba invisible, de lo contrario,
# restablece el parpadeo en curso del agente.
StartOrResetFlickering(InAgent:agent):void=
if (not IsFlickering[InAgent], FortCharacter := InAgent.GetFortCharacter[]):
Logger.Print("Attempting to start NEW FlickerEvent for this character")
# Comenzó un nuevo parpadeo.
if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds):
spawn{FlickerCharacter(FortCharacter)}
Logger.Print("Spawned a FlickerEvent for this character")
else:
# Restablece el parpadeo en curso.
if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds):
Logger.Print("Reset character's FlickerTimer to VulnerableSeconds")
# Indica si al jugador le queda algo de tiempo para parpadear.
IsFlickering(InAgent:agent)<decides><transacts>:void=
PlayerVisibilitySeconds[InAgent] > 0.0
# Genera una función OnInfiltratorDamaged cuando un nuevo infiltrado se une al juego.
OnInfiltratorJoined<public>(InAgent:agent):void=
spawn{OnInfiltratorDamaged(InAgent)}
# Controla la aparición de un jugador desde la plataforma de aparición de un infiltrado.
OnPlayerSpawn(SpawnedAgent:agent):void=
Logger.Print("Un jugador acaba de aparecer desde una plataforma de aparición de infiltrado.")
if:
FortCharacter:fort_character = SpawnedAgent.GetFortCharacter[]
CurrentTeam := GetPlayspace().GetTeamCollection().GetTeam[SpawnedAgent]
AllTeams[0] = CurrentTeam
Logger.Print("Jugador generado como infiltrado, por lo que se ha hecho invisible.")
then:
FortCharacter.Hide()
item_capture_manager.verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Fortnite.com/Characters }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/SpatialMath }
triad_item_capture_log_channel := class(log_channel){}
item_capture_manager := class(creative_device):
Logger:log = log{Channel := triad_item_capture_log_channel}
# Generador de objetos de captura que genera el elemento que se capturará.
@editable
CaptureItemSpawner:capture_item_spawner_device = capture_item_spawner_device{}
# Utilería que flota sobre la cabeza de los jugadores cuando están sosteniendo el elemento.
# CaptureItemSpawner.
@editable
CaptureItemIndicator:creative_prop = creative_prop{}
# Indicador que muestra en el mapa el lugar donde se encuentran los objetivos de cada equipo.
@editable
MapIndicator:map_indicator_device = map_indicator_device{}
# Frecuencia con la que CaptureItemIndicator actualiza su posición.
@editable
UpdateRateSeconds:float = 0.15
# Altura a la que flota CaptureItemIndicator sobre la cabeza de un jugador.
@editable
VerticalOffset:float = 180.0
# Muestra un mensaje cuando un jugador agarra el elemento de captura.
@editable
ItemGrabbedMessageDevice:hud_message_device = hud_message_device{}
# Cantidad de tiempo que se debe esperar para que se devuelvan los indicadores de CaptureItem y de mapa.
# Si el tiempo es negativo, significa que los indicadores nunca se devolverán a menos que se
# vuelva a recoger el objetivo.
@editable
ReturnTime:float = 10.0
# Otorga la puntuación cuando un jugador captura el elemento de captura.
@editable
ScoreManagerDevice:score_manager_device = score_manager_device{}
OnBegin<override>()<suspends>:void=
CaptureItemSpawner.ItemPickedUpEvent.Subscribe(OnItemPickedUp)
CaptureItemSpawner.ItemCapturedEvent.Subscribe(OnItemCaptured)
CaptureItemSpawner.ItemDroppedEvent.Subscribe(OnItemDropped)
SpawnerTransform := CaptureItemSpawner.GetTransform()
# Vuelve a teletransportar al generador y oculta CaptureItemIndicator debajo del mapa fuera del sitio.
CaptureItemIndicator.MoveTo(SpawnerTransform.Translation + vector3{Z := VerticalOffset * 10.0}, SpawnerTransform.Rotation, UpdateRateSeconds)
MapIndicator.MoveTo(SpawnerTransform.Translation + vector3{Z := VerticalOffset * 10.0}, SpawnerTransform.Rotation, UpdateRateSeconds)
Logger.Print("La baliza regresó al generador de captura.")
# Señala a cada jugador cuando un jugador agarra el objetivo.
OnItemPickedUp(InAgent:agent):void=
Logger.Print("Objectivo recogido.")
if(FortCharacter := InAgent.GetFortCharacter[]):
ItemGrabbedMessageDevice.Show()
spawn{FollowCharacter(FortCharacter)}
# Cuando un jugador suelta un elemento, genera la función WaitForReturn()
# si el valor de ReturnTime es mayor que 0.
OnItemDropped(InAgent:agent):void=
Logger.Print("Objectivo soltado.")
if(ReturnTime >= 0.0):
spawn{WaitForReturn()}
else:
Logger.Print("El objetivo soltado no regresa.")
# Cuando se captura el elemento, otorga puntuación al equipo capturador y devuelve los indicadores.
OnItemCaptured(CapturingAgent:agent):void=
Logger.Print("Objectivo capturado.")
ScoreManagerDevice.Activate()
ReturnIndicators()
# Espera la cantidad de tiempo de ReturnTime, luego devuelve los indicadores.
WaitForReturn()<suspends>:void=
Logger.Print("Esperando a que regresen los indicadores...")
# Devuelve los indicadores de CaptureItem y de mapa si el elemento de captura
# no se recoge antes de que se agote el tiempo.
ShouldReturn:logic := race:
block:
Sleep(ReturnTime)
true
block:
CaptureItemSpawner.ItemPickedUpEvent.Await()
false
if(ShouldReturn?):
ReturnIndicators()
# Hace que CaptureItemIndicator siga continuamente a un jugador encima de su cabeza.
# Hace una carrera entre el bucle de actualización para CaptureItemIndictator, y si el jugador
# captura el elemento, suelta el elemento o es eliminado.
FollowCharacter(FortCharacter:fort_character)<suspends>:void=
Logger.Print("Se generó la función FollowCharacter.")
race:
loop:
Transform := FortCharacter.GetTransform()
spawn{CaptureItemIndicator.MoveTo(Transform.Translation + vector3{Z := VerticalOffset}, Transform.Rotation, UpdateRateSeconds)}
spawn{MapIndicator.MoveTo(Transform.Translation + vector3{Z := VerticalOffset}, Transform.Rotation, UpdateRateSeconds)}
# Es conveniente asegurarnos de que este bucle se ejecute una sola vez por actualización de simulación, así que pausamos el proceso durante un solo tic de juego.
Sleep(0.0)
CaptureItemSpawner.ItemCapturedEvent.Await()
CaptureItemSpawner.ItemDroppedEvent.Await()
FortCharacter.EliminatedEvent().Await()
Logger.Print("Objectivo soltado o capturado.")
# Vuelve a colocar los indicadores de mapa y de elemento de captura en sus posiciones iniciales encima de los generadores.
ReturnIndicators():void=
SpawnerTransform := CaptureItemSpawner.GetTransform()
# Vuelve a teletransportar al generador y oculta CaptureItemIndicator y MapIndicator encima del mapa fuera del sitio.
spawn{CaptureItemIndicator.MoveTo(SpawnerTransform.Translation + vector3{Z := VerticalOffset * 10.0}, SpawnerTransform.Rotation, UpdateRateSeconds)}
spawn{MapIndicator.MoveTo(SpawnerTransform.Translation + vector3{Z := VerticalOffset * 10.0}, SpawnerTransform.Rotation, UpdateRateSeconds)}
Logger.Print("Los indicadores regresaron a capture spawner.")
Por tu cuenta
Al completar esta guía, aprendiste a utilizar Verse para crear un juego que equilibra automáticamente los jugadores de los equipos.
Con lo que aprendiste, intenta hacer lo siguiente:
- Intenta jugar con diferentes parámetros para los Infiltrados, Atacantes y Defensores para crear tu experiencia de juego ideal. ¿Y si los infiltrados tuvieran armas cuerpo a cuerpo? ¿Y si los Defensores también fueran invisibles?
- ¿Pueden los Infiltrados y los Atacantes luchar por el mismo objetivo? ¿Se puede cambiar la condición de victoria?