Code complet
Le code complet pour un jeu d'infiltration à trois équipes, répartissant les joueurs de façon asymétrique afin de créer des expériences de jeu dynamiques, est le suivant :
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}
# Pour éviter que les joueurs ne puissent pas rejoindre une équipe, définissez la somme de toutes les variables Maximum(Team)
# comme le nombre maximal de joueurs dans les paramètres de l'île.
# Nombre maximum de joueurs dans l'équipe des infiltrés.
@editable
MaximumInfiltrators:int = 2
# Nombre maximum de joueurs dans l'équipe des attaquants.
@editable
MaximumAttackers:int = 4
# Nombre maximum de joueurs dans l'équipe des défenseurs.
@editable
MaximumDefenders:int = 4
# Matrice des téléporteurs qui transportent les joueurs à la zone d'apparition de leur équipe une fois la partie commencée.
@editable
Teleporters:[]teleporter_device = array{}
# Référence au script invisibility_manager qui contrôle l'invisibilité des infiltrés.
@editable
InvisibilityManager:invisibility_manager = invisibility_manager{}
# Matrice de fournisseurs d'armes pour chaque équipe.
@editable
var WeaponGranters:[]item_granter_device = array{}
# Matrice des points d'apparition de joueur de chaque équipe.
@editable
PlayersSpawners:[]player_spawner_device = array{}
# Référence à l'équipe des infiltrés.
var MaybeInfiltrators:?team = false
# Référence à l'équipe des attaquants.
var MaybeAttackers:?team = false
# Référence à l'équipe des défenseurs.
var MaybeDefenders:?team = false
# Matrice de l'ensemble des équipes du jeu.
var AllTeams:[]team = array{}
# Mappage des équipes à leur nombre maximum de joueurs.
var TeamsAndTotals:[team]int = map{}
OnBegin<override>()<suspends>:void =
# Obtenir toutes les équipes.
set AllTeams = GetPlayspace().GetTeamCollection().GetTeams()
var AllPlayers:[]player = GetPlayspace().GetPlayers()
# Enregistrez les équipes pour y faire référence ultérieurement.
set MaybeInfiltrators = option{AllTeams[0]}
set MaybeAttackers = option{AllTeams[1]}
set MaybeDefenders = option{AllTeams[2]}
if:
Infiltrators := MaybeInfiltrators?
Attackers := MaybeAttackers?
Defenders := MaybeDefenders?
Logger.Print("Found all three teams")
set TeamsAndTotals[Infiltrators] = MaximumInfiltrators
set TeamsAndTotals[Attackers] = MaximumAttackers
set TeamsAndTotals[Defenders] = MaximumDefenders
Logger.Print("Set all three teams in TeamsAndTotals")
then:
#S'abonner à l'événement PlayerAddedEvent pour permettre le rééquilibrage de l'équipe lorsqu'un nouveau joueur rejoint la partie.
GetPlayspace().PlayerAddedEvent().Subscribe(OnPlayerAdded)
for(PlayerSpawner:PlayersSpawners):
PlayerSpawner.SpawnedEvent.Subscribe(OnPlayerSpawn)
BalanceTeams()
Logger.Print("Teams balanced, calling invisibility script")
InvisibilityManager.StartInvisibilityManager(AllTeams, AllPlayers, Infiltrators)
Sleep(0,25)
TeleportPlayersToStartLocations()
else:
Logger.Print("Couldn't find all teams, make sure to assign the correct teams in your island settings.")
# Attribue une arme aux joueurs en fonction de l'index de leur équipe dans la matrice des équipes
# par indexation dans la matrice 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("Granted the a Player on team {TeamIndex + 1} a weapon")
# S'exécute lorsqu'un joueur apparaît d'un point d'apparition.
# Appelle GrantTeamWeapon à l'aide du SpawnedAgent fourni.
OnPlayerSpawn(SpawnedAgent:agent):void=
if(SpawnedPlayer := player[SpawnedAgent]):
Logger.Print("Attempting to grant spawned player a weapon")
GrantTeamWeapon(SpawnedPlayer)
# Gère l'arrivée d'un nouveau joueur dans la partie.
OnPlayerAdded(InPlayer:player):void=
Logger.Print("A new Player joined, assigning them to a team")
FortTeamCollection := GetPlayspace().GetTeamCollection()
# Affecter le nouveau joueur à la plus petite équipe, de manière asymétrique.
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("Teleported the spawned player to their start location")
# Si le joueur était un infiltré, appeler OnInfiltratorJoined dans
# InvisibilityManager.
if(PlayerTeam = MaybeInfiltrators?):
InvisibilityManager.OnInfiltratorJoined(InPlayer)
# Répartit les joueurs dans l'ensemble des équipes de la partie
BalanceTeams():void=
Logger.Print("Beginning to balance teams")
var AllPlayers:[]player := GetPlayspace().GetPlayers()
set AllPlayers = Shuffle(AllPlayers)
Logger.Print("AllPlayers Length is {AllPlayers.Length}")
for (TeamPlayer:AllPlayers):
BalancePlayer(TeamPlayer)
# Pour chaque joueur, itérer sur la liste des équipes et l'assigner à
# l'équipe ayant le moins de joueurs ou son équipe de départ en cas d'égalité.
BalancePlayer(InPlayer:player):void=
Logger.Print("Beginning to balance player")
var TeamToAssign:?team = false
set TeamToAssign = FindTeamWithLargestDifference()
if (AssignedTeam := TeamToAssign?, GetPlayspace().GetTeamCollection().AddToTeam[InPlayer, AssignedTeam]):
Logger.Print("Assigned player to a new team")
else:
Logger.Print("This player was already on the smallest team")
# Trouve l'équipe ayant la plus grande différence entre son nombre de joueurs actuels et son
# nombre maximum de joueurs.
FindTeamWithLargestDifference():?team =
Logger.Print("Attempting to find smallest team")
var TeamToAssign:?team = false
var LargestDifference:int = 0
for:
CandidateTeamIndex -> CandidateTeam:AllTeams
CurrentTeamSize := GetPlayspace().GetTeamCollection().GetAgents[CandidateTeam].Length
MaximumTeamSize := TeamsAndTotals[CandidateTeam]
do:
Logger.Print("Checking a team...")
Logger.Print("Maximum size of team {CandidateTeamIndex + 1} is {MaximumTeamSize}")
DifferenceFromMaximum := MaximumTeamSize - CurrentTeamSize
Logger.Print("Difference from maximum is {DifferenceFromMaximum}")
if(LargestDifference < DifferenceFromMaximum):
set LargestDifference = DifferenceFromMaximum
set TeamToAssign = option{CandidateTeam}
Logger.Print("Found team {CandidateTeamIndex + 1} with difference {DifferenceFromMaximum}")
return TeamToAssign
# Téléporte les joueurs au point d'apparition de leur équipe une fois l'équilibrage des équipes terminé.
TeleportPlayersToStartLocations():void=
Logger.Print("Teleporting players to start locations")
for:
TeamIndex -> PlayerTeam:AllTeams
TeamPlayers := GetPlayspace().GetTeamCollection().GetAgents[PlayerTeam]
TeamTeleporter := Teleporters[TeamIndex]
do:
for(TeamPlayer:TeamPlayers):
TeamTeleporter.Teleport(TeamPlayer)
Logger.Print("Teleported this player to their start location")
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}
# Matrice des points d'apparition de joueurs de l'équipe des infiltrés
@editable
PlayersSpawners:[]player_spawner_device = array{}
# Si la visibilité des infiltrés est partagée avec les coéquipiers.
@editable
IsVisibilityShared:logic = true
# Combien de temps les infiltrés restent visibles après avoir subi des dégâts.
@editable
VulnerableSeconds:float = 3.0
# Vitesse de clignotement des infiltrés après qu'ils ont subi des dégâts.
@editable
FlickerRateSeconds:float = 0.4
# Matrice de l'ensemble des équipes du jeu.
var AllTeams:[]team = array{}
# Mappage des joueurs au nombre de secondes qu'il leur reste à clignoter.
var PlayerVisibilitySeconds:[agent]float = map{}
OnBegin<override>()<suspends>:void=
# Attendre que les équipes soient équilibrées avant l'inscription aux événements qui rendent les joueurs invisibles.
Logger.Print("Waiting for teams to be balanced...")
# Démarre la logique du gestionnaire d'invisibilité. Appelé par la classe triad_infiltration une fois l'équilibrage des équipes terminé
StartInvisibilityManager<public>(GameTeams:[]team, AllPlayers:[]player, Infiltrators:team):void=
Logger.Print("Invisibility script started!")
set AllTeams = GameTeams
for(PlayerSpawner:PlayersSpawners):
PlayerSpawner.SpawnedEvent.Subscribe(OnPlayerSpawn)
Générez une fonction OnInfiltratorDamaged pour chaque joueur qui apparait dans l'équipe des
# infiltrés. Rendez ensuite leurs personnages invisibles.
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("Player spawned as an infiltrator, making them invisible")
FortCharacter.Hide()
else:
Logger.Print("This player isn't an infiltrator)")
# Fait clignoter les agents en masquant et montrant son fort_character à répétition
FlickerCharacter(InCharacter:fort_character)<suspends>:void=
Logger.Print("FlickerCharacter() appelé)
# Masquer puis afficher en boucle le personnage pour créer un effet de scintillement.
loop:
InCharacter.Hide()
Sleep(FlickerRateSeconds)
InCharacter.Show()
Sleep(FlickerRateSeconds)
# À chaque boucle, réduisez la durée pendant laquelle le personnage scintille à l’aide de FlickerRateSeconds.
# Si le temps restant atteint 0, interrompez la boucle.
if:
TimeRemaining := set PlayerVisibilitySeconds[InCharacter.GetAgent[]] -= FlickerRateSeconds * 2
TimeRemaining <= 0.0
then:
InCharacter.Hide()
break
# Fait clignoter les agents toute fois qu'ils subissent des dégâts.
OnInfiltratorDamaged(InAgent:agent)<suspends>:void=
Logger.Print("Tentative de début de scintillement de ce personnage")
TeamCollection := GetPlayspace().GetTeamCollection()
if (FortCharacter := InAgent.GetFortCharacter[]):
loop:
if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]):
# Définir chaque coéquipier en secondes de PlayerVisibility, puis générer un FlickerEvent.
for(Teammate:TeamAgents):
Logger.Print("Calling StartOrResetFlickering on a Teammate")
StartOrResetFlickering(Teammate)
else:
# Juste faire clignoter le personnage qui a subi des dégâts.
Logger.Print("Calling StartOrResetFlickering on InAgent")
StartOrResetFlickering(InAgent)
FortCharacter.DamagedEvent().Await()
# Démarre un nouvel événement de clignotement si l'agent était invisible, sinon
# réinitialise le clignotement en cours de l'agent.
StartOrResetFlickering(InAgent:agent):void=
if (not IsFlickering[InAgent], FortCharacter := InAgent.GetFortCharacter[]):
Logger.Print("Attempting to start NEW FlickerEvent for this character")
# Un nouveau clignotement a commencé.
if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds):
spawn{FlickerCharacter(FortCharacter)}
Logger.Print("FlickerEvent généré pour ce personnage")
else:
# Réinitialiser le clignotement en cours.
if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds):
Logger.Print("Reset character's FlickerTimer to VulnerableSeconds")
# Indique si le joueur va encore clignoter
IsFlickering(InAgent:agent)<decides><transacts>:void=
PlayerVisibilitySeconds[InAgent] > 0.0
# Génère une fonction OnInfiltratorDamaged lorsqu'un nouvel infiltré rejoint la partie
OnInfiltratorJoined<public>(InAgent:agent):void=
spawn{OnInfiltratorDamaged(InAgent)}
# Gère un joueur qui apparaît à un point d'apparition des infiltrés.
OnPlayerSpawn(SpawnedAgent:agent):void=
Logger.Print("A player just spawned from an infiltrator spawn pad!")
if:
FortCharacter:fort_character = SpawnedAgent.GetFortCharacter[]
CurrentTeam := GetPlayspace().GetTeamCollection().GetTeam[SpawnedAgent]
AllTeams[0] = CurrentTeam
Logger.Print("Player spawned as an infiltrator, making them 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}
# Générateur d'objets de capture qui fait apparaître l'objet à capturer.
@editable
CaptureItemSpawner:capture_item_spawner_device = capture_item_spawner_device{}
# Accessoire qui flotte au-dessus de la tête d'un joueur lorsqu'il tient l'objet issu
# du CaptureItemSpawner.
@editable
CaptureItemIndicator:creative_prop = creative_prop{}
# Indicateur qui affiche sur la carte où se trouvent les objectifs de chaque équipe.
@editable
MapIndicator:map_indicator_device = map_indicator_device{}
# Fréquence à laquelle l'indicateur CaptureItemIndicator met à jour sa position.
@editable
UpdateRateSeconds:float = 0.15
# Hauteur à laquelle l'indicateur CaptureItemIndicator flotte au-dessus de la tête du joueur.
@editable
VerticalOffset:float = 180.0
# Affiche un message lorsqu'un joueur s'empare de l'objet à capturer.
@editable
ItemGrabbedMessageDevice:hud_message_device = hud_message_device{}
# Le délai d'attente avant de renvoyer les indicateurs CaptureItem et les indicateurs de carte.
# Un délai négatif indique que les indicateurs ne renverront rien à moins que l'objectif
# soit ramassé à nouveau.
@editable
ReturnTime:float = 10.0
# Attribue des points lorsqu'un joueur capture l'objet à capturer.
@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()
# Renvoyer au point d'apparition par téléportation, en cachant le CaptureItemIndicator sous la carte.
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("Returned Beacon to capture spawner")
# Signaler à chaque joueur qu'un joueur s'est emparé de l'objectif.
OnItemPickedUp(InAgent:agent):void=
Logger.Print("Objective Grabbed")
if(FortCharacter := InAgent.GetFortCharacter[]):
ItemGrabbedMessageDevice.Show()
spawn{FollowCharacter(FortCharacter)}
# Lorsqu'un joueur dépose un objet, générer une fonction WaitForReturn()
# si le ReturnTime est supérieur à 0.
OnItemDropped(InAgent:agent):void=
Logger.Print("Objective Dropped")
if(ReturnTime >= 0.0):
spawn{WaitForReturn()}
else:
Logger.Print("The dropped objective does not return")
# Une fois l'objet capturé, attribuer un score à l'équipe qui l'a capturé et renvoyer les indicateurs.
OnItemCaptured(CapturingAgent:agent):void=
Logger.Print("Objective Captured")
ScoreManagerDevice.Activate()
ReturnIndicators()
# Attendre toute la durée de ReturnTime, puis renvoyer les indicateurs.
WaitForReturn()<suspends>:void=
Logger.Print("Waiting t return the indicators...")
# Renvoyer les indicateurs CaptureItem et de carte si l'objet à capturer
# n'est pas ramassé avant la fin du temps imparti.
ShouldReturn:logic := race:
block:
Sleep(ReturnTime)
true
block:
CaptureItemSpawner.ItemPickedUpEvent.Await()
false
if(ShouldReturn?):
ReturnIndicators()
# L'indicateur CaptureItemIndicator suit continuellement le joueur, au-dessus de sa tête.
# Les courses entre la boucle de mise à jour pour le CaptureItemIndictator, et si le joueur
# capture l'objet, le laisse tomber ou est éliminé.
FollowCharacter(FortCharacter:fort_character)<suspends>:void=
Logger.Print("Spawned FollowCharacter function")
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)}
# Nous voulons nous assurer que cette boucle ne s'exécute qu'une seule fois par mise à jour de simulation et entrons donc en veille pendant un tick de jeu.
Sleep(0.0)
CaptureItemSpawner.ItemCapturedEvent.Await()
CaptureItemSpawner.ItemDroppedEvent.Await()
FortCharacter.EliminatedEvent().Await()
Logger.Print("Objective dropped or captured")
# Renvoie les indicateurs de carte et d'objets capturés à leurs positions initiales au-dessus des générateurs.
ReturnIndicators():void=
SpawnerTransform := CaptureItemSpawner.GetTransform()
# Renvoyer au point d'apparition par téléportation, en cachant le CaptureItemIndicator et le MapIndicator au-dessus de la carte.
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("Returned Indicators to capture spawner")
À vous de jouer
Au cours de ce tutoriel, vous avez appris à utiliser Verse pour créer un jeu qui répartit de façon asymétrique les équipes de joueurs.
Utilisez vos connaissances pour effectuer les tâches suivantes :
- Essayez de jouer avec différents paramètres pour les infiltrés, les attaquants et les défenseurs afin de créer votre expérience de jeu idéale. Et si les infiltrés avaient des armes de mêlée ? Et si les défenseurs étaient également invisibles ?
- Les infiltrés et les attaquants peuvent-ils se battre pour le même objectif ? Pouvez-vous changer la condition de victoire ?