Utilizzo di THNK per un platform
THNK non è un framework di giochi multiplayer.
Il THNK è un framework di giochi autorevoli. È un modello per costruire giochi che seguono un architettura autorevole client-server. Un gioco costruito con THNK è pronto per essere trasformato in un gioco multiplayer da un’estensione adattatore, ma non è un gioco pienamente multiplayer.
Pertanto, un'anteprima senza alcun adattatore funzionerà lo stesso, e il gioco funzionerà correttamente anche in singleplayer. Quando si costruisce con THNK, si inizierà a costruire il gioco in singleplayer e per poi aggiungere il multiplayer in un secondo momento attraverso le estensioni dell'adattatore.
Iniziamo a costruire in singleplayer!
Facciamo un platformer!
Per prima cosa, aggiungeremo un personaggio platform (con i controlli predefiniti) e alcune piattaforme utilizzando i comportamenti predefiniti di GDevelop. Questo è simile a come si farebbe di solito in GDevelop - niente di nuovo finora!
Se avvii un'anteprima, dovresti essere in grado di saltare sulle piattaforme.
Aggiungi animazioni al giocatore
Aggiungiamo le animazioni al giocatore! Si tratta di un'operazione abbastanza comune in GDevelop. Ma ricordate che in THNK gli eventi devono appartenere a una delle due categorie: Server o Client. Prima di aggiungere le animazioni, occorre decidere a quale sezione appartengono.
Quando aggiungi eventi al tuo gioco, devi porti sempre alcune domande:
- Richiede informazioni che il client non dovrebbe conoscere?
- È qualcosa che riguarda gli altri giocatori?
Qui, le animazioni dei giocatori:
- Utilizza la posizione dei giocatori visibili sullo schermo (per determinare se si stanno muovendo o meno), informazioni di cui il client dispone comunque
- Influisce solo sull'aspetto della partita locale, non su quella degli altri giocatori
Pertanto, dovrebbe essere nella sezione client!
Tecnicamente, non è necessario avere le animazioni come eventi client, ma spesso nella pratica è un'idea migliore. Se fosse sul server, dovrebbe essere sincronizzato con i client. Questo è problematico per due motivi:
Questo è negativo per la larghezza di banda: ogni volta che un'animazione cambia, la modifica deve essere inviata ai client. Con molti oggetti e client, questo può sovraccaricare rapidamente la rete trasmettendo qualcosa che il client in un certo senso conosce già, in quanto può dedurlo dal movimento di altri oggetti. Si dovrebbe sempre cercare di ridurre al minimo la comunicazione tra client e server per evitare problemi su connessioni a bassa velocità.
Questo potrebbe far sì che le animazioni non corrispondano a ciò che viene visualizzato. Poiché il server gestirebbe le animazioni, queste verrebbero aggiornate solo in base ai tick del server, che potrebbero essere impostati a una velocità inferiore rispetto alla frequenza dei fotogrammi di rendering. Per alcuni fotogrammi, l'animazione potrebbe non corrispondere a ciò che ci si aspetta finché il server non invia un aggiornamento.
Adattare il platform per il multiplayer
Assegnare a ogni client un oggetto giocatore
Ora abbiamo un semplice platform singleplayer con animazioni. Aggiungiamo un po' di multiplayer! Per prima cosa, dobbiamo dire a THNK cosa fare quando un altro giocatore si connette. Vogliamo che ogni giocatore abbia il proprio personaggio. Pertanto, quando un giocatore si connette, dobbiamo creare un oggetto per quel giocatore e cancellarlo quando il giocatore si disconnette. Dobbiamo collegare l’oggetto al giocatore: Ciò ci permetterà di sapere a quale giocatore appartiene ogni istanza. In questo modo, sapremo quale istanza del personaggio platform cancellare, quando un giocatore se ne va.
Avrai notato che non si specifica a quale giocatore collegare l'oggetto. Questo perché THNK ha la selezione del giocatore: l’evento “On Connection” seleziona il giocatore che si è connesso per essere utilizzato dalle prossime azioni.
Se desideri collegare un oggetto (o generalmente usare qualsiasi azione THNK che utilizza il giocatore selezionato) a un altro giocatore specifico o al di fuori di una condizione di selezione del giocatore, puoi usare l’azione “Scegli giocatore” per specificare il giocatore da influenzare.
Se avevi un'istanza iniziale del giocatore sulla scena, dovresti rimuoverla! Questa istanza sarebbe solo uno "zombie" - poiché non è legata a nessun giocatore, nessuno sarà in grado di controllarla...
Le connessioni e le disconnessioni sono ovviamente una competenza del server, pertanto è necessario gestirle all'interno degli eventi del server.
Poiché vogliamo che i client siano in grado di vedere gli oggetti dei giocatori, abbiamo bisogno che tali oggetti siano sincronizzati dal server a tutti i client. Per farlo, è molto semplice: basta aggiungere il comportamento synchronize (da THNK) al proprio oggetto giocatore!
Aggiunta dei controlli del giocatore per i client
Mentre il giocatore potrebbe controllare bene in single-player, in multiplayer il movimento non si sincronizza con gli altri client, nonostante il comportamento di sincronizzazione. Perché questo?
Il motivo è che i comportamenti vengono eseguiti sia sul client che sul server. Tuttavia, il server non può leggere gli input di un giocatore, in quanto si tratta di un compito del client. Il comportamento del platform non riceverà alcun input per quanto riguarda il server e quindi gli altri client non vedranno alcun movimento.
I client possono comunque muovere il proprio personaggio, poiché il codice del client, che ha accesso agli input del giocatore, gestisce anche il comportamento del platform. Solo il server può spostare gli oggetti per tutti i giocatori, quindi questo movimento non sarà sincronizzato.
In THNK, tutte le interazioni dei giocatori con lo stato del gioco, dal movimento del giocatore all’uso di un oggetto nell’inventario, devono essere inviate come comando dal client al server. È quindi compito del server convalidare ed elaborare il comando, aggiornando di conseguenza lo stato del gioco per tutti.
In questo caso, tutto ciò che dobbiamo fare è inviare gli input dai client e alimentarli al comportamento della piattaforma. Il comportamento del platformer elaborerà il modo in cui l’oggetto dovrebbe muoversi e aggiornerà automaticamente la posizione, occupandosi della “validazione e aggiornamento dello stato del gioco” nella gestione dei messaggi.
Il modo ingenuo
L'invio di un comando dal client e la sua ricezione sul server possono essere effettuati con le azioni e le condizioni corrispondenti:
La via intelligente
Finora abbiamo questi eventi:
Possono tecnicamente funzionare, ma... sono difettosi.
Ricevere eventi in un evento normale è più leggibile, ma permette di elaborare solo un messaggio per tick del server, poiché la condizione viene chiamata solo una volta. Lasciare che i messaggi non vengano gestiti e che poi vengano gestiti al prossimo tick potrebbe causare problemi se molti giocatori inviano questi messaggi. Per gestire tutti i messaggi ricevuti in un singolo tick del server, usa un evento while per gestire gli eventi mentre alcuni sono disponibili per essere elaborati:
Il secondo problema è il tipo di messaggio che stiamo inviando. Quando si inviano messaggi, bisogna sempre tenere a mente alcuni aspetti:
- La connessione può essere instabile e i messaggi possono essere ritardati
- Il traffico tra il server e il client deve essere il più ridotto possibile, in modo da non utilizzare una larghezza di banda superiore a quella a disposizione
Questi eventi vengono inviati ad ogni frame in cui viene premuto un tasto. Si tratta di un sacco di messaggi e questo richiede una grande larghezza di banda! Se per qualche motivo un messaggio non viene ricevuto dal server prima di ogni tick, ad esempio a causa di brevi disconnessioni dovute a instabilità della connessione, per il server è come se il giocatore continuasse a premere e rilasciare il pulsante, quando, in realtà, lo sta ancora tenendo premuto.
Per risolvere questo problema, adottiamo un altro metodo: Invieremo solo 1 messaggio quando si preme e 1 quando si rilascia il pulsante. In questo modo, possiamo inviare molti meno messaggi, e se la connessione è o diventa instabile, il server supporrà che il client continui a fare ciò che stava facendo invece di supporre che abbia smesso di fare qualsiasi cosa.
Questa soluzione è più complessa, ma offrirà un'esperienza migliore ai giocatori.
Naturalmente, lo facciamo solo qui perché ha senso per il movimento continuo del giocatore. Per esempio, l'uso di una pozione in un gioco di ruolo dovrebbe essere processato solo una volta quando richiesto, al contrario - consumare continuamente pozioni ad ogni tick del server finché il client non chiede di fermarsi non è un buon game design :p