Cette section comprend le code complet à ajouter aux fichiers Verse que vous avez créés.
Code complet
Ce projet contient plusieurs fichiers Verse.
-
heartbeat.verse : voir ci-dessous pour obtenir le code complet du fichier.
-
base_team.verse : voir ci-dessous pour obtenir le code complet du fichier.
-
hunter_team.verse : voir ci-dessous pour obtenir le code complet du fichier.
-
prop_team.verse : voir ci-dessous pour obtenir le code complet du fichier.
-
round_timer.verse : voir ci-dessous pour obtenir le code complet du fichier.
-
waiting_for_more_players.verse : voir ci-dessous pour obtenir le code complet du fichier.
heartbeat.verse
using { /Fortnite.com/Characters }
using { /Fortnite.com/Devices }
using { /Fortnite.com/UI }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/UI }
using { /Verse.org/Colors }
using { /Verse.org/Simulation }
log_heart_beat := class(log_channel){}
# Ces messages informent un agent d'accessoire qu'il doit se déplacer afin d'éviter que sa pulsation ne devienne visible.
HeartBeatWarningMessage<localizes>(Time:int):message = "Heart Beat in {Time} Seconds. Move!"
HeartBeatWarningClear<localizes>:message = ""
# Cette classe a exposé les propriétés modifiables de battement de cœur à l'appareil prop_hunt.
heart_beat := class<concrete>():
Logger:log = log{Channel:=log_heart_beat}
@editable # Nombre de secondes pendant lesquelles un agent d'accessoire doit se déplacer avant que la pulsation n'indique sa position.
MoveTime:float = 15.0
@editable # Nombre de secondes restantes avant l'affichage de l'avertissement de pulsation. Cette valeur ne doit pas être supérieure au paramètre HeartBeatTimer.
WarningTime:float = 5.0
@editable # Matrice d'appareils d'effets visuels de pulsation. Il en existe une par joueur.
AgentVFX:[]heartbeat_vfx = array{}
@editable # Lecteur audio utilisé pour lire les effets sonores (SFX) des battements de coeur.
SFXPlayer:radio_device = radio_device{}
# Cette carte associe une interface utilisateur pour l'affichage de l'avertissement de pulsation à chaque agent d'accessoire.
var WarningUI:[agent]heartbeat_warning_ui = map{}
# Effectue un suivi du nombre de joueurs avec un effet de pulsation actif afin de pouvoir gérer l'appareil d'effets sonores.
var NumberOfHeartBeats:int = 0
# Configure l'interface utilisateur de pulsation pour l'agent.
SetUpUI(PropAgent:agent):void =
if:
not WarningUI[PropAgent]
AsPlayer := player[PropAgent]
PlayerUI := GetPlayerUI[AsPlayer]
then:
UIData:heartbeat_warning_ui = heartbeat_warning_ui{}
UIData.CreateCanvas()
PlayerUI.AddWidget(UIData.Canvas, player_ui_slot{ZOrder := 1})
if (set WarningUI[PropAgent] = UIData) {}
# Activer les effets visuels et sonores de battement de cœur du joueur indiqué.
Enable(PropAgent:agent, HeartBeatVFXData:heartbeat_vfx):void =
if:
# Identifie le personnage utilisé pour trouver la position de l'accessoire dans la scène.
Character := PropAgent.GetFortCharacter[]
then:
# Définir la position de l'effet visuel de battement de cœur sur la position de l'agent d'accessoire.
HeartBeatVFXData.Activate(Character.GetTransform())
# Incrémente le nombre de pulsations ; s'il s'agit de la première pulsation, lit le son pour l'activer.
set NumberOfHeartBeats += 1
if (NumberOfHeartBeats = 1) then SFXPlayer.Play()
# Abonner l'agent d'accessoire au lecteur audio afin que le son de battement de cœur soit lu à partir de cette position.
SFXPlayer.Register(PropAgent)
else:
Logger.Print("Character, Index, or HeartBeatVFXData not found. Cannot start the heartbeat")
# Supprime les effets visuels et les effets sonores de battement de cœur pour l'agent d'accessoire indiqué.
Disable(PropAgent:agent, HeartBeatVFXData:heartbeat_vfx):void =
Logger.Print("Disabling heart beat.")
# Désactiver les effets visuels.
HeartBeatVFXData.Deactivate()
# Désabonner l'agent d'accessoire du lecteur audio, ce qui arrête le son de battement de cœur.
SFXPlayer.Unregister(PropAgent)
# Diminue le nombre de pulsations. Ce nombre ne doit jamais descendre en dessous de 0.
set NumberOfHeartBeats -= 1
if (NumberOfHeartBeats < 0) then set NumberOfHeartBeats = 0
# Supprime tous les effets visuels et les effets sonores de pulsation pour les agents d'accessoire.
DisableAll():void =
Logger.Print("Disabling all heart beats.")
# Itérer tous les effets visuels et les déplacer vers 0,0,0.
for (HeartBeatVFXDevice : AgentVFX):
HeartBeatVFXDevice.Deactivate()
# Désabonne tous les joueurs du son de pulsation.
SFXPlayer.UnregisterAll()
# Remet à 0 le compteur de pulsations
set NumberOfHeartBeats = 0
# La classe heartbeat_warning_ui contient une structure de données permettant de suivre le canevas d'IU et le bloc de texte par joueur, ainsi que la fonction permettant de créer un nouveau canevas d'IU pour l'avertissement de battement de cœur.
heartbeat_warning_ui := class:
var Canvas:canvas = canvas{}
var Text:text_block = text_block{}
# Crée la zone de dessin d'IU pour le message d'avertissement.
CreateCanvas():void =
set Text = text_block{DefaultTextColor := NamedColors.White, DefaultShadowColor := NamedColors.Black}
set Canvas = canvas:
Slots := array:
canvas_slot:
Anchors := anchors{Minimum := vector2{X := 0.5, Y := 0.75}, Maximum := vector2{X := 0.5, Y := 0.75}}
Offsets := margin{Top := 0.0, Left := 0.0, Right := 0.0, Bottom := 0.0}
Alignment := vector2{X := 0.5, Y := 1.0}
SizeToContent := true
Widget := Text
# La classe heartbeat_vfx contient une structure de données de suivi de la racine des effets visuels et des objets vfx_spawner_device par joueur, ainsi que les fonctions permettant de positionner l'effet visuel ou de le réinitialiser.
heartbeat_vfx := class<concrete>:
@editable # L'appareil d'effets visuels pour chaque pulsation.
VFXDevice:vfx_spawner_device = vfx_spawner_device{}
# Ce décalage est utilisé pour positionner la pulsation au-dessus de la tête d'un agent d'accessoire.
HeartBeatVFXOffset:vector3 = vector3{X := 0.0, Y := 0.0, Z := 110.0}
# Définit la position de l'effet visuel de pulsation et active l'effet visuel.
Activate(Transform:transform):void =
VFXPosition := Transform.Translation + HeartBeatVFXOffset
if (VFXDevice.TeleportTo[VFXPosition, Transform.Rotation]):
VFXDevice.Enable()
# Désactive l'effet visuel, et masque ainsi les effets visuels de pulsation
Deactivate():void =
VFXDevice.Disable()
base_team.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){}
# Cette classe définit les appareils nécessaires aux diverses équipes du jeu.
# Cette classe abstraite n'est pas utilisable seule. Une autre classe doit en hériter.
base_team := class<abstract>:
Logger:log = log{Channel:=log_team}
@editable # Permet d'affecter un joueur à l'équipe.
ClassSelector:class_and_team_selector_device = class_and_team_selector_device{}
@editable # Permet d'attribuer un score aux agents de l'équipe.
ScoreManager:score_manager_device = score_manager_device{}
@editable # Permet d'afficher le titre d'assignation de l'équipe.
TeamTitle:hud_message_device = hud_message_device{}
@editable # Permet d'afficher la description d'assignation de l'équipe.
TeamDescription:hud_message_device = hud_message_device{}
@editable # Permet de s'abonner aux événements d'élimination des membres de l'équipe (équipe des accessoires) ou des ennemis (équipe des chasseurs).
TeamManager:team_settings_and_inventory_device = team_settings_and_inventory_device{}
# Voici une matrice des agents de l'équipe.
var TeamAgents<private>:[]agent = array{}
# Cet événement est déclenché lorsque la matrice TeamAgents est vide (signal de fin de manche).
TeamEmptyEvent:event() = event(){}
# Renvoyer la matrice TeamAgents actuelle.
# Cela est nécessaire en raison du caractère privé de la matrice TeamAgents, qui fait que les autres classes ne peuvent pas y accéder directement.
GetAgents()<decides><transacts>:[]agent =
TeamAgents
# Renvoyer la taille de la matrice TeamAgents
# Une fonction est nécessaire ici en raison du caractère privé de la matrice TeamAgents, qui fait que les autres classes ne peuvent pas y accéder directement.
Count()<transacts>:int =
TeamAgents.Length
# Renvoie un index dans la matrice TeamAgents d'un agent, sinon échoue.
FindOnTeam(Agent:agent)<decides><transacts>: int =
Index := TeamAgents.Find[Agent]
# Attribuer l'agent à l'équipe et notifier le joueur.
InitializeAgent(Agent:agent):void =
AddAgentToTeam(Agent)
ClassSelector.ChangeTeamAndClass(Agent)
DisplayTeamInformation(Agent)
# Ajouter un agent à TeamAgents.
AddAgentToTeam(AgentToAdd:agent):void =
if (not FindOnTeam[AgentToAdd]):
Logger.Print("Adding agent to team.")
set TeamAgents += array{AgentToAdd}
# Activer les appareils de message d'ATH pour informer le joueur de l'équipe dont il fait partie
DisplayTeamInformation(Agent:agent):void =
TeamTitle.Show(Agent)
TeamDescription.Show(Agent)
# Lorsqu'un agent quitte la partie, le retirer de la matrice TeamAgents et vérifier si la manche est terminée.
EliminateAgent(Agent:agent)<suspends>:void =
Sleep(0.0) # Retarder 1 tick de jeu pour s'assurer de la réapparition du joueur avant de continuer.
RemoveAgentFromTeam(Agent)
# Retirer un agent de TeamAgents.
# Si l'agent retiré était le dernier, déclencher TeamEmptyEvent.
RemoveAgentFromTeam(AgentToRemove:agent):void =
set TeamAgents = TeamAgents.RemoveAllElements(AgentToRemove)
Logger.Print("{Count()} agent(s) on team remaining.")
if (Count() < 1):
Logger.Print("No agents on team remaining. Ending the round.")
TeamEmptyEvent.Signal()
hunter_team.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Verse.org/Simulation }
# La classe hunter_team, qui hérite de la classe base_team, contient les définitions et fonctions d'appareil relatives à l'équipe des chasseurs et à ses agents.
hunter_team := class<concrete>(base_team):
@editable # Un agent de chasseur est créé à chaque manche pour un nombre n de joueurs. Exemple : HunterTeamPerNumberOfPlayers = 5,0 correspond à 1 pour 5 joueurs. Si 6 joueurs sont présents, 2 agents de chasseur sont créés.
HunterAgentPerNumberOfPlayers:float = 5.0 # Le minimum de 1,1 est forcé pour assurer la création d'au moins 1 agent d'accessoire.
@editable # Le nombre de secondes avant l'apparition des agents de chasseur, pour laisser le temps aux agents d'accessoire de se cacher.
SpawnDelay:float = 15.0
@editable # Le maximum de points de base qu'un agent de chasseur reçoit pour l'élimination d'un agent d'accessoire. Ces points sont divisés par le nombre d'agents d'accessoire restants.
MaxEliminationScore:int = 5000
@editable # Le chronomètre accorde aux accessoires le temps nécessaire pour se cacher.
WaitTimer:timer_device = timer_device{}
# Attribuer l'agent à un agent de chasseur.
InitializeAgent<override>(NewHunterAgent:agent):void =
Logger.Print("Setting a new hunter agent.")
(super:)InitializeAgent(NewHunterAgent)
# Lorsqu'un agent de chasseur quitte la partie, le retirer de la matrice HunterAgents et vérifier si la manche est terminée.
# Notez que cette fonction est contournée dans la mesure où il n'est pas nécessaire de transmettre de données supplémentaires comme c'est le cas pour l'équipe des accessoires.
EliminateAgent<override>(HunterAgent:agent)<suspends>:void =
Logger.Print("Hunter agent eliminated.")
(super:)EliminateAgent(HunterAgent)
prop_team.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 }
# Ce message permet de rendre visible le nombre d'accessoires restants à tous les joueurs au cours d'une manche.
PropAgentsRemainingMessage<localizes>(Count:int):message = "{Count} Prop(s) Remaining"
# La classe prop_team, qui hérite de la classe base_team, contient les définitions et fonctions d'appareil relatives à l'équipe des accessoires et à ses agents.
# Elle contient notamment le comportement de battement de cœur d'un agent d'accessoire.
prop_team := class<concrete>(base_team):
@editable # Le score que reçoit un agent d'accessoire, par seconde.
ScorePerSecond:int = 10
@editable # La distance minimale qu'un agent d'accessoire doit parcourir pour réinitialiser le chronomètre de battement de cœur.
MinimumMoveDistance:float = 100.0
@editable # Le chronomètre qui permet d'attribuer un score à un accessoire.
ScoreTimer:timer_device = timer_device{}
@editable # Ce traqueur permet d'afficher les accessoires restants.
PropsRemainingTracker:tracker_device = tracker_device{}
@editable # Obtenir les propriétés de la classe heart_beat.
HeartBeat:heart_beat = heart_beat{}
# Attribuer l'agent à un agent d'accessoire et assigner l'IU d'avertissement de battement de cœur.
InitializeAgent<override>(NewPropAgent:agent):void =
Logger.Print("Setting a new prop agent.")
(super:)InitializeAgent(NewPropAgent)
# Attribuer des points à tous les agents d'accessoire à la fin du PropScoreTimer. PropInstigator, nécessaire pour l'abonnement à un événement, n'est pas utilisé.
OnPropsScore(PropInstigator:?agent):void =
if (PropAgents := GetAgents[]):
for (PropAgent : PropAgents):
ScoreManager.Activate(PropAgent)
# Lorsqu'un agent d'accessoire est éliminé ou quitte la partie, le retirer de la matrice PropAgents et vérifier si la manche est terminée.
# Notez qu'il ne s'agit pas d'un contournement dans la mesure où nous transmettons tous les joueurs à la fonction, afin de mettre à jour le message d'accessoires restants.
EliminateAgent<override>(PropAgent:agent)<suspends>:void =
Logger.Print("Prop agent eliminated.")
(super:)EliminateAgent(PropAgent)
# Mettre à jour le nombre d'accessoires restants.
UpdatePropsRemainingTracker()
# Mettre à jour la valeur du traqueur qui indique le nombre d'accessoires restants.
UpdatePropsRemainingTracker():void =
PropsRemainingTracker.SetValue(Count())
# Si l'agent d'accessoire s'arrête de bouger, utiliser race pour voir si l'agent d'accessoire se déplace au-delà du paramètre MinimumMoveDistance, si le chronomètre de battement de cœur se termine, ou si l'agent d'accessoire est éliminé.
RunPropGameLoop(PropAgent:agent)<suspends>:void =
Logger.Print("Starting prop agent game loop.")
# Mettre le comportement de l'accessoire en boucle infinie jusqu'à ce que l'agent d'accessoire soit éliminé ou que le joueur quitte la session.
race:
PropAgent.AwaitNoLongerAProp()
loop:
# Attendre que l'agent d'accessoire se déplace à une distance inférieure à la distance minimale, puis avancer.
PropAgent.AwaitStopMoving(MinimumMoveDistance)
# Compte à rebours jusqu'au battement de cœur en attendant que l'agent d'accessoire dépasse la distance minimale, puis déclenchement indéfini du battement de cœur.
race:
PropAgent.AwaitStartMoving(MinimumMoveDistance)
block:
CountdownTimer(PropAgent)
PropAgent.StartHeartbeat()
Sleep(0.0) # Une fois la fonction race terminée (déplacement de l'agent d'accessoire), relancer la boucle.
# Mettre en boucle jusqu'à ce que l'agent d'accessoire ne fasse plus partie de la matrice PropAgents. Le retrait a lieu si l'agent d'accessoire est éliminé et transformé en chasseur ou si le joueur quitte la session.
(PropAgent:agent).AwaitNoLongerAProp()<suspends>:void =
loop:
if (not FindOnTeam[PropAgent]):
Logger.Print("Cancelling prop agent behavior.")
break
Sleep(0.0) # Passer au tick de jeu suivant.
# Mettre en boucle jusqu'à ce que l'agent se déplace à une distance inférieure à la distance minimale.
(PropAgent:agent).AwaitStopMoving(MinimumDistance:float)<suspends>:void =
Logger.Print("Checking if the agent has moved less than the minimum distance.")
# Obtenir la position initiale de l'agent à partir du personnage de l'agent dans la scène.
if (Tracked := PropAgent.GetFortCharacter[]):
var StartPosition:vector3 = Tracked.GetTransform().Translation
loop:
Sleep(0.0) # Obtenir la position de l'agent au prochain tick de jeu.
NewPosition := Tracked.GetTransform().Translation
# Si la distance entre la nouvelle position et la position de départ est inférieure au paramètre MinimumDistance, l'agent n'a pas bougé et la boucle est interrompue.
if (Distance(StartPosition, NewPosition) < MinimumDistance):
Logger.Print("Agent has moved less than the minimum distance.")
break
# Sinon, réinitialiser StartPosition pour s'assurer que le joueur part de la nouvelle position.
else:
set StartPosition = NewPosition
# Mettre en boucle jusqu'à ce que l'agent se déplace à une distance supérieure à la distance minimale.
(PropAgent:agent).AwaitStartMoving(MinimumDistance:float)<suspends>:void =
Logger.Print("Checking if the agent moves further than the minimum distance.")
# Obtenir la position initiale de l'agent à partir du personnage de l'agent dans la scène.
if (Tracked := PropAgent.GetFortCharacter[]):
StartPosition:vector3 = Tracked.GetTransform().Translation
loop:
Sleep(0.0) # Obtenir la position de l'agent au prochain tick de jeu.
NewPosition := Tracked.GetTransform().Translation
# Si la distance entre la nouvelle position et la position de départ est supérieure ou égale à MinimumDistance, l'agent a bougé et la boucle est interrompue.
if (Distance(StartPosition, NewPosition) >= MinimumDistance):
Logger.Print("Agent has moved more than or equal to the minimum distance.")
break
# Retarder jusqu'au déclenchement de HeartBeatWarningTime. Puis compte à rebours selon HeartBeatWarningTime et définition du texte de compte à rebours. Effacer le texte en cas de report.
CountdownTimer(PropAgent:agent)<suspends>:void =
Logger.Print("Starting heart beat countdown.")
if (UIData := HeartBeat.WarningUI[PropAgent]):
Sleep(HeartBeat.MoveTime - HeartBeat.WarningTime) # Veille pendant la durée précédant l'affichage de l'avertissement.
Logger.Print("Starting heart beat warning.")
var WarningTimeRemaining:int = 0
if (set WarningTimeRemaining = Ceil[HeartBeat.WarningTime]) {}
# Un report (defer) a lieu lorsque la fonction se termine ou est annulée, par exemple si elle perd une course (race).
# Le texte d'avertissement est alors effacé à la fin du compte à rebours ou si l'agent d'accessoire se déplace avant la fin du compte à rebours.
defer:
UIData.Text.SetText(HeartBeatWarningClear)
# Définir le texte d'avertissement sur le temps restant, attendre une seconde, puis réduire le temps restant. Si le compte à rebours se termine, interrompre la boucle.
loop:
Logger.Print("Heart beat in {WarningTimeRemaining} seconds.")
UIData.Text.SetText(HeartBeatWarningMessage(WarningTimeRemaining))
Sleep(1.0)
set WarningTimeRemaining -= 1
if (WarningTimeRemaining <= 0):
break
else:
Logger.Print("UIData not found.")
# Activer les effets visuels et sonores de battement de cœur. Attendre indéfiniment jusqu'au report, puis désactiver les effets visuels et sonores de battement de cœur.
(PropAgent:agent).StartHeartbeat()<suspends>:void =
Logger.Print("Spawning heart beat.")
# Enregistrer les données de battement de cœur pour pouvoir les transmettre ultérieurement au report, après destruction ou départ du PropAgent.
var HeartBeatVFXData:heartbeat_vfx = heartbeat_vfx{}
if:
# Obtenir l'index de l'agent d'accessoire dans la matrice PropAgents pour accéder ensuite à l'acteur d'effets visuels de battement de cœur correspondant.
Index := FindOnTeam[PropAgent]
set HeartBeatVFXData = HeartBeat.AgentVFX[Index]
then:
HeartBeat.Enable(PropAgent, HeartBeatVFXData)
# Désactiver le battement de cœur lorsque le déplacement ou l'élimination de l'agent de joueur annule cette fonction (ou lorsque le joueur quitte la session).
defer:
HeartBeat.Disable(PropAgent, HeartBeatVFXData)
Sleep(Inf) # N'arrêter la veille qu'une fois la course terminée.
round_timer.verse
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_round_timer_device := class(log_channel){}
# Int qui autorise la spécification des valeurs entre les plages. player_ui_slot.ZOrder nécessite ce type.
round_int_clamped := type{_X:int where 0 <= _X, _X <= 2147483647}
# Ce message permet d'afficher le temps restant avant la fin d'une manche.
TimeRemainingMessage<localizes>(Minutes:string, Seconds:string):message = "{Minutes}:{Seconds}"
<#
Cette classe contient toute la logique de gestion et d'affichage du temps de manche.
Vous pouvez utiliser cet appareil avec un round_settings_device pour mettre fin à la manche.
Cet appareil gère le temps sans recourir à un chronomètre.
Pour utiliser cette classe :
1) Ajouez le fichier à votre projet.
2) Compilez le code Verse à partir du menu Verse de la barre d'outils.
3) Faites glisser l'appareil sur votre île à partir du dossier Contenu/Appareils du mode Créatif (Content/Creative Devices) de votre île dans le navigateur de contenu.
4) Incluez la classe waiting_for_more_players dans un autre script Verse avec :
@editable
RoundTimer:round_timer = round_timer{}
5) Compilez le code Verse à partir du menu Verse de la barre d'outils.
6) Reliez l'appareil de l'étape 3 à l'appareil Verse.
7) Lancez le chronomètre de manche à l'aide de la commande Verse :
RoundTimer.Start()
8) Relancez ou arrêtez le chronomètre avec les fonctions équivalentes.
9) Attendez le déclenchement du chronomètre avec :
RoundTimer.AwaitStart()
10) Attendez la fin du chronomètre avec :
RoundTimer.AwaitEnd()
Appeler la fonction EndRound sur un round_settings_device pour mettre fin à la manche.
#>
round_timer := class(creative_device):
Logger:log = log{Channel:=log_prop_hunt_device}
@editable # Durée d'une manche, en minutes.
RoundTimeInMinutes:float = 5.0
@editable # Les positions horizontale et verticale de l'IU de chronomètre à l'écran. X 0-1 est de gauche à droite, et Y 0-1 est de haut en bas.
UIPosition:vector2 = vector2{X:= 0.98, Y:=0.13}
@editable # Les positions horizontale et verticale de l'IU de chronomètre à l'écran. X 0-1 est de gauche à droite, et Y 0-1 est de haut en bas.
UIAlignment:vector2 = vector2{X := 1.0, Y := 0.0}
@editable # Le ZOrder de l'IU par rapport aux autres éléments d'IU.
UIZOrder:round_int_clamped = 4
# Signal au déclenchement de la manche.
RoundStarted:event() = event(){}
# Signal lorsque la manche est sur le point de se terminer.
RoundEndedEvent:event() = event(){}
# Ce mappage associe à chaque joueur une boîte de dialogue d'affichage du temps.
var TimeRemainingTextBlocks:[player]text_block = map{}
# Temps restant avant la fin de la manche, sous forme d'entier.
var TimeRemainingInSeconds:int = 0
# Attendre le déclenchement du chronomètre de manche.
AwaitStart()<suspends>:void =
RoundStarted.Await()
Logger.Print("Round timer started.")
# Permet de déclencher le chronomètre de manche.
Start():void =
Logger.Print("Starting the round timer.")
RoundStarted.Signal()
set TimeRemainingInSeconds = GetRoundTimeInSeconds()
spawn{ Running() }
# Redéclencher le chronomètre sur RoundTime
Restart():void =
Logger.Print("Restarting the round timer.")
set TimeRemainingInSeconds = GetRoundTimeInSeconds()
# Exécuter la logique de chronomètre.
Running()<suspends>:void =
Logger.Print("Round timer running.")
loop:
UpdateTimeUI()
Sleep(1.0)
# Décrementer TimeRemaining de 1 seconde, puis vérifier si le temps est écoulé. Si c'est le cas, mettre fin à la manche.
set TimeRemainingInSeconds -= 1
if (TimeRemainingInSeconds < 0):
Stop()
break
# Arrêter le chronomètre et mettre fin à la manche.
Stop():void =
Logger.Print("Ending the round timer.")
# Nous obtenons un joueur des joueurs restants dans la scène pour mettre fin à la manche.
Players:[]player = GetPlayspace().GetPlayers()
if (Instigator := Players[0]):
RoundEndedEvent.Signal()
# Attendre jusqu'à l'imminence de la fin du chronomètre de manche.
AwaitEnd()<suspends>:void =
RoundEndedEvent.Await()
Logger.Print("Round timer ended.")
# Accepter une valeur de temps en minutes et renvoyer la valeur en secondes.
GetRoundTimeInSeconds():int =
var InSeconds:int = 0
if (set InSeconds = Round[RoundTimeInMinutes * 60.0]) {}
InSeconds
# À la fin du chronomètre, mettre à jour le temps restant et vérifier s'il a expiré.
UpdateTimeUI():void =
# Attribuer les minutes de TimeRemainingInSeconds/60 sans le reste.
var Minutes:int = 0
if (set Minutes = Floor(TimeRemainingInSeconds / 60)) {}
# Attribuer les secondes au reste de TimeRemainingInSeconds/60.
var Seconds:int = 0
if (set Seconds = Mod[TimeRemainingInSeconds, 60]) {}
# Convertir les minutes et les secondes en chaînes.
MinutesAsString := string("{Minutes}")
# Si les secondes sont < 10, nous devons ajouter un 0 devant la valeur pour qu'elle s'affiche sous la forme :0# au lieu de :#
SecondsAsString := if (Seconds < 10) then Join(array{string("{0}"),string("{Seconds}")},string()) else string("{Seconds}")
# Itérer sur tous les joueurs, vérifier s'ils disposent d'un TimeRemainingTextBlock, leur en attribuer un si ce n'est pas le cas. Puis mettre à jour le texte.
Players:[]player = GetPlayspace().GetPlayers()
for (Player : Players):
var TextBlock:text_block = text_block{}
if (set TextBlock = TimeRemainingTextBlocks[Player]) {}
else:
set TextBlock = SetUpTimeRemainingUI(Player)
TextBlock.SetText(TimeRemainingMessage(MinutesAsString, SecondsAsString))
# Accepter un joueur puis ajouter un canevas d'iu de temps de manche à son écran et stocker son TimeRemainingTextBlock pour mise à jour ultérieure.
SetUpTimeRemainingUI(Player:player):text_block =
Logger.Print("Adding round timer UI to a player.")
# Il s'agit du text_block qui affiche le texte de temps restant.
TextBlock:text_block = text_block:
DefaultTextColor := NamedColors.White
DefaultShadowColor := NamedColors.Black
if (PlayerUI := GetPlayerUI[Player]):
if (set TimeRemainingTextBlocks[Player] = TextBlock) {}
# Il s'agit du canevas qui détient le text_block et le positionne à l'écran.
Canvas := canvas:
Slots := array:
canvas_slot:
Anchors := anchors{Minimum := UIPosition, Maximum := UIPosition}
Offsets := margin{Top := 0.0, Left := 0.0, Right := 0.0, Bottom := 0.0}
Alignment := UIAlignment
SizeToContent := true
Widget := TextBlock
# Le canevas est assigné au joueur.
PlayerUI.AddWidget(Canvas, player_ui_slot{ZOrder := UIZOrder})
# Le text_block est renvoyé de manière à pouvoir être enregistré dans le mappage et mis à jour ultérieurement à mesure que le compte à rebours défile.
return TextBlock
waiting_for_more_players.verse
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_waiting_for_more_players_device := class(log_channel){}
# Int qui autorise la spécification des valeurs entre les plages. player_ui_slot.ZOrder nécessite ce type.
waiting_int_clamped := type{_X:int where 0 <= _X, _X <= 2147483647}
# Ce message permet d'afficher le nombre de joueurs nécessaire avant qu'une manche puisse commencer.
WaitingForMorePlayersMessage<localizes>(Count:int):message = "Waiting for {Count} more Player(s)"
# Cette classe permet de montrer combien de joueurs sont nécessaires pour commencer la manche.
waiting_for_more_players_ui := class:
var Canvas:canvas
var TextBlock:text_block
<#
Cette classe contient toute la logique de définition d'un nombre minimal de joueurs et de vérification de leur présence pour lancer la manche.
Pour utiliser cette classe :
1) Ajouez le fichier à votre projet.
2) Compilez le code Verse à partir du menu Verse de la barre d'outils.
3) Faites glisser l'appareil sur votre île à partir du dossier Contenu/Appareils du mode Créatif (Content/Creative Devices) de votre île dans le navigateur de contenu.
4) Reliez un chronomètre à la propriété "WaitingForMorePlayersTimer" de cet appareil.
5) Incluez la classe waiting_for_more_players dans un autre script Verse avec :
@editable
WaitingForMorePlayers:waiting_for_more_players = waiting_for_more_players{}
6) Compilez le code Verse à partir du menu Verse de la barre d'outils.
7) Reliez l'appareil de l'étape 3 à l'appareil Verse et à la propriété exposée à l'étape 6.
8) Attendez la fonction CheckForMinimumNumberOfPlayers en la transmettant à un joueur. Par exemple :
Players = GetPlayspace().GetPlayers()
CheckForMinimumNumberOfPlayers(Players)
9) Dans IslandSettings (paramètres de l'île), définissez le compte à rebours de début de jeu à 0,0.
#>
waiting_for_more_players := class(creative_device):
Logger:log = log{Channel:=log_waiting_for_more_players_device}
@editable # Le nombre minimum de joueurs nécessaire dans la partie pour que la manche commence.
MinimumNumberOfPlayers:int = 2
@editable # Les positions horizontale et verticale de l'IU de chronomètre à l'écran. X 0-1 est de gauche à droite, et Y 0-1 est de haut en bas.
UIPosition:vector2 = vector2{X:= 0.5, Y:=0.4}
@editable # Les positions horizontale et verticale de l'IU de chronomètre à l'écran. X 0-1 est de gauche à droite, et Y 0-1 est de haut en bas.
UIAlignment:vector2 = vector2{X := 0.5, Y := 0.5}
@editable # Le ZOrder de l'IU par rapport aux autres éléments d'IU.
UIZOrder:waiting_int_clamped = 3
@editable # Ce chronomètre est un compte à rebours avant le début de la manche après avoir attendu que des joueurs rejoignent la partie.
WaitingForMorePlayersTimer:timer_device = timer_device{}
# Ce mappage associe à chaque joueur un canevas d'IU pour l'affichage du nombre de joueurs nécessaire au lancement de la manche.
var WaitingForMorePlayersUI:[player]?waiting_for_more_players_ui = map{}
# Vérifier qu'assez de joueurs sont présents pour lancer la manche. Si ce n'est pas le cas, attendre que le nombre de joueurs soit >= MinimumNumberOfPlayers.
WaitForMinimumNumberOfPlayers(Players:[]player)<suspends>:[]player =
Logger.Print("Waiting if there are enough players for the round to start.")
# Création d'une nouvelle variable modifiable selon l'arrivée des joueurs. Initialisation à l'aide d'une matrice de joueurs transmise à la fonction.
var PlayersWaiting:[]player = Players
# Si le nombre de joueurs est inférieur au minimum nécessaire au lancement de la manche...
if (PlayersWaiting.Length < MinimumNumberOfPlayers):
loop: # Mettre en boucle jusqu'à ce que le nombre de joueurs soit supérieur ou égal au minimum nécessaire.
Logger.Print("{PlayersWaiting.Length}/{MinimumNumberOfPlayers} players needed for the round to start.")
# Mettre à jour l'IU d'attente de joueurs.
DisplayWaitingForMorePlayers(PlayersWaiting)
Sleep(2.0) # Attendre de voir si d'autres joueurs rejoignent la partie, puis vérifier qu'assez de joueurs sont présents pour lancer la manche.
set PlayersWaiting = GetPlayspace().GetPlayers()
if (PlayersWaiting.Length >= MinimumNumberOfPlayers):
# Si assez de joueurs sont présents, supprimer l'IU d'attente de joueurs,
Logger.Print("{PlayersWaiting.Length}/{MinimumNumberOfPlayers} players in round, preparing for round start.")
ClearWaitingForMorePlayers()
# Puis sortir de la boucle.
break
# lancer le compte à rebours de début de manche, puis attendre la fin du compte à rebours.
WaitingForMorePlayersTimer.Start()
WaitingForMorePlayersTimer.SuccessEvent.Await()
Logger.Print("Starting round.")
# Renvoyer la liste des joueurs de la session.
return PlayersWaiting
# Afficher un message d'IU "En attente d'autres joueurs" pour chaque joueur qui n'en dispose pas déjà. Mettre à jour le compteur de joueurs pour tous les joueurs.
DisplayWaitingForMorePlayers(Players:[]player):void =
PlayersNeededCount := MinimumNumberOfPlayers - Players.Length
Logger.Print("{Players.Length} players in round, waiting for {PlayersNeededCount} more player(s) to join.")
for (Player : Players):
# Si le joueur dispose d'une WaitingForMorePlayersUI, obtenir le TextBlock et actualiser le texte de manière à ce qu'il affiche le bon nombre de joueurs nécessaire pour lancer la manche.
if (UIData := WaitingForMorePlayersUI[Player]?):
UIData.TextBlock.SetText(WaitingForMorePlayersMessage(PlayersNeededCount))
# Sinon, créer une WaitingForMorePlayersUI pour le joueur.
else:
SetUpWaitingForMorePlayersUI(Player, PlayersNeededCount)
# Accepter un joueur et une player_ui et ajouter un canevas d'iu d'attente de joueurs à son écran.
SetUpWaitingForMorePlayersUI(Player:player, PlayersNeededCount:int):void =
Logger.Print("Creating 'waiting for more players' UI.")
if (PlayerUI := GetPlayerUI[Player]):
# Il s'agit du text_block qui affiche le texte d'attente d'autres joueurs.
TextBlock:text_block = text_block:
DefaultText := WaitingForMorePlayersMessage(PlayersNeededCount)
DefaultTextColor := NamedColors.White
DefaultShadowColor := NamedColors.Black
# Il s'agit du canevas qui détient le text_block et le positionne à l'écran.
Canvas := canvas:
Slots := array:
canvas_slot:
Anchors := anchors{Minimum := UIPosition, Maximum := UIPosition}
Offsets := margin{Top := 0.0, Left := 0.0, Right := 0.0, Bottom := 0.0}
Alignment := UIAlignment
SizeToContent := true
Widget := TextBlock
# Le canevas est assigné au joueur.
PlayerUI.AddWidget(Canvas, player_ui_slot{ZOrder := UIZOrder})
# Le text_block est enregistré dans le mappage de manière à pouvoir actualiser le texte ultérieurement au fur et à mesure que des joueurs rejoignent le jeu.
if (set WaitingForMorePlayersUI[Player] = option{ waiting_for_more_players_ui{Canvas := Canvas, TextBlock := TextBlock} }) {}
# Supprimer le message d'IU "En attente d'autres joueurs" pour chaque joueur qui en dispose.
ClearWaitingForMorePlayers():void =
Logger.Print("Clearing 'waiting for more players' UI.")
for (Player -> UIData : WaitingForMorePlayersUI):
if:
PlayerUI := GetPlayerUI[Player]
Canvas := UIData?.Canvas
set WaitingForMorePlayersUI[Player] = false
then:
PlayerUI.RemoveWidget(Canvas)