Indietro
Come abbiamo migliorato il modello di gara a circuito in Fortnite con la Persistenza di Verse e altro ancora.
Il team di Fortnite
L'introduzione della Persistenza di Verse ha offerto la possibilità di aggiornare l'isola del modello "Progetta una gara a circuito" della modalità Creativa di Fortnite in UEFN. Da aggiornamenti quali l'aggiunta di sequenze cinematografiche e paesaggi, alla rimozione di attivatori e lo spostamento all'interno di Verse, siamo entusiasti di condividere con te il viaggio dello sviluppo di questo modello. Ti diamo il benvenuto alla Gara a circuito con la Persistenza di Verse!
Il modello "Progetta una gara a circuito" è stato inizialmente creato per utilizzare gli asset del circuito di gara appena creati e costruire un'esperienza di gara distintiva con alcune buone caratteristiche di qualità della vita.
Con l'ultimo aggiornamento, abbiamo assunto un approccio olistico e abbiamo sostituito e aggiornato molte caratteristiche su tutta la mappa. Diamo un'occhiata ad alcuni degli aggiornamenti eseguiti che sfruttano veramente la potenti capacità di UEFN.
Il ciclo legacy giorno/notte del progetto originale è stato sostituito dalla più avanzata illuminazione del Capitolo 4 di Battaglia reale di Fortnite. Questo nuovo ciclo ci ha consentito di utilizzare Lumen, creando ombre più morbide e un'illuminazione globale realistica. Abbiamo inoltre aggiunto caratteristiche come una cascata e reso il circuito stesso visivamente più interessante.
La Persistenza di Verse ha fornito la soluzione, consentendo il tracciamento dei dati fra le sessioni di gioco. Questa funzionalità ci ha permesso di monitorare le statistiche sulla vita del giocatore e creare classifiche locali.
Il nostro obiettivo era aggiornare il miglior tempo di giro di un giocatore a conclusione di un giro e anche registrarne i punti e le vittorie ogni volta conclusa una gara.
Aggiornare i tempi di giro era semplice, poiché potevamo attendere il LapCompletedEvent del dispositivo del Gestione gara per scoprire quando un giocatore avesse terminato un giro. Quando inizia la gara, avviamo un dispositivo Timer in esecuzione per giocatore. La funzione WaitForPlayerToFinishLap attende che un giocatore termini un giro, calcola il suo tempo di giro, aggiorna la tabella delle statistiche e reimposta il timer per la registrazione del giro successiva.
Per risolvere la questione, abbiamo usato la funzione ArraySync. ArraySync chiama una funzione asincrona su ciascun elemento nell’array ivi trasferito e attende il completamento di tutte quelle funzioni. Trasferendo un array di giocatori e la funzione WaitForPlayerToFinishRace, potevamo chiamare la funzione su ognuno di essi e attendere che avesse terminato per tutti.
Questo ci ha consentito di assegnare punti e una vittoria ai giocatori in base al loro piazzamento quando hanno terminato la gara e di aggiornare dunque la loro tabella delle statistiche. Quando termina ArraySync, sappiamo che tutti i giocatori hanno terminato la gara e possono concludere il gioco.
Poiché avevamo già statistiche persistenti per ciascun giocatore, potevamo recuperare quelle statistiche e visualizzarle tramite i cartelli. Nell'area di attesa di prepartita, abbiamo aggiunto dispositivi Manifesto e Riferimento giocatore che mostrano per ciascun giocatore sia le statistiche sia i costumi usati in quel momento. Tuttavia, ordinare i giocatori in base alle prestazioni è rimasta una sfida.
Per riuscirci, abbiamo implementato l'algoritmo Merge Sort. Merge Sort è un normale algoritmo dividi e conquista che suddivide in modo ricorsivo un array in due, ordina ciascun sottoarray e li fonde di nuovo. Abbiamo inoltre aggiunto la possibilità di passare a una funzione di confronto con l'algoritmo e abbiamo creato funzioni di confronto per ciascuna delle diverse statistiche del giocatore.
Ogni qualvolta dovevamo ordinare i giocatori, potevamo recuperare le statistiche di ciascun giocatore dalla relativa tabella statistiche, aggiungerle tutte a un array e trasferire questo e una funzione di confronto all'algoritmo Merge Sort. Scegliendo la funzione di confronto trasferita, potevamo recuperare l'array dei giocatori ordinati in base a qualsiasi statistica. Nel nuovo modello abbiamo incluso sia l'algoritmo Merge Sort che un file di prova, così puoi testare te stesso l'algoritmo e adattarlo alle tue funzioni!
Con i meccanismi di ordinamento adottati, abbiamo finalizzato le nostre classifiche. Durante il primo round del gioco, i giocatori sono generati in un'area di attesa prepartita con i loro riferimenti e cartelli. Ordiniamo questi riferimenti giocatore in base ai punti vita di ciascun giocatore e visualizziamo ciascuna delle relative statistiche sul cartello davanti a loro. In tal modo i giocatori hanno la possibilità di studiare la concorrenza prima della gara e sapere a chi fare attenzione se vogliono vincere.
Continua a giocare e potresti ritrovarti in cima alla classifica!

Per eseguire questa operazione, abbiamo creato una variabile di mappa debole del secondo giocatore denominata CircuitInfo. Questa variabile CircuitInfo contiene tutte le informazioni che dobbiamo reimpostare al termine del gioco o quando un giocatore abbandona il gioco.
Abbiamo utilizzato una seconda variabile di mappa debole per chiarire quali informazioni dovessero essere reimpostate (informazioni sul circuito) e quali dovessero perdurare per sempre (statistiche giocatore).
Per ogni giocatore registriamo le seguenti informazioni nella variabile di mappa debole del giocatore CircuitInfo:
Nella funzione OnBegin del dispositivo Verse (che viene eseguito all'inizio di ogni round), immaginiamo in quale round siamo scorrendo tutti i giocatori attivi e determinando il valore più alto dell'ultimo round completato fra i relativi dati perpetui. Facciamo questo soltanto una volta per round e registriamo le informazioni sul round in una variabile di mappa debole della sessione cosicché tutto il codice Verse del progetto può accedere a tale valore in qualsiasi momento senza bisogno di ricalcolarlo.
Quando un giocatore conclude la gara, rilevato attendendo il RaceCompletedEvent del dispositivo Gestione gara, registriamo l'ordine di arrivo e il round attuale nella variabile di mappa debole CircuitInfo associata al giocatore. Reimpostiamo queste informazioni a due condizioni:
Nel modello aggiornato, sostituiamo l’Attivatore a impulsi con il Sequencer per ottenere la nostra sequenza cinematografica di apertura. Utilizzando il Sequencer, abbiamo potuto aggiungere telecamere diverse, visori a sovrimpressione (HUD) e una visuale in serie dinamica che si regola in base alla quantità di giocatori attivi.
In modo simile alla configurazione dell'Attivatore a impulsi, abbiamo collegato i dispositivi alla sequenza in momenti importanti, che ci hanno consentito di stabilire quando mostrare il punteggio del giocatore successivo o quando tagliare l'introduzione.

Restiamo in attesa di aggiornare questo modello man mano che usciranno nuove funzioni in grado di migliorarne la progettazione. Nel frattempo, puoi scaricare il modello, esplorarne i componenti e integrarne la funzionalità nei tuoi progetti tramite la scheda modello in UEFN. Non vediamo l'ora di veder prendere forma le tue gare, classifiche e altro ancora!
Il modello "Progetta una gara a circuito" è stato inizialmente creato per utilizzare gli asset del circuito di gara appena creati e costruire un'esperienza di gara distintiva con alcune buone caratteristiche di qualità della vita.
Con l'ultimo aggiornamento, abbiamo assunto un approccio olistico e abbiamo sostituito e aggiornato molte caratteristiche su tutta la mappa. Diamo un'occhiata ad alcuni degli aggiornamenti eseguiti che sfruttano veramente la potenti capacità di UEFN.
Progettazione visiva
La modalità paesaggio in UEFN ci ha permesso di tornare alle radici delle gare e creare un circuito sterrato utilizzando i nuovi strumenti di editing del paesaggio. Le montagne fatte di asset di roccia sullo sfondo della nostra isola originaria sono state sostituite da una progettazione paesaggistica più naturale.Il ciclo legacy giorno/notte del progetto originale è stato sostituito dalla più avanzata illuminazione del Capitolo 4 di Battaglia reale di Fortnite. Questo nuovo ciclo ci ha consentito di utilizzare Lumen, creando ombre più morbide e un'illuminazione globale realistica. Abbiamo inoltre aggiunto caratteristiche come una cascata e reso il circuito stesso visivamente più interessante.
Persistenza di Verse: una classifica migliore
Nella mappa originale, una torre facente uso del dispositivo Riferimento giocatore mostrava per prima cosa il giocatore e quanti punti aveva. I dati tuttavia non perduravano da una sessione all'altra.La Persistenza di Verse ha fornito la soluzione, consentendo il tracciamento dei dati fra le sessioni di gioco. Questa funzionalità ci ha permesso di monitorare le statistiche sulla vita del giocatore e creare classifiche locali.
Tracciamento delle statistiche del giocatore
Abbiamo sviluppato una classe di tabelle delle statistiche giocatori perpetua che traccia le vittorie nella vita di un giocatore, il suo miglior tempo di giro e i punti guadagnati per finale di gara. Abbiamo anche utilizzato una mappa debole perpetua denominata PlayerStatsMap per mappare i giocatori nelle relative tabelle di statistiche, consentendo alle statistiche stesse di perdurare attraverso round e sessioni. Abbiamo quindi creato una classe di gestione statistiche per gestire l'inizializzazione, il recupero e l'aggiornamento delle statistiche per giocatore.Il nostro obiettivo era aggiornare il miglior tempo di giro di un giocatore a conclusione di un giro e anche registrarne i punti e le vittorie ogni volta conclusa una gara.
Aggiornare i tempi di giro era semplice, poiché potevamo attendere il LapCompletedEvent del dispositivo del Gestione gara per scoprire quando un giocatore avesse terminato un giro. Quando inizia la gara, avviamo un dispositivo Timer in esecuzione per giocatore. La funzione WaitForPlayerToFinishLap attende che un giocatore termini un giro, calcola il suo tempo di giro, aggiorna la tabella delle statistiche e reimposta il timer per la registrazione del giro successiva.
Registrazione di vittorie e punti
La registrazione di vittorie e punti si è dimostrata più complessa. Sapevamo che avremmo potuto utilizzare la funzione simile "WaitForPlayerToFinishRace" per attendere il "RaceCompletedEvent" del dispositivo Gestione gara e sapere quando un qualunque giocatore avesse terminato. Tuttavia, volevamo che il gioco finisse solo quando tutti i giocatori avessero terminato e il dispositivo Gestione gara non aveva un modo per tracciarlo o concludere il gioco quando avessero terminato.Per risolvere la questione, abbiamo usato la funzione ArraySync. ArraySync chiama una funzione asincrona su ciascun elemento nell’array ivi trasferito e attende il completamento di tutte quelle funzioni. Trasferendo un array di giocatori e la funzione WaitForPlayerToFinishRace, potevamo chiamare la funzione su ognuno di essi e attendere che avesse terminato per tutti.
Questo ci ha consentito di assegnare punti e una vittoria ai giocatori in base al loro piazzamento quando hanno terminato la gara e di aggiornare dunque la loro tabella delle statistiche. Quando termina ArraySync, sappiamo che tutti i giocatori hanno terminato la gara e possono concludere il gioco.
Visualizzazione dei risultati
Registrare le statistiche è stata una cosa, ma avevamo bisogno anche di un modo per mostrarle ai giocatori. Volevamo creare classifiche nel livello che fossero visibili e abbiamo ordinato i giocatori in base ai punti vita per evidenziare quelli col punteggio più alto.Poiché avevamo già statistiche persistenti per ciascun giocatore, potevamo recuperare quelle statistiche e visualizzarle tramite i cartelli. Nell'area di attesa di prepartita, abbiamo aggiunto dispositivi Manifesto e Riferimento giocatore che mostrano per ciascun giocatore sia le statistiche sia i costumi usati in quel momento. Tuttavia, ordinare i giocatori in base alle prestazioni è rimasta una sfida.
Per riuscirci, abbiamo implementato l'algoritmo Merge Sort. Merge Sort è un normale algoritmo dividi e conquista che suddivide in modo ricorsivo un array in due, ordina ciascun sottoarray e li fonde di nuovo. Abbiamo inoltre aggiunto la possibilità di passare a una funzione di confronto con l'algoritmo e abbiamo creato funzioni di confronto per ciascuna delle diverse statistiche del giocatore.
Ogni qualvolta dovevamo ordinare i giocatori, potevamo recuperare le statistiche di ciascun giocatore dalla relativa tabella statistiche, aggiungerle tutte a un array e trasferire questo e una funzione di confronto all'algoritmo Merge Sort. Scegliendo la funzione di confronto trasferita, potevamo recuperare l'array dei giocatori ordinati in base a qualsiasi statistica. Nel nuovo modello abbiamo incluso sia l'algoritmo Merge Sort che un file di prova, così puoi testare te stesso l'algoritmo e adattarlo alle tue funzioni!
Con i meccanismi di ordinamento adottati, abbiamo finalizzato le nostre classifiche. Durante il primo round del gioco, i giocatori sono generati in un'area di attesa prepartita con i loro riferimenti e cartelli. Ordiniamo questi riferimenti giocatore in base ai punti vita di ciascun giocatore e visualizziamo ciascuna delle relative statistiche sul cartello davanti a loro. In tal modo i giocatori hanno la possibilità di studiare la concorrenza prima della gara e sapere a chi fare attenzione se vogliono vincere.
Continua a giocare e potresti ritrovarti in cima alla classifica!

Ordine dei piloti sulla linea di partenza
La versione della modalità Creativa di Fortnite della mappa mette i giocatori in ordine casuale all'inizio di ciascuna partita. Per motivare i giocatori a raggiungere posizionamenti migliori in classifica, impostiamo l'ordine della linea di partenza in base alla posizione di arrivo del round precedente.Tracciamento delle informazioni sul round
Diversamente dai dati perpetui utilizzati per la classifica, avevamo bisogno dell'ordine dei piloti e delle informazioni sul circuito da mantenere per tutti i round ma non per tutte le sessioni di gioco. Una variabile di mappa debole della sessione in Verse reimposta i relativi dati a ogni round, così dobbiamo archiviare queste informazioni per ciascun giocatore e reimpostarle al termine del gioco.Per eseguire questa operazione, abbiamo creato una variabile di mappa debole del secondo giocatore denominata CircuitInfo. Questa variabile CircuitInfo contiene tutte le informazioni che dobbiamo reimpostare al termine del gioco o quando un giocatore abbandona il gioco.
Abbiamo utilizzato una seconda variabile di mappa debole per chiarire quali informazioni dovessero essere reimpostate (informazioni sul circuito) e quali dovessero perdurare per sempre (statistiche giocatore).
Per ogni giocatore registriamo le seguenti informazioni nella variabile di mappa debole del giocatore CircuitInfo:
- Ordine di arrivo
- Ultimo round completato
Logica specifica del round
Dobbiamo sapere in quale round siamo per applicare la logica specifica del round (come ordinare i giocatori in base al loro ultimo ordine di arrivo se non è il primo round) e per sapere quando reimpostare i dati giocatore. Attualmente non c’è alcuna API per determinare il round corrente, per questo dobbiamo registrarlo nei dati perpetui per ciascun giocatore.Nella funzione OnBegin del dispositivo Verse (che viene eseguito all'inizio di ogni round), immaginiamo in quale round siamo scorrendo tutti i giocatori attivi e determinando il valore più alto dell'ultimo round completato fra i relativi dati perpetui. Facciamo questo soltanto una volta per round e registriamo le informazioni sul round in una variabile di mappa debole della sessione cosicché tutto il codice Verse del progetto può accedere a tale valore in qualsiasi momento senza bisogno di ricalcolarlo.
Quando un giocatore conclude la gara, rilevato attendendo il RaceCompletedEvent del dispositivo Gestione gara, registriamo l'ordine di arrivo e il round attuale nella variabile di mappa debole CircuitInfo associata al giocatore. Reimpostiamo queste informazioni a due condizioni:
- Il giocatore abbandona durante il gioco. Aderiamo al PlayerRemovedEvent dello spazio di gioco per sapere quando avviene l'abbandono e chiamiamo la nostra funzione ResetCircuitInfo.
- All'inizio di ogni round, calcoliamo l'ultimo round completato. Se fosse il round finale, invochiamo ResetCircuitInfo per aggiornare i dati del giocatore. Sappiamo quanti round ci sono disponendo di una proprietà modificabile sul dispositivo Verse che è impostata sul numero totale di round per un gioco. In tal modo il creatore dell'isola si accerta che il numero corrisponda con le impostazioni del round.
Quando usare le Mappe deboli della sessione e le Mappe deboli del giocatore.
Questa nuova Gara a circuito è un ottimo esempio per mostrare le differenze e il ragionamento dietro all’uso nel proprio codice della variabile di mappa debole della sessione e della variabile di mappa debole del giocatore:- Le variabili di mappa debole della sessione sono utili per elementi singoli e per archiviare i dati per il round corrente che non si vuole ricalcolare ogni volta.
- Le variabili di mappa debole del giocatore sono progettate per informazioni che devono perdurare in vari round e sessioni di gioco, ma devono essere associate ai singoli giocatori.
Attivatore a impulsi nella sequenza di apertura
Nella versione originale del modello di Gara a circuito, abbiamo utilizzato un dispositivo chiamato Attivatore a impulsi per gestire la parte della gara "pronti - partenza - via". L'Attivatore a impulsi ha riprodotto una sequenza di eventi in un determinato intervallo di tempo attivando attivatori per visualizzare testo e abilitare le luci sulla linea di partenza.Nel modello aggiornato, sostituiamo l’Attivatore a impulsi con il Sequencer per ottenere la nostra sequenza cinematografica di apertura. Utilizzando il Sequencer, abbiamo potuto aggiungere telecamere diverse, visori a sovrimpressione (HUD) e una visuale in serie dinamica che si regola in base alla quantità di giocatori attivi.
In modo simile alla configurazione dell'Attivatore a impulsi, abbiamo collegato i dispositivi alla sequenza in momenti importanti, che ci hanno consentito di stabilire quando mostrare il punteggio del giocatore successivo o quando tagliare l'introduzione.

Cosa ci aspetta?
Poiché UEFN continua ad avanzare, ci auguriamo di evolvere anche questo modello. Il nostro obiettivo è offrirti la possibilità di continuare a creare una maggior quantità di contenuti elaborati con UEFN, sfruttando le più recenti funzioni per costruire isole divertenti e coinvolgenti.Restiamo in attesa di aggiornare questo modello man mano che usciranno nuove funzioni in grado di migliorarne la progettazione. Nel frattempo, puoi scaricare il modello, esplorarne i componenti e integrarne la funzionalità nei tuoi progetti tramite la scheda modello in UEFN. Non vediamo l'ora di veder prendere forma le tue gare, classifiche e altro ancora!