Questa sezione include il codice completo da aggiungere ai file Verse che hai creato.
Codice completo
In questo progetto sono presenti più file di Verse.
-
heartbeat.verse: Per il codice completo del file, vedi di seguito.
-
base_team.verse: Per il codice completo del file, vedi di seguito.
-
hunter_team.verse: Per il codice completo del file, vedi di seguito.
-
prop_team.verse: Per il codice completo del file, vedi di seguito.
-
round_timer.verse: Per il codice completo del file, vedi di seguito.
-
waiting_for_more_players.verse: Per il codice completo del file, vedi di seguito.
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){}
# Questi messaggi vengono utilizzati per notificare a un agente oggetto scenografico un messaggio (o per nasconderlo) quando deve muoversi per evitare che il suo battito cardiaco diventi visibile.
HeartBeatWarningMessage<localizes>(Time:int):message = "Battito cardiaco tra {Time} secondi. Muoviti!"
HeartBeatWarningClear<localizes>:message = ""
# Questa classe ha esposto le proprietà modificabili del battito cardiaco al dispositivo prop_hunt.
heart_beat := class<concrete>():
Logger:log = log{Channel:=log_heart_beat}
@editable # Il numero di secondi prima che un agente oggetto scenografico si muova prima che il battito cardiaco riveli la sua posizione.
MoveTime:float = 15.0
@editable # I secondi rimanenti prima che venga visualizzato l'avviso del battito cardiaco. Non deve essere > di HeartBeatTimer.
WarningTime:float = 5.0
@editable # Un array di dispositivi VFX del battito cardiaco. Ce n'è uno per ogni giocatore.
AgentVFX:[]heartbeat_vfx = array{}
@editable # Il dispositivo riproduttore audio è utilizzato per riprodurre gli effetti sonori del battito cardiaco (SFX).
SFXPlayer:radio_device = radio_device{}
# Questa mappa associa un'UI per la visualizzazione dell'avviso del battito cardiaco a ogni agente oggetto scenografico.
var WarningUI:[agent]heartbeat_warning_ui = map{}
# Tiene traccia di quanti giocatori hanno un battito cardiaco attivo in modo da poter gestire il dispositivo SFX.
var NumberOfHeartBeats:int = 0
# Imposta l'UI del battito cardiaco per l'agente.
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) {}
# Attiva i VFX e gli SFX del battito cardiaco per il giocatore specificato.
Enable(PropAgent:agent, HeartBeatVFXData:heartbeat_vfx):void =
if:
# Ottieni il personaggio, che viene utilizzato per trovare la posizione dell'agente oggetto scenografico nella scena.
Character := PropAgent.GetFortCharacter[]
then:
# Imposta la posizione del battito del cuore di VFX sulla posizione dell'agente oggetto scenografico.
HeartBeatVFXData.Activate(Character.GetTransform())
# Aumenta il conteggio del battito cardiaco e, se questo è il primo battito in riproduzione, dobbiamo riprodurre il suono per farlo partire.
set NumberOfHeartBeats += 1
if (NumberOfHeartBeats = 1) then SFXPlayer.Play()
# Registra l'agente oggetto scenografico nel dispositivo riproduttore audio in modo che l'audio del battito cardiaco venga riprodotto da quella posizione.
SFXPlayer.Register(PropAgent)
else:
Logger.Print("Character, Index, o HeartBeatVFXData non trovati. Impossibile avviare il battito cardiaco")
# Cancella i VFX e gli SFX del battito cardiaco per l'agente oggetto scenografico specificato.
Disable(PropAgent:agent, HeartBeatVFXData:heartbeat_vfx):void =
Logger.Print("Disabilitazione battito cardiaco.")
# Disattiva la grafica VFX.
HeartBeatVFXData.Deactivate()
# Annulla la registrazione dell'agente oggetto scenografico dal dispositivo riproduttore audio, causando l'interruzione dell'audio del battito cardiaco.
SFXPlayer.Unregister(PropAgent)
# Diminuisci il contatore dei battiti cardiaci. Questo contatore non deve mai scendere sotto lo 0.
set NumberOfHeartBeats -= 1
if (NumberOfHeartBeats < 0) then set NumberOfHeartBeats = 0
# Cancella tutti i VFX e gli SFX del battito cardiaco per tutti gli agenti oggetti scenografici.
DisableAll():void =
Logger.Print("Disabilitazione di tutti i battiti cardiaci.")
# Scorri tutti i VFX e spostali a 0,0,0.
for (HeartBeatVFXDevice : AgentVFX):
HeartBeatVFXDevice.Deactivate()
# Annulla la registrazione di tutti i giocatori dal suono del battito cardiaco.
SFXPlayer.UnregisterAll()
# Reinizializza il contatore del battito cardiaco a 0
set NumberOfHeartBeats = 0
# La classe heartbeat_warning_ui contiene una struttura di dati per tenere traccia del canvas UI e del text_block per giocatore, nonché la funzione per creare un nuovo canvas UI per l'avviso del battito cardiaco.
heartbeat_warning_ui := class:
var Canvas:canvas = canvas{}
var Text:text_block = text_block{}
# Crea il canvas UI per il messaggio di avviso.
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 := Testo
# La classe heartbeat_vfx contiene una struttura di dati per tracciare la radice del VFX e gli oggetti vfx_spawner_device per giocatore, nonché le funzioni per impostare il VFX in una posizione o resettarlo.
heartbeat_vfx := class<concrete>:
@editable # Il dispositivo VFX per ogni battito cardiaco.
VFXDevice:vfx_spawner_device = vfx_spawner_device{}
# Questo offset viene utilizzato per posizionare il battito cardiaco sopra la testa dell'agente oggetto scenografico.
HeartBeatVFXOffset:vector3 = vector3{X := 0.0, Y := 0.0, Z := 110.0}
# Imposta la posizione del VFX del battito cardiaco e lo attiva.
Activate(Transform:transform):void =
VFXPosition := Transform.Translation + HeartBeatVFXOffset
if (VFXDevice.TeleportTo[VFXPosition, Transform.Rotation]):
VFXDevice.Enable()
# Disabilita VFX, nascondendo la grafica del battito cardiaco.
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){}
# Questa classe definisce i dispositivi necessari per i diversi team dell'esperienza.
# Questa classe è astratta e non può essere utilizzata da sola. Deve essere ereditato da un'altra classe.
base_team := class<abstract>:
Logger:log = log{Channel:=log_team}
@editable # Serve a impostare un giocatore nel team.
ClassSelector:class_and_team_selector_device = class_and_team_selector_device{}
@editable # Serve ad assegnare un punteggio agli agenti del team.
ScoreManager:score_manager_device = score_manager_device{}
@editable # Serve a visualizzare il titolo dell'assegnazione al team.
TeamTitle:hud_message_device = hud_message_device{}
@editable # Serve a visualizzare la descrizione dell'assegnazione al team.
TeamDescription:hud_message_device = hud_message_device{}
@editable # Serve a iscriversi agli eventi di eliminazione di un membro del team (team oggetti scenografici) o di un nemico (team cacciatori).
TeamManager:team_settings_and_inventory_device = team_settings_and_inventory_device{}
# È un array di agenti nel team.
var TeamAgents<private>:[]agent = array{}
# Questo evento viene segnalato quando l'array TeamAgents diventa vuoto (segnalando la fine del round).
TeamEmptyEvent:event() = event(){}
# Restituisce l'array di TeamAgents attuale.
# È necessario perché l'array TeamAgents è privato, quindi le altre classi non possono accedervi direttamente.
GetAgents()<decides><transacts>:[]agent =
TeamAgents
# Restituisce la dimensione dell'array TeamAgents
# Richiede una funzione perché l'array TeamAgents è privato, quindi le altre classi non possono accedervi direttamente.
Count()<transacts>:int =
TeamAgents.Length
# Restituisce un indice nell'array TeamAgents di un agente, altrimenti ha esito negativo.
FindOnTeam(Agent:agent)<decides><transacts>: int =
Index := TeamAgents.Find[Agent]
# Imposta l'agente nel team e notifica il giocatore.
InitializeAgent(Agent:agent):void =
AddAgentToTeam(Agent)
ClassSelector.ChangeTeamAndClass(Agent)
DisplayTeamInformation(Agent)
# Aggiungi un agente a TeamAgents.
AddAgentToTeam(AgentToAdd:agent):void =
if (not FindOnTeam[AgentToAdd]):
Logger.Print("Aggiunta di un agente al team.")
set TeamAgents += array{AgentToAdd}
# Attiva i dispositivi di messaggio HUD per mostrare al giocatore in quale team si trova
DisplayTeamInformation(Agent:agent):void =
TeamTitle.Show(Agent)
TeamDescription.Show(Agent)
# Quando un agente lascia la partita, rimuovilo dall'array TeamAgents e controlla la fine del round.
EliminateAgent(Agent:agent)<suspends>:void =
Sleep(0.0) # Ritardo di 1 tick di gioco per assicurarsi che il giocatore sia ri-generato prima di procedere.
RemoveAgentFromTeam(Agent)
# Rimuovi un agente da TeamAgents.
# Se l'agente rimosso era l'ultimo, segnala TeamEmptyEvent.
RemoveAgentFromTeam(AgentToRemove:agent):void =
set TeamAgents = TeamAgents.RemoveAllElements(AgentToRemove)
Logger.Print("{Count()} agente/i rimasto/i nel team.")
if (Count() < 1):
Logger.Print("Non sono rimasti agenti nel team. Il round viene concluso.")
TeamEmptyEvent.Signal()
hunter_team.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Verse.org/Simulation }
# Ereditando dalla classe base_team, la classe hunter_team contiene le definizioni del dispositivo e le funzioni relative al team cacciatori e ai suoi agenti.
hunter_team := class<concrete>(base_team):
@editable # A ogni round viene creato un agente cacciatore ogni n giocatori. Esempio: HunterTeamPerNumberOfPlayers = 5.0 è 1 per 5 giocatori. Se giocatori = 6, vengono creati 2 agenti cacciatori.
HunterAgentPerNumberOfPlayers:float = 5.0 # Il minimo 1.1 viene applicato per garantire la creazione di almeno 1 agente oggetto scenografico.
@editable # Il numero di secondi prima della generazione degli agenti cacciatori, per dare agli agenti oggetti scenografici un vantaggio per nascondersi.
SpawnDelay:float = 15.0
@editable # I punti base massimi che un agente cacciatore riceve per l'eliminazione di un agente oggetto scenografico. Questi punti vengono divisi per il numero di agenti oggetti scenografici rimasti.
MaxEliminationScore:int = 5000
@editable # Il dispositivo timer viene utilizzato per dare agli oggetti scenografici un periodo di tolleranza per nascondersi.
WaitTimer:timer_device = timer_device{}
# Imposta l'agente come agente cacciatore.
InitializeAgent<override>(NewHunterAgent:agent):void =
Logger.Print("Impostazione di un nuovo agente cacciatore.")
(super:)InitializeAgent(NewHunterAgent)
# Quando un agente cacciatore lascia la partita, rimuovilo dall'array HunterAgents e controlla la fine del round.
# Tieni presente che stiamo sovrascrivendo questa funzione perché non abbiamo bisogno di passare dati aggiuntivi come invece accade per il team oggetti scenografici.
EliminateAgent<override>(HunterAgent:agent)<suspends>:void =
Logger.Print("Agente cacciatore eliminato.")
(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 }
# Questo messaggio viene utilizzato per stampare il numero di oggetti scenografici rimasti a tutti i giocatori durante un round.
PropAgentsRemainingMessage<localizes>(Count:int):message = "{Count} Prop(s) Remaining"
# Ereditando dalla classe base_team, la classe prop_team contiene le definizioni del dispositivo e le funzioni relative al team oggetti scenografici e ai suoi agenti.
# In particolare, il comportamento del battito cardiaco di un agente oggetto scenografico si trova in questa classe.
prop_team := class<concrete>(base_team):
@editable # Il punteggio che un agente oggetto scenografico riceve al secondo.
ScorePerSecond:int = 10
@editable # La distanza minima che un agente oggetto scenografico deve percorrere per reimpostare il timer del battito cardiaco.
MinimumMoveDistance:float = 100.0
@editable # Il dispositivo timer utilizzato per assegnare un punteggio a un oggetto scenografico.
ScoreTimer:timer_device = timer_device{}
@editable # Questo dispositivo tracciatore viene utilizzato per visualizzare sullo schermo gli oggetti scenografici rimasti.
PropsRemainingTracker:tracker_device = tracker_device{}
@editable # Ottieni le proprietà dalla classe heart_beat.
HeartBeat:heart_beat = heart_beat{}
# Imposta l'agente come agente oggetto scenografico e assegna l'UI dell'avviso del battito cardiaco.
InitializeAgent<override>(NewPropAgent:agent):void =
Logger.Print("Impostazione di un nuovo agente oggetto scenografico.")
(super:)InitializeAgent(NewPropAgent)
# Al termine del PropScoreTimer, assegna i punti a tutti gli agenti oggetti scenografici. PropInstigator è necessario per iscriversi all'evento, ma non viene utilizzato.
OnPropsScore(PropInstigator:?agent):void =
if (PropAgents := GetAgents[]):
for (PropAgent : PropAgents):
ScoreManager.Activate(PropAgent)
# Quando un agente oggetto scenografico viene eliminato o lascia la partita, rimuovilo dall'array PropAgents e controlla la fine del round.
# Tieni presente che non si tratta di un override, perché stiamo passando tutti i giocatori alla funzione per aggiornare il messaggio relativo agli oggetti scenografici rimanenti.
EliminateAgent<override>(PropAgent:agent)<suspends>:void =
Logger.Print("Agente oggetto scenografico eliminato.")
(super:)EliminateAgent(PropAgent)
# Aggiorna il numero degli oggetti scenografici rimanenti.
UpdatePropsRemainingTracker()
# Aggiorna il valore del dispositivo tracciatore mostrando il numero di oggetti scenografici rimasti.
UpdatePropsRemainingTracker():void =
PropsRemainingTracker.SetValue(Count())
# Se l'agente oggetto scenografico smette di muoversi, allora esegui un'espressione race per vedere se l'agente oggetto scenografico si muove oltre la MinimumMoveDistance, se il timer del battito cardiaco viene completato o se l'agente oggetto scenografico viene eliminato.
RunPropGameLoop(PropAgent:agent)<suspends>:void =
Logger.Print("Avvio loop di gioco dell'agente oggetto scenografico.")
# Esegui un loop infinito del comportamento dell'oggetto scenografico fino a quando l'agente oggetto scenografico viene eliminato o il giocatore abbandona la sessione.
race:
PropAgent.AwaitNoLongerAProp()
loop:
# Attendi che l'agente oggetto scenografico si muova a una distanza inferiore a quella minima, quindi avanza.
PropAgent.AwaitStopMoving(MinimumMoveDistance)
# Finché l'agente oggetto scenografico non si muove oltre la distanza minima, esegui il conto alla rovescia fino al battito cardiaco e poi lo riproduci all'infinito.
race:
PropAgent.AwaitStartMoving(MinimumMoveDistance)
block:
CountdownTimer(PropAgent)
PropAgent.StartHeartbeat()
Sleep(0.0) # Una volta completata l'espressione race (l'agente oggetto scenografico si muove), ricomincia il loop.
# Esegue un loop fino a quando l'agente oggetto scenografico non fa più parte dell'array PropAgents. La rimozione avviene se l'agente oggetto scenografico viene eliminato e trasformato in un cacciatore o se il giocatore abbandona la sessione.
(PropAgent:agent).AwaitNoLongerAProp()<suspends>:void =
loop:
if (not FindOnTeam[PropAgent]):
Logger.Print("Annullamento del comportamento dell'agente oggetto scenografico.")
break
Sleep(0.0) # Passa al prossimo tick di gioco.
# Esegue un loop finché l'agente non si muove a una distanza inferiore alla MinimumDistance.
(PropAgent:agent).AwaitStopMoving(MinimumDistance:float)<suspends>:void =
Logger.Print("Verifica se l'agente si è mosso di una distanza inferiore a quella minima.")
# Ottieni la posizione iniziale dell'agente dal personaggio dell'agente nella scena.
if (Tracked := PropAgent.GetFortCharacter[]):
var StartPosition:vector3 = Tracked.GetTransform().Translation
loop:
Sleep(0.0) # Ottieni la posizione dell'agente nel prossimo tick di gioco.
NewPosition := Tracked.GetTransform().Translation
# Se la distanza della nuova posizione dalla posizione di partenza è inferiore a MinimumDistance, l'agente non si è mosso e si interrompe il loop.
if (Distance(StartPosition, NewPosition) < MinimumDistance):
Logger.Print("L'agente si è mosso di una distanza inferiore a quella minima.")
break
# Altrimenti, reimpostiamo StartPosition per assicurarci che il giocatore si muova dalla nuova posizione.
else:
set StartPosition = NewPosition
# Esegue un loop finché l'agente non si muove più della MinimumDistance.
(PropAgent:agent).AwaitStartMoving(MinimumDistance:float)<suspends>:void =
Logger.Print("Verifica se l'agente si muove oltre la distanza minima.")
# Ottieni la posizione iniziale dell'agente dal personaggio dell'agente nella scena.
if (Tracked := PropAgent.GetFortCharacter[]):
StartPosition:vector3 = Tracked.GetTransform().Translation
loop:
Sleep(0.0) # Ottieni la posizione dell'agente nel prossimo tick di gioco.
NewPosition := Tracked.GetTransform().Translation
# Se la distanza della nuova posizione dalla posizione di partenza è maggiore o uguale alla MinimumDistance, l'agente si è mosso e si interrompe il loop.
if (Distance(StartPosition, NewPosition) >= MinimumDistance):
Logger.Print("L'agente si è mosso per una distanza pari o superiore a quella minima.")
break
# Ritarda fino a quando HeartBeatWarningTime deve avviarsi. Quindi esegui il conto alla rovescia per HeartBeatWarningTime e imposta il testo del conto alla rovescia. Cancella il testo in caso di differita.
CountdownTimer(PropAgent:agent)<suspends>:void =
Logger.Print("Avvio del conto alla rovescia del battito cardiaco.")
if (UIData := HeartBeat.WarningUI[PropAgent]):
Sleep(HeartBeat.MoveTime - HeartBeat.WarningTime) # Sospendi per il tempo necessario a far visualizzare l'avviso.
Logger.Print("Avvio dell'avviso del battito cardiaco.")
var WarningTimeRemaining:int = 0
if (set WarningTimeRemaining = Ceil[HeartBeat.WarningTime]) {}
# Un rinvio avviene quando la funzione viene completata o se viene annullata, ad esempio se perde un'espressione race.
# Quindi, in questo caso, il testo di avviso viene cancellato quando il conto alla rovescia termina o se l'agente oggetto scenografico si muove prima del termine del conto alla rovescia.
defer:
UIData.Text.SetText(HeartBeatWarningClear)
# Imposta il testo di avviso sul tempo rimanente, attendi un secondo e poi riduci il tempo rimanente. Se il conto alla rovescia viene completato, interrompi il loop.
loop:
Logger.Print("Battito cardiaco tra {WarningTimeRemaining} secondi.")
UIData.Text.SetText(HeartBeatWarningMessage(WarningTimeRemaining))
Sleep(1.0)
set WarningTimeRemaining -= 1
if (WarningTimeRemaining <= 0):
break
else:
Logger.Print("UIData non trovato.")
# Attiva i VFX e gli SFX del battito cardiaco. Attende all'infinito fino a quando non viene rimandata e poi disabilita i VFX e gli SFX del battito cardiaco.
(PropAgent:agent).StartHeartbeat()<suspends>:void =
Logger.Print("Generazione battito cardiaco.")
# Salva i dati del battito cardiaco in modo da poterli passare nell'espressione defer in un secondo momento, dopo che il PropAgent è stato distrutto o ha lasciato il gioco.
var HeartBeatVFXData:heartbeat_vfx = heartbeat_vfx{}
if:
# Ottieni l'indice dell'agente oggetto scenografico nell'array PropAgents per poi accedere all'attore VFX corrispondente al battito cardiaco.
Index := FindOnTeam[PropAgent]
set HeartBeatVFXData = HeartBeat.AgentVFX[Index]
then:
HeartBeat.Enable(PropAgent, HeartBeatVFXData)
# Quando questa funzione viene annullata dal movimento dell'agente oggetto scenografico, dall'eliminazione o dall'uscita del giocatore dalla sessione, disabilita il battito cardiaco.
defer:
HeartBeat.Disable(PropAgent, HeartBeatVFXData)
Sleep(Inf) # Non interrompere la sospensione fino al completamento dell'espressione race.
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){}
# Un int che consente valori compresi tra gli intervalli specificati. Questo tipo è necessario a player_ui_slot.ZOrder.
round_int_clamped := type{_X:int where 0 <= _X, _X <= 2147483647}
# Questo messaggio viene utilizzato per stampare il tempo rimanente prima della fine del round.
TimeRemainingMessage<localizes>(Minutes:string, Seconds:string):message = "{Minutes}:{Seconds}"
<#
Questa classe contiene l'intera logica per la gestione del tempo del round e la visualizzazione del tempo sullo schermo.
Puoi utilizzare questo dispositivo con un dispositivo round_settings_device per terminare effettivamente il round.
Questo dispositivo gestisce il tempo senza l'utilizzo di un timer.
Per utilizzare questa classe:
1) Aggiungi il file al tuo progetto.
2) Compila il codice Verse dal menu Verse della barra degli strumenti.
3) Trascina il dispositivo nella tua isola dalla cartella Content/Creative Devices in Esplora contenuti.
4) Includi la classe waiting_for_more_players in un altro script Verse insieme a:
@editable
RoundTimer:round_timer = round_timer{}
5) Compila il codice Verse dal menu Verse della barra degli strumenti.
6) Collega il dispositivo creato al passaggio 3 al dispositivo Verse.
7) Avvia il timer del round con il seguente Verse:
RoundTimer.Start()
8) Riavvia o arresta il timer con le funzioni equivalenti.
9) Attendi che il timer inizi con:
RoundTimer.AwaitStart()
10) Attendi che il timer termini con:
RoundTimer.AwaitEnd()
Chiama la funzione EndRound su un dispositivo round_settings_device per terminare effettivamente il round di gioco.
#>
round_timer := class(creative_device):
Logger:log = log{Channel:=log_prop_hunt_device}
@editable # Il tempo, in minuti, della durata di un round.
RoundTimeInMinutes:float = 5.0
@editable # La posizione orizzontale e verticale della UI del timer sullo schermo. X 0-1 è sinistra-destra e Y 0-1 è alto-basso.
UIPosition:vector2 = vector2{X:= 0.98, Y:=0.13}
@editable # La posizione orizzontale e verticale della UI del timer sullo schermo. X 0-1 è sinistra-destra e Y 0-1 è alto-basso.
UIAlignment:vector2 = vector2{X := 1.0, Y := 0.0}
@editable # ZOrder dell'UI rispetto agli altri elementi dell'UI.
UIZOrder:round_int_clamped = 4
# Viene segnalato quando il round è iniziato.
RoundStarted:event() = event(){}
# Viene segnalato quando il round sta per terminare.
RoundEndedEvent:event() = event(){}
# Questa mappa associa una casella di testo per visualizzare il tempo a ciascun giocatore.
var TimeRemainingTextBlocks:[player]text_block = map{}
# Il tempo rimanente prima del completamento del round, come numero intero.
var TimeRemainingInSeconds:int = 0
# Attende l'avvio del timer del round.
AwaitStart()<suspends>:void =
RoundStarted.Await()
Logger.Print("Timer del round avviato.")
# Usato per avviare il timer del round.
Start():void =
Logger.Print("Avvio del timer del round.")
RoundStarted.Signal()
set TimeRemainingInSeconds = GetRoundTimeInSeconds()
spawn{ Running() }
# Riavvia il timer a RoundTime
Restart():void =
Logger.Print("Riavvio del timer del round.")
set TimeRemainingInSeconds = GetRoundTimeInSeconds()
# Esegue la logica del timer.
Running()<suspends>:void =
Logger.Print("Timer del round in esecuzione.")
loop:
UpdateTimeUI()
Sleep(1.0)
# Diminuisce TimeRemaining di 1 secondo e controlla se il tempo è scaduto. In caso affermativo, termina il round.
set TimeRemainingInSeconds -= 1
if (TimeRemainingInSeconds < 0):
Stop()
break
# Ferma il timer e termina il round.
Stop():void =
Logger.Print("Fine del timer del round.")
# Prendiamo un giocatore tra quelli rimasti nella scena per concludere il round.
Players:[]player = GetPlayspace().GetPlayers()
if (Instigator := Players[0]):
RoundEndedEvent.Signal()
# Attende che il timer del round stia per terminare.
AwaitEnd()<suspends>:void =
RoundEndedEvent.Await()
Logger.Print("Timer del round terminato.")
# Accetta un valore temporale in minuti e restituisce il valore in secondi.
GetRoundTimeInSeconds():int =
var InSeconds:int = 0
if (set InSeconds = Round[RoundTimeInMinutes * 60.0]) {}
InSeconds
# Quando il timer termina, aggiorna il tempo rimanente e controlla se il tempo è scaduto.
UpdateTimeUI():void =
# Imposta i minuti a TimeRemainingInSeconds/60 senza il resto.
var Minutes:int = 0
if (set Minutes = Floor(TimeRemainingInSeconds / 60)) {}
# Imposta i secondi al resto di TimeRemainingInSeconds/60.
var Seconds:int = 0
if (set Seconds = Mod[TimeRemainingInSeconds, 60]) {}
# Converti i minuti e i secondi in stringhe.
MinutesAsString := string("{Minutes}")
# Se secondi < 10, dobbiamo aggiungere uno 0 davanti al valore in modo che venga visualizzato come :0# invece di :#
SecondsAsString := if (Seconds < 10) then Join(array{string("{0}"),string("{Seconds}")},string()) else string("{Seconds}")
# Scorri tutti i giocatori, controlla se hanno un TimeRemainingTextBlock e, se non ce l'hanno, assegnagliene uno. Quindi aggiorna il testo.
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))
# Accetta un giocatore e aggiunge un canvas dell'UI del tempo del round al suo schermo e memorizza il suo TimeRemainingTextBlock per aggiornarlo in seguito.
SetUpTimeRemainingUI(Player:player):text_block =
Logger.Print("Aggiunta della UI del timer del round a un giocatore.")
# Questo è il text_block che stampa il testo del tempo rimanente sullo schermo.
TextBlock:text_block = text_block:
DefaultTextColor := NamedColors.White
DefaultShadowColor := NamedColors.Black
if (PlayerUI := GetPlayerUI[Player]):
if (set TimeRemainingTextBlocks[Player] = TextBlock) {}
# Questo è il canvas che contiene e posiziona il text_block sullo schermo.
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
# Il canvas viene assegnato al giocatore.
PlayerUI.AddWidget(Canvas, player_ui_slot{ZOrder := UIZOrder})
# Il text_block viene restituito in modo che possa essere salvato sulla mappa e aggiornato in seguito, man mano che il tempo passa.
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){}
# Un int che consente valori compresi tra gli intervalli specificati. Questo tipo è necessario a player_ui_slot.ZOrder.
waiting_int_clamped := type{_X:int where 0 <= _X, _X <= 2147483647}
# Questo messaggio viene utilizzato per stampare il numero di giocatori necessari per l'inizio di un round.
WaitingForMorePlayersMessage<localizes>(Count:int):message = "Waiting for {Count} more Player(s)"
# Questa classe serve a mostrare quanti giocatori sono necessari per iniziare il round.
waiting_for_more_players_ui := class:
var Canvas:canvas
var TextBlock:text_block
<#
Questa classe contiene tutta la logica per impostare un numero minimo di giocatori e verificare se ce ne sono abbastanza per iniziare il round.
Per utilizzare questa classe:
1) Aggiungi il file al tuo progetto.
2) Compila il codice Verse dal menu Verse della barra degli strumenti.
3) Trascina il dispositivo nella tua isola dalla cartella Content/Creative Devices in Esplora contenuti.
4) Collega un dispositivo Timer alla proprietà "WaitingForMorePlayersTimer" di questo dispositivo.
5) Includi la classe waiting_for_more_players in un altro script Verse insieme a:
@editable
WaitingForMorePlayers:waiting_for_more_players = waiting_for_more_players{}
6) Compila il codice Verse dal menu Verse della barra degli strumenti.
7) Collega il dispositivo che hai creato al passaggio 3 al dispositivo Verse e alla proprietà che hai esposto al passaggio 6.
8) Attende le funzioni CheckForMinimumNumberOfPlayers passandole un giocatore. Ad esempio:
Players = GetPlayspace().GetPlayers()
CheckForMinimumNumberOfPlayers(Players)
9) In IslandSettings, imposta Conto alla rovescia avvio del gioco su 0.0.
#>
waiting_for_more_players := class(creative_device):
Logger:log = log{Channel:=log_waiting_for_more_players_device}
@editable # Il numero minimo di giocatori necessari per iniziare un round di gioco.
MinimumNumberOfPlayers:int = 2
@editable # La posizione orizzontale e verticale della UI del timer sullo schermo. X 0-1 è sinistra-destra e Y 0-1 è alto-basso.
UIPosition:vector2 = vector2{X:= 0.5, Y:=0.4}
@editable # La posizione orizzontale e verticale della UI del timer sullo schermo. X 0-1 è sinistra-destra e Y 0-1 è alto-basso.
UIAlignment:vector2 = vector2{X := 0.5, Y := 0.5}
@editable # ZOrder dell'UI rispetto agli altri elementi dell'UI.
UIZOrder:waiting_int_clamped = 3
@editable # Questo timer viene utilizzato per il conto alla rovescia per l'inizio del round dopo aver atteso che i giocatori si uniscano alla partita.
WaitingForMorePlayersTimer:timer_device = timer_device{}
# Questa mappa associa un canvas UI per visualizzare il numero di giocatori necessari per iniziare un round per ciascun giocatore.
var WaitingForMorePlayersUI:[player]?waiting_for_more_players_ui = map{}
# Controlla se ci sono abbastanza giocatori per iniziare il round. In caso contrario, aspetta che il numero di giocatori sia >= MinimumNumberOfPlayers.
WaitForMinimumNumberOfPlayers(Players:[]player)<suspends>:[]player =
Logger.Print("In attesa di sapere se ci sono abbastanza giocatori per iniziare il round.")
# Creo una nuova variabile in modo da poterla modificare man mano che si aggiungono altri giocatori. La inizializzo con un array di giocatori passati alla funzione.
var PlayersWaiting:[]player = Players
# Se il numero di giocatori è inferiore al minimo necessario per iniziare il round...
if (PlayersWaiting.Length < MinimumNumberOfPlayers):
loop: # Esegui il loop finché il numero di giocatori non è maggiore o uguale al minimo necessario.
Logger.Print("{PlayersWaiting.Length}/{MinimumNumberOfPlayers} giocatori necessari per iniziare il round.")
# Aggiorna l'UI in attesa di altri giocatori.
DisplayWaitingForMorePlayers(PlayersWaiting)
Sleep(2.0) # Aspetta di vedere se altri giocatori si uniscono alla partita e controlla se è presente un numero sufficiente di giocatori per iniziare il round.
set PlayersWaiting = GetPlayspace().GetPlayers()
if (PlayersWaiting.Length >= MinimumNumberOfPlayers):
# Se è presente un numero sufficiente di giocatori, cancella l'UI in attesa di altri giocatori,
Logger.Print("{PlayersWaiting.Length}/{MinimumNumberOfPlayers} giocatori nel round, che si preparano per l'inizio del round.")
ClearWaitingForMorePlayers()
# Quindi interrompi il loop.
break
# avvia il conto alla rovescia per l'inizio del round e aspetta che il conto alla rovescia sia completato.
WaitingForMorePlayersTimer.Start()
WaitingForMorePlayersTimer.SuccessEvent.Await()
Logger.Print("Avvio del round.")
# Restituisce l'elenco dei giocatori della sessione.
return PlayersWaiting
# Visualizza un messaggio UI "In attesa di altri giocatori" per ogni giocatore, se non ne ha già uno. Aggiorna il contatore dei giocatori per tutti i giocatori.
DisplayWaitingForMorePlayers(Players:[]player):void =
PlayersNeededCount := MinimumNumberOfPlayers - Players.Length
Logger.Print("{Players.Length} giocatori nel round, in attesa che si aggiungano altri {PlayersNeededCount}.")
for (Player : Players):
# Se il giocatore ha una WaitingForMorePlayersUI, prendi il TextBlock e aggiorna il testo in modo che mostri il numero corretto di giocatori necessari per iniziare il round.
if (UIData := WaitingForMorePlayersUI[Player]?):
UIData.TextBlock.SetText(WaitingForMorePlayersMessage(PlayersNeededCount))
# Altrimenti crea una WaitingForMorePlayersUI per il giocatore.
else:
SetUpWaitingForMorePlayersUI(Player, PlayersNeededCount)
# Accetta un giocatore e una player_ui e aggiunge un canvas UI in attesa di altri giocatori al loro schermo.
SetUpWaitingForMorePlayersUI(Player:player, PlayersNeededCount:int):void =
Logger.Print("Creazione dell'UI 'in attesa di altri giocatori'".)
if (PlayerUI := GetPlayerUI[Player]):
# Questo è il text_block che stampa sullo schermo il testo in attesa di altri giocatori.
TextBlock:text_block = text_block:
DefaultText := WaitingForMorePlayersMessage(PlayersNeededCount)
DefaultTextColor := NamedColors.White
DefaultShadowColor := NamedColors.Black
# Questo è il canvas che contiene e posiziona il text_block sullo schermo.
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
# Il canvas viene assegnato al giocatore.
PlayerUI.AddWidget(Canvas, player_ui_slot{ZOrder := UIZOrder})
# Il text_block viene salvato nella mappa in modo da poter aggiornare il testo in un secondo momento, quando altri giocatori si uniscono alla partita.
if (set WaitingForMorePlayersUI[Player] = option{ waiting_for_more_players_ui{Canvas := Canvas, TextBlock := TextBlock} }) {}
# Rimuove il messaggio UI "In attesa di altri giocatori" per ogni giocatore che ne ha uno.
ClearWaitingForMorePlayers():void =
Logger.Print("Cancellazione dell'UI 'in attesa di altri giocatori'".)
for (Player -> UIData : WaitingForMorePlayersUI):
if:
PlayerUI := GetPlayerUI[Player]
Canvas := UIData?.Canvas
set WaitingForMorePlayersUI[Player] = false
then:
PlayerUI.RemoveWidget(Canvas)