Massive Multiplayer Role Play Game
Questo documento prova ad affrontare i problemi teorici che
ruotano attorno al concetto di Gioco di Ruolo Online partendo dal punto
di vista grafico fino ad arrivare ai protocolli di rete. Queste sono tutte
mia considerazioni personali, fondate solo dall'esperienza, che comunque
è molto poca - per ora -. La visione è puramente teorica
e sta ad ognuno implementare il problema come crede.
Un gioco di Ruolo
Questo discorso investe prevalentemente il Gameplay. Un gioco non online,
permette di inserire una trama, visto che il giocatore quando lascia il
gioco e salva può continuare in tempi successivi l'avventura. Un
gioco online invece non permette questa formula, a scapito di una parte
di divertimento legata al gioco di ruolo. Questo però non implica
che non ci possa essere in ogni caso una trama. Esistono diverse forme
surrogate di trama:
-
Le Quest.
-
Le Missioni
-
Il Background del personaggio
Le missioni sono avventure brevi (neanche un'ora) e si limitano nell'uccidere
un PNG o ritrovare un oggetto. Le Quest sono brevi avventure che il personaggio/i
possono compiere nell'arco massimo di un paio d'ore, cioè il tempo
massimo che un utente (italiano) concede a questo genere di cose. Le Quest,
a differenza delle missioni che possono essere create da un PNG grazie
a un semplice algoritmo, devono essere creata a misura da un DM. Il Background
del personaggio permette invece a un PG di avere uno scopo nella vita,
un obiettivo principale a cui giungere.
Il gioco di Ruolo consiste nel poter impersonare un personaggio virtuale
(chiamiamolo pure Avatar, burattino) che compie azioni secondo un proprio
schema logico. Se il gioco non fissa un obiettivo finale a cui arrivare,
il giocatore potrebbe non sentire la volontà di giocare, a meno
che non si dia al giocatore una libertà nel far quello che più
gli piace e che nella vita reale (IRL) non ha tempo, coraggio, voglia di
fare.
Gioco Online o Gioco locale
Per quello che riguarda le differenze sono elencate nella tabella seguente,
solo per bellezza credo.
|
Gioco Online |
Gioco Locale |
Salvataggi |
1 sono salvataggio e non possibilità di recuperare o fermare
il tempo. |
Salvataggi a volontà e possibilità di recuperare da punti
precedenti |
Morte |
La morte non pregiudica il gioco. Mal che vada si perdono delle caratteristiche
o esperienza |
La Morte termina il gioco. I Salvataggi comunque rendono questa evenienza
un semplice evento. |
Interazione con persone |
Con PNG e con altri giocatori |
Solo con PNG |
Trama |
Semplice. E' il giocatore stesso che con il suo operato crea un'avventura
per se stesso e per gli altri. In questo modo il gioco è virtualmente
infinito. |
C'è una trama, anche molto complessa ed eccitante, ma che termina
con un obiettivo finale, o nella possibilità di ricominciare, ripetitiva
a un certo punto. |
Testo o Grafica
Le denominazioni sono tante, MUD (Multi User Dungeon), MUG (Multi User
Game), MOO (MUD orientato ad oggetti), la prima definizione di gioco online.
L'epoca non permetteva grande potenza di calcolo delle macchine client,
ne tanto meno la rete permetteva scambi elevati di dati tra gli utenti.
In queste condizioni i giochi erano testuali supportati da un programma
Client standard come è il Telnet. In questo caso il protocollo di
comunicazione, visto il tipo di Client, è il TCP. Al giorno d'oggi
invece la banda dati di Internet è tale da permette un trasferimento
sostenuto di dati che permettono un gioco più vario e graficamente
parlando più vicino a un giocatore moderno. Ovviamente i giochi
testuali locali sono un ricordo del passato, mentre i giochi online testuali
continuano nonostante tutto a sopravvivere, visto che lo sviluppo e il
mantenimento da parte del Server è minimo, e la mancanza di grafica
può essere sopperita dalla fantasia degli utenti (e in più
sono sempre delle ottime chat!).
Grafica a Tile
La bellezza di un gioco Online, ma anche Locale, sta nella possibilità
di avere un mondo molto vasto. Online questa richiesta si fa ancora più
esigente per due motivi: spazio (troppi giocatori in un piccolo ambiente
non ci stanno) e varietà, la varietà che dovrebbe sopperire
alle mancanze di trama agli avventurieri. Un mondo molto grande permette
al giocatore di poter passare diverso tempo ad esplorare e scoprire nuovi
posti.
Il difetto di tutto ciò sta nella dimensione che questo mondo
possa occupare. Bisogna dunque trovare dunque un compromesso. Un mondo
completamente vario obbliga a riservare una quantità di memoria
fisica al di fuori totalmente della nostra (attuale) concezione. Se il
mondo fosse completamente in 3D potremmo occupare anche 1CD di dati per
Kmq, e ricordiamo che Baldur's Gate occupa
5 CD nonostante le ambientazioni siano solo una trentina, perché
le mappe del gioco sono delle pure e semplici Bitmap. Da molto tempo si
è cercato un modo di rappresentare il mondo con unità semplici
da poterle poi duplicare. Questi singoli oggetti sono chiamati Tile,
e come le tessere del mosaico, permettono di creare ambienti molto vasti
con poco occupazione di HD. Per esempio, sia che le Tile siano 3D o 2D
isometrico, queste occuperanno una dimensione ridottissima, e il mondo
potrà essere rappresentato semplicemente da una matrice dove a ogni
elemento (entry) è assegnato il codice di una particolare
tile. Ovviamente fare il mondo a mesh o in bitmap lo rende molto migliore
graficamente, e permette una varietà di ambienti praticamente infinita.
Si pensi solo che in una foresta ogni albero sarà diverso dagli
altri, come in realtà. Con le tile invece ci sarà sempre
una scelta (comunque anche ampia) dei possibili elementi.
Questo metodo permette di disegnare solo le Tile che si vedono e nessuna
di più tramite un semplice algoritmo di visibilità, e visto
che la distanza tra Osservatore e Mondo è costante, lo è
anche il livello di dettaglio (LOD), e questo valore può essere
impostato dall'utente. |
Singola Tile
Matrice. Ogni elemento è un codice che indica quale tile
disegnare in quel punto |
Scelta della Grafica
Grafica 2D o 3D? Ovviamente visto i tempi che corrono si può pensare
che la grafica 2D sia morta. Invece c'è da notare che ancora oggi è
preferibile usare la 2D, sempre per motivi di performance. Baldur's Gate ne
è stato un esempio, ma anche Baldur's Gate II seguirà l'esempio
del predecessore, introducendo però la modalità mista che va molto
per la maggiore al giorno d'oggi. La scelta comunque dipende da ogni implementazione,
e dal mio punto di vista sia la modalità 3D che in modalità mista
danno molte soddisfazioni. La modalità mista risolve con Bitmap i problemi
di performance della 3D introducendo invece la possibilità di animazione
che invece solo la grafica in 3D può permettere (rotazioni del personaggio,
IK e Bone). Questa tecnica consiste nel memorizzare gli sfondi (oggetti, case,
alberi...) in Bitmap insieme a uno ZBuffer (in questo caso specifico relativo
alla tile) e invece i personaggi, animali, magie, etc etc in 3D, e integrare
il tutto. E' una tecnica vecchia (Alone in the Dark) ma che continua a rendere
ancora moltissimo. L'unico metodo di scelta sta nelle capacità che si
hanno e quanto grosso si vuole fare il database grafico sul client.
I terreni possono essere creati usando bitmap, ma visti i tempi è buona
cosa implementarli come texture su poligoni, e affidare il rendering alla scheda
3d o, se ne si possiede uno, al proprio engine di rendering software. In questa
maniera sarà possibile disegnare avvallamenti e alture, con effetti di
luce e ombra in modo molto semplice.
Grafica. Idee Varie
Questo capitoletto andrebbe letto alla fine di tutto, perché fa
riferimento a concetti spiegati di seguito.
Visto che il concetto di Pavimento, sia in 2D e in 3D, è fondamentale
per alcuni motivi, primo fra tutti l'altezza del personaggio rispetto al
livello zero, è cosa utile codificare a parte l'altezza del terreno
rispetto alla grafica che ne verrà fuori sopra questo. In questo
modo si può codificare che texture assegnare al pavimento e aggiungere
un valore per ogni vertice per indicare l'altezza del vertice del terreno.
Ecco come sarebbe una entry (public) della mappa:
-
ID Tile (grafica escluso il terreno) [2 byte]
-
ID Texture Floor (Texture del terreno) [1 byte]
-
Vertex Height (altezza di un vertice, in alto a sinistra per esempio) [1
byte]
In questo modo si riesce ad esprimere una quantità enorme di ambienti
nonostante si abbiano solo 4 byte per entry. Avendo codificato solo un
vertice per entry permette di disegnare solo terreni continui, comunque
non è un gran problema. Se il dato del bordo non si conosce si può
mettere temporaneamente a zero, tanto il bordo non sarà visibile
finché non verra caricata la mappa adiacente. 2 byte per le Tile
significano 65534 possibili Tile più che sufficienti per qualsiasi
esigenza pratica. 1 byte per il terreno invece indicano solo 254 possibili
terreni (+ no terreno), comunque un gran numero rispetto alle esigenze
odierne. Si ottengono, se completamente riempiti, con 4 byte per entry,
4 Miliardi di combinazioni di altezze (considerando i 4 vertici limitrofi)
che vanno moltiplicate per le 256 combinazioni del terreno e alle 65mila
combinazioni dovute alle tile (unico indice dove c'è del reale lavoro
da lasciar fare ai grafici). A conti fatti sono 2^56 combinazioni possibili,
con un lavoro reale di grafica solo per 65536 mesh di tile massimo, un
tool per generare altimetrie e 256 texture per il terreno. Tra le texture
bisognerà includere una texture 0 indicante la totale mancanza di
terreno e tutte le transizioni tra due texture (tipo erba->strada) a senconda
dei possibili angoli. Ovviamente questa transizioni potrebbe essere anche
codificata come parte della mesh della tile sovrastante per risparmiare.
E' poi anche vero che se si usa un rendering 3D è abbastanza stupido disegnare
4 tile per rappresentare la medesima tile ruotata di 90° alla volta. Nella
visualizzazione bisognerà dunque considerare di allocare 2 bit (o più
bit) per indicare la rotazione della tile rispetto a un riferimento.
Formato delle Tile
Affrontiamo adesso il formato delle tile. Questo formato però è
da suddividere in due punti di vista, quello del client e quello del server.
Infatti i dati che a uno servono all'altro sarebbero inutili. Ecco una
tabella sintetica:
|
Client |
Server |
Grafica |
|
Al Server non interessa l'aspetto del mondo dal punto di vista estetico |
Entry Tile (Dati Privati) |
Sono dati sotto la diretta gestione del server |
|
Bounding Box |
|
Dipende dall'implementazione. |
Action (Dati Comuni) |
|
|
La Grafica ovviamente è gestita solo dal Client per visualizzare
il mondo. Che questa sia una collezione di mesh tridimensionali, che una
bitmap, non interessa al Server. Infatti in molti casi moderni, come Ultima
Online 3.0 o o nati dalla Open Source generation, a un
server unico corrispondono sia un client a bitmap 2d che un client 3D:
è un ottimo esempio di come un software si può basare benissimo
su molteplici livelli di astrazione.
I Dati Privati invece sono quello che il Client non vede, ma
che il Server manda al Giocatore sotto altri formati. Questi dati comunque
possono essere anche non esistere. Questi dati differenziano due Tile che
hanno lo stesso codice e perciò stesso aspetto. Invece i Dati
Comuni sono dati che come dice la parola sono sia sul Server e sincronizzati
sul Client. Sono questi dati che permettono ai giocatori (e anche ai PNG)
di agire sul mondo. Questi dati possono essere una estensione del concetto
di oggetto, relativo però a entità fisse del paesaggio.
Le Bounding Box indicano un artificio per le prestazioni. Dipende
comunque dall'implementazione e per esempio la bounding box più
semplice (di default) è la Tile stessa. La bounding box è
un concetto che si può applicare sia in 2D che per estensione alla
3D. Rappresenta per estensione una scatola dove dentro è contenuto
un oggetto virtuale che determina alcuni effetti. Questa è una mera
approssimazione dello spazio che il server e il client usano per determinare
questi effetti, visto che controllare poligono per poligono la tile è
un'impresa abbastanza complessa, perciò si raccolgono i poligoni
in una forma semplice che contiene tutti i poligoni legati da una relazione
comune. Le Bounding Box possono perciò non esistere, cioè
occupare l'intera tile, o essere rappresentate da rettangoli bidimensionali
nel caso di Tile su Bitmap, fino all'estensione a rettangoli e sfere tridimensionali.
Le Bounding box poi possono essere diverse tra Client e il Server.
Questo infatti è una conseguenza del fatto che l'interazione tra
PG e ambiente è diversa tra PNG e ambiente, o in ogni caso è
di una forma diversa.
Per esempio le BB posso essere usate per indicare quali zone dello
spazio non possono essere attraversate dai giocatori, dove c'è perciò
un ostacolo, per esempio una tile con una panchina e un lampione . Al Server
che interessa poter muovere molti personaggi contemporaneamente e che preferisce
fare pochi controlli a scapito della precisione, preferisce una bounding
box che racchiuda tutti i due oggetti, mentre il client preferisce poter
spostarsi in modo più libero, ha due box per i due oggetti.
Altre BB (che possono essere comunque le stesse delle intersezioni,
visto che indicano sempre oggetti) possono permettere di interagire tra
Client e il Server. Per esempio per sapere che la panchina si chiama "panchina"
e che permette di "sedersi". Per esempio la stringa "panchina" dovrà
essere memorizzata insieme alla Tile sul Client, ma al Server ciò
non interessa, mentre "sedersi" è un azione che il Server deve per
prima cosa verificare che il Client può fare, ma potrebbe usare
anche per eventuali PNG come azione. Ovviamente questo può
anche essere relativo ai dati privati la stringa "panchina" può
essere inviata dal Server.
Facciamo alcuni esempi su questi dati che mi vengono in mente adesso:
L'albero è uno degli oggetti più interessanti
di Ultima Online. Permette di fare molti esempi:
L'albero per esempio occupa più di una casella elementare, e
perciò può essere spezzato in più di una casella,
o fare in modo che le bitmap siano di dimensione fissa. L'albero permette
anche alcune azioni su di se, per esempio una è quella di essere
"tagliato". Questa azione è possibile cliccando sulla BB dell'albero
che lo contiene o su una BB privata relativa ai rami, la quale BB contiene
una flag che permette il taglio. Il numero di rami che rimangono dall'essere
tagliati è memorizzato insieme al codice dell'albero nella mappa
del mondo, mentre l'azione tagliare è condivisa tra Client e Server.
Lo stesso si può fare con l'arredamento: cliccando sopra a un
comodino potremo vedere cosa contengono i cassetti per esempio.
Dunque ci sono tre concetti di Tile:
-
Versione Client
-
Versione Server
-
Dati privati relativi alla Tile memorizzati in ogni entry della
mappa.
Dati Privati: Overhead di memoria
Come memorizzare i dati privati sul Server? Ci sono diverse teorie e implementazioni.
Una che usa Entrate della mappa di dimensioni fisse, dimensioni che permettono
di inserire i dati privati più grandi, o usare dei puntatori alla
memoria che contiene tutta la Tile della dimensione che si desidera. Questa
soluzione ha senso ovviamente se l'entry massima della mappa ha dimensione
> 12 byte, visto che 2-4byte sono richiesti dall'ID del tipo di Tile e
approssimativamente 8 sono usati dal SO come descrittori della memoria
allocata e un dato privato sarà almeno di 4 byte. Ovviamente se
buona parte del mondo è occupata da tile che non accettano dati
questo puntatore può essere lasciato a NULL risparmiando l'overhead
di allocazione.
Questo metodo è vantaggioso se F < (E-4)/g, con F la percentuale
del mondo che necessita di dati allocati, E i dati extra massimi, 4 è
la dimensione di un puntatore, g la quantità media di memoria allocata
(comprensiva di Overhead di allocazione). Con un mondo coperto al 50% (acqua e
campi) e un overhead di 8 byte si ottiene che se i dati extra massimi sono minori
o uguali a 16 byte conviene utilizzare una allocazione fissa, veloce e facile
da gestire.
Mondo paginato
Ogni Entry a questo punto è formata da questi dati
Chiave |
Dimensione |
Significato |
ID |
2-4 byte |
Che tipo di immagine è rappresentata sul questa casella della
matrice. |
data |
4(puntatore)-16 byte |
I Dati privati che determinano la condizione della tile |
oggetti |
4(puntatore)-16 byte |
Oggetti contenuti nella casella. |
A conti fatti ogni casella occupa da 12 a 36 byte (non considerando eventuale
memoria allocata). Un Server con 64MB di Ram assegnati alla Mappa permette mappe
di dimensione massima 2048*2048 a un livello. Questo limite può essere
superato usando il SO che permette di gestire il file della mappa in modo virtuale
caricando solo i dati che effettivamente servono: una mappa di 64MB memorizzata
su un HD è piccola rispetto alla dimensione del supporto. Questo lavoro
però lo può fare anche l'utente il quale può tenere conto
che più dell 50% del mondo (Mari, Zone desertiche o disabitate) non sono
normalmente attraversate da gente e addirittura i PNG in essi contenute possono
anche essere disabilitati. Ultima Online ha per
esempio una mappa 6144x4096 divisa in sottomappe quadrate di dimensione fissa
256x256 che vengono caricate in memoria solo se servono. In questo modo il mondo
è visto dal''utente lineare, ma dal punto di vista logico è diviso
in sezioni non coincidenti. Una conseguenza di questo sistema di dividere il
mondo in Pagine è che anche verticalmente può non essere
lineare. Possiamo collegare per esempio alla pagina 54, la pagina 96, come suo
livello superiore.
Con questa tecnica basta aggiungere a ogni pagina del mondo un piccolo header
per indicare il codice della pagina superiore e quello della pagina inferiore
(ma anche alle direzioni nord, sud, ovest, est).
Con un indice a 16 bit (1word) e mappe da 256x256 si possono usare mondi fino
a 65536x65536 (4GB di entry).
Dati Privati: Liste di oggetti
Come per i dati privati anche gli oggetti sul terreno necessitano di un
modo di memorizzione sul Server. Il valore di memoria riservata agli oggetti
è lo stesso dei dati privati, intorno ai 12 byte. Diablo
per esempio permetteva di posare un sono oggetto per entry e questa è
stata una filosofia molto seguita soprattutto sui giochi in locale. Se
invece si vogliono posare infiniti oggetti in un posto è preferibile
usare liste di oggetti, dove ogni oggetto ha un puntatore al successivo
in una lista a cascata. I MUD testuali usano questo sistema per esempio,
visto che il giocatore vede l'intera stanza.
C'è anche da considerare che alcune Tile possono essere formate
da contenitori, come cassetti, armadi e roba simile, e contenere addirittura
molteplici di questi contenitori. Ogni contenitore deve avere l'ID o il
puntatore sul Server alla lista degli oggetti (sia che essa sia Dinamica
che Statica).
Se invece si considera il mondo diviso in sottomappe sarà possibile
associare gli oggetti non tanto alla singola Tile, ma alla mappa, indicando
la posizione dell'oggetto all'interno della mappa. Questo è facilitato
dal fatto che in un mondo a pagine, ogni pagina ha un header e la lista
potrà essere inserita (con una qualsiasi tecnica) in questa struttura.
Ricordiamo che anche il giocatore ha una lista degli oggetti trasportati. Questo
complica il salvataggio del giocatore sul Server, visto che la dimensione dei
dati del giocatore varia di molto e probabilmente non sarà possibile
salvare il giocatore su disco nell'offset precedente. Assegnare una dimensione
fissa occupabile massimo dal giocatore (una dimensione abbondantemente sovrastimata)
è una soluzione rapida. Deframmentare e spostare allocazioni, è
una soluzione più lenta e complicata, ma risparmia spazio su disco. La
soluzione scelta per la maggionre, che occupa una quantità enorme di
spazio su disco, è quella di salvare i dati in file di testo, in un formato
totalemente ASCII (non binario). In questo modo è il gestore del file
system che si prende cura di riservare lo spazio per il file, e l'editing delle
strutture è possibile in maniera immediata con qualsiasi editor di testo.
Spesso comunque ho visto che tutti i dati dei giocatori (anche quelli che in
quel momento non stanno giocando) sono caricati in memoria. Dopotutto i dati
occupati dal giocatore saranno di circa 2K massimo, ben poca cosa rispetto al
resto della memoria usata (mappa, oggetti, descrizioni...).
Normalmente in memoria si cerca di non fare distinzione tra NPG/MOB e PG, in
modo da dare da principio la sensazione che chi il giocatore abbia di fronte
possa sia essere un'altro giocatore che una IA. Abbiamo anche detto (e se non
l'ho detto, lo dirò in seguito) che aree non attraversate da nessun giocatore
(giocante) possono essere disabilitate e swappate su disco. Se l'elenco dei
giocatori è riferito a un area, allora è possibile scaricare su
disco mappa, mob e giocatori offline allo stesso modo.
In ogni modo si usa una lista dinamica di persone, dove nell'header dell'area
c'è il puntatore (l'identificatore) a un giocatore, e ogni giocatore
ha un puntatore (l'identificatore) nei suoi dati privati a uno successivo nell'area.
Quando un giocatore esce da un'area prima viene tolto dalla lista e viene aggiunto
(solitamente in cima per velocità) alla lista della nuova area.
Il difetto di avere il Server su una macchina e il Client su un'altra pregiudica
il poter usare puntatore per indirizzare la memoria. Viene perciò più
comodo riferirsi a ogni PG/PNG, oggetto, etc etc con un identificatore
(un numero intero), e sul Server questo identificatore sarà gestito in
un modo, e sul Client (per la gestione della cache di rete) sarà gestito
in un'altro modo.
Server Multiprocessore o Distribuito
Nuovi problemi nascono se si sceglie di usare un sistema multiprocessore
o distribuito. Normalmente il Server deve fare carico di leggere i dati
UDP dai clienti e da questi compiere delle variazioni sul mondo. E' possibile
che questo lavoro non si riesca a fare per le ridotte potenze di calcolo
del sistema. Il mio consiglio a questo punto e associare dei meccanismi
di lettori e scrittori ad Aree del mondo (le dimensioni vanno scelte
usando quelle già stabilite dalle sottomappe o altre, come si vuole).
Il modo più semplice è associare a ogni area un Mutex ed
accedere all'area in mutua esclusione. Ovviamente sarebbe stato impossibile
usare un mutex per ogni tile, mentre, visto che, come da me sopra consigliato,
la lista degli oggetti è associata a ogni area, diventa ovvia la
scelta di associare a ogni area anche un mutex (questa è per esempio
la soluzione adottata dai server Origin di Ultima
Online). Soluzioni più corrette dal punto di vista teorico
le considero inutili, perchè usano più mutex e il tempo necessario
per entrare in una sezione critica in questo modo è più lungo
del tempo in cui si sta dentro la stessa sezione. Riporto comunque, per
completezza, uno dei tanti algoritmi di lettori e scrittori. Per
chi fosse interessato ne può trovare quanti ne vuole su Internet
o su un libro di Sistemi Operativi.
Lettori e Scrittori (Weak Writer) con 3 mutex: mutex.c
E' importante osservervare che se si ha che fare con un multiprocessore, nel
99.9% dei casi saranno due i processori in questione. Il server perciò
dovrà essere diviso normalmente di un fattore 2. Perciò si potranno
fare girare 2 aree per server, o un'area sola, ma per esempio la IA, o un server
Http con le statistiche del gioco, dal sottosistema di input/output da rete,
abiterà in un thread diverso da quello del gioco.
La necessità di implementare il software lato server in maniera distribuita
è dettata dal fatto che normalmente una macchina potrà gestire
al massimo un migliaio di connessioni alla volta (molte meno se in TCP/IP).
Il server perfetto, è un server scalabile linearmente, dove il numero
di utenti massimo è in relazione perfettamente lineare con il numero
di macchine in dotazione.
Gestione Dati: Overhead di comunicazione
Per migliorare le prestazioni del gioco l'utente al momento dell'esecuzione
del software (client) a casa, deve avere già installati i dati che
il server dovrebbe (solo in teoria) mandargli più spesso.
Questi dati possono essere i seguenti.
-
Tile (Grafica)
-
Tile (Informazioni)
-
Personaggi e oggetti (Grafica)
-
Personaggi e oggetti (Informazioni)
-
Mappa
A questi poi si aggiungono i dati relativi alla grafica dei menu e i suoni.
Bisogna comunque fare delle opportune precisazioni, dettate dalla possibilità
che alcune cose siano
a) Troppo Ingombranti sul Computer Client.
b) Facilmente modificabili.
Se sono troppo ingombranti, l'utente potrebbe anche non riuscire (o
non volere) installare il gioco.
La Mappa e la grafica rientrano in questa sezione. La mappa però,
a differenza delle Tile, oltre alla possibilità di occupare molto
di più (teoricamente) della grafica delle tile, potrebbe essere
spesso modificabile.
Se l'oggetto non è modificabile spesso, e occupa molto spazio,
la sincronizzazione dei dati tra il computer Client e il Server dovrebbe
avvenire al momento del lancio del programma, prima cioè di entrare
effettivamente nel gioco. Se invece i dati sono pochi (per esempio la mappa
è divisa in piccole sottomappe) queste possono essere inviate sul
momento.
Il concetto di sincronizzazione introduce la necessità di inserire
in ogni oggetto (Tile, Grafica, Programma, etc...) logico del gioco un
codice sequenziale per sincronizzare Client e Server.
Per esempio un numero sequenziale o l'orario e il giorno in cui è
stato modificato l'oggetto. In questo modo ci possono essere due logiche:
- Il server manda al Client il timecode, il Client confronta con il timecode
locale in cache, e se necessita di un aggiornamento fa una richiesta al server.
Questa richiesta è simile a quella a cui il Client non ha proprio l'oggetto
in questione. A questo punto il Server invia una copia aggiornata dell'oggetto
al Client. Il Server comunque dovrebbe sapere che dato serve in precedenza
al client (e non conosce gli eventi che offline possono essere accaduti al
client... reinstallazioni e simili), e questo spesso non è possibile,
e inviare tutti i possibili dati è notevolmente ridondante.
- Il Client manda al Server il timecode. Se questo codice è 0, il
Client richiede un dato che non ha. Il Server fa il confronto e se c'è
bisogno invia il dato.
L'altra faccia della medaglia è che comunque il pacchetto UDP/IP o TCP/IP
ha un suo header che deve essere inviato. Questo header è di 26 byte
nel caso dell'UDP, mentre nel caso TCP la situazione è molto più
complessa dove poi un po' di banda viene sempre consumata dal protocollo stesso
per restare connesso.
Ulteriori Considerazioni: Aggiornamento
del Mondo
Se il mondo non è normalmente aggiornabile,
mappe di 256x256 sono una ottima soluzione. Capiterà solo ogni tanto
che l'utente dovrò perdere quel mezzo minuto per aggiornare la pagina
modificata e se questo lavoro viene fatto in fase di Login non è
essenziale ai fini del gioco. Resta però il fatto che l'utente non
va di dover memorizzare sul proprio HD 40GB di una mappa di un gioco...
e il primo aggiornamento sarà un po' lento... E' opportuno dunque
dividere le mappe in unità più piccole e tenere sul Client
solo le mappe più usate recentemente (come da sezione precedente
spiegato), chiedendo all'utente quanto spazio riservare alla cache della
mappa. Ovviamente però inviare 1 tile per volta è sconsigliato:
troppi dati da trasferire tra client e server e comunque alla fine occuperebbero
troppo sul computer (Client e Server) in relazione allo spazio effettivamente
coperto.
Per semplificare la gestione del Server si potrebbe
adattare la dimensione della pagina a quella delle mappe che si mandano
al Client, riducendo drasticamente la dimensione delle pagine sul Server.
Questa soluzione, quando possibile, é comunque meglio evitarla per
evitare la frammentazione della memoria sul Server. In queste condizioni
comunque saranno necessari indici per le mappe a 32 bit.
Nel caso di un TimeCode per Entry comunque l'organizzazione
del Server e del Client resterà' a pagine ragionevolmente grandi
(256x256) e nel caso di trasferimenti a pagine queste dovranno avere dimensione
sottomultipla della dimensione della pagina in cache.
Se T è la dimensione del time
code, W la dimensione totale della pagina, D la dimensione
del dato:
Metodo di Archiviazione |
Occupazione di W entry |
Size (W=8*8=64, T=4, D=16) |
World Size (4096*4096: 262144 mappe) |
1 Time Code per Pagina |
T+WD |
1028 byte |
257MB |
1 Time Code per Entry |
(T+D)W |
1280 byte |
320MB |
Visto che in questo caso D>>T, la dimensione del mondo e' solamente
piu grande del 20%. Il problema pero' non e' relativo all'occupazione sul
Server, ma quanti dati trasferire tra il Client e il Server.
Prendiamo dunque il caso di comunicazione della sezione precedente
(2), e valutiamo quanti dati saranno trasferiti tra i due computer:
Caso 1: 1 Time Code Per Entry
-
Il Client si sposta di una casella e invia al Server m TimeCode
di T byte
-
Il Server confronta e con probabilità' di variazione h dei
dati invia al client (T+D)m dati (D sono i dati condivisi
< dati privati). In questo caso D é confrontabile con
T.
Caso 2: 1 Time Code Per Pagina
-
Il Client si sposta di una casella e se esce fuori dalla mappa (m/W)
invia al Server 1 TimeCode di T byte. (m << W)
-
Il Server confronta e con probabilita' di variazione h*W dei dati
invia al client T+WD dati.
Metodo di Archiviazione |
Client -> Server |
Server -> Client |
1 Time Code per Pagina |
(m/W)T |
h*W*(T+WD)*(m/W) |
1 Time Code per Entry |
mT |
h*(T+D)m |
Il sistema a pagine ha senso se ci sono seri problemi di banda tra Client->Server
o comunque se h < (T / WD) e questo numero é dell'ordine
del 5%. Bisogna considerare peró che questa é la probabilitá
di variazione media di TUTTA la mappa del mondo e questo valore, rapportato
ai tempi di comunicazione (1-2 spostamenti al secondo), rappresenta un
evento infrequente. Dunque una variazione dell'5% a ciclo é una
cosa enorme: vorrebbe dire che l'intero mondo possa cambiare costantemente
ogni mezzo minuto.
In questo modo si potrebbe stimare la dimensione della pagina W
ponendo come limite superiore T/hD ma:
-
questo numero é di gran lunga superiore a quello che la banda internet
puó fornire considerando anche che il mondo varia poco.
-
all'utente potrebbe dare fastidio un caricamento di qualche secondo, non
dovuto solo alle variazioni di una mappa che é in cache, ma di una
nuova mappa mentre il giocatore é in esplorazione (comunque in 2KB/s
si possono trasferire piú di 400 entry al secondo).
Ulteriori osservazioni:
-
Il Server sa dove si trova il personaggio sulla mappa e se quella mappa
cambia invia direttamente la variazione al Client.
-
Se una tile si deve animare, codificare l'animazione come una particolare
Tile animata e inviare al Client solo la tile iniziale e la tile ad animazione
avvenuta (questa modifica la potrebbe fare addirittura il Client).
-
Se si inviano pagine alla volta e non singole tile, sarà possibile
comprimere (anche con un semplice algoritmo RLE) i dati da trasmettere.
Questo però potrebbe non portare elevato giovamento alle dimensioni
dei dati trasferiti, e appesantire il Server. Si potrebbero però
identificare in automatico in fase di booting aree che compresse occupano
meno e se l'area non è modificata spesso comprimerla adirittura
subito.
Mappe di trasferimento 32x32 rappresentano
secondo me un compromesso accettabile. Questo valore dipende anche da quanti
dati condivisi e' composta la singola entry. Dati composti da un
semplice indice dell'immagine da 2 byte sono downloadabili dal Client in
1 secondo (salvo eventuali lag della rete).
Lista della spesa
Il Server dovrá essere tarato per supportare un numero
massimo di giocatori ai quali verrá divisa la banda in uscita del Server.
Questo valore potrebbe anche aggirarsi sul migliaio di utenti con una semplice
rete da 10Mb/s. Normalmente il protocollo utilizzato sarà l'UDP, molto
più leggero dalla parte del server, anche se, se il traffico di rete è
sostenuto, i pacchetti tendono a essere eliminati. Si può ovviare cercando
di non far saturare il server o inviando più di un pacchetto uguale UDP
alla volta. La maggior parte dei giochi online adotta questa tecnica. I pacchetti
affidabili dovranno essere implementati a cura del programmatore attraverso tecniche
di req/ack. Il pacchetto massimo dovrà avere una dimensione fissata (Quake
usa pacchetti affidabili di dimensione massima da 1024 byte). Il Collo di
bottiglia a questo punto diverrá la capacitá di elaborazione
del Server, visto che dovrá animare e gestire migliaia di Intelligenze
Artificiali e tenere traccia degli spostamenti di migliaia di utenti.
Il server
dovrà implementare:
- Aggiornamento mappe
- Oggetti, animazioni, effetti visibili
- Comunicazione tra Client (a patto che non si voglia
implementare con Peer to Peer o Brodcasting)
- Notifica delle azioni dei PNG e dei PG sullo schermo
(e solo di quelli)
- Verifica validità (ostacoli) ed esecuzione
azioni
- IA dei PNG
Il Client daltra parte dovrà
cercare di sobbarcarsi la maggior parte del lavoro possibile, scontrandosi con
la possibilità che l'utente possa modificare in qualche maniera il software
locale a proprio vantaggio. Le collisioni (fini), le azioni di informazione,
e altro possono essere lasciate sul client.
- Comunicazione tra Client (peer to peer) o basandosi su
un server opzionale per le comunicazioni
- Cache dei dati più recenti ricevuti dalla rete
- Sincronismo e predizione posizione dei PG
- Rendering della grafica
Realizzazione di un gioco Locale (in breve)
Le limitazioni imposte dalla comunicazione via rete vanno eliminate. Le uniche
limitazioni che si devono comunque considerare sono quelle del Server: limitata
memoria e spazio su HD limitato. Solitamente in un gioco locale comunque si carica
in memoria una sola area alla volta (può essere molto grande comunque,
a seconda dei requisiti di memoria per visualizzarla e gestirla). Ovviamente in
quanto gioco locale è possibile inserire uno storyboard ben fissato che
permette di aggiungere notevole coinvolgimento al gioco. La IA dei PNG sarà
importante per dare maggiore spessore al gioco, visto che non sarà possibile
interagire con altre persone reali.
Varie. Interfaccia con il Mondo. Sincronizzazioni.
Ci sono diverse cose da tenere in considerazione per bilanciare
il gioco.
- Una visuale troppo ampia del mondo richiede molte
comunicazioni tra client e server. Questo é dovuto al fatto che anche
se il mondo non cambia saranno visibili molti piú giocatori e PNG,
oggetti ed effetti, di cui il Server dovra spesso notificare al Client loro
variazioni.
[continua...]
Tool Necessari
A questo punto c'è da chiedersi cosa è
ncessario programmare per fare un Gioco di Ruolo (sia su Rete che
in locale). Sono, a mio avviso, necessari questi programmi:
-
Un Editor per le Tile. Questo programma converte
le tile in un formato orientato al gioco, sia che siano mesh di 3DSMAX,
sia che siano Bitmap. Questo editor dovrebbe permettere di definire le
BoundingBox (o addirittura importarle) per le tile, gli effetti, le animazioni.
-
Un Packer per i files. Un programma che unisce piu
files in un'unico. Avere 2000 files uno per ogni tile e texture non è
efficiente. Queste funzioni se messe su DLL potrebbero essere usate direttamente
dal programma precedente per creare, a editing finito, il pack per la grafica.
- Un editor per i MOB. Programma che permetta di definire
le loro caratteristiche e la loro IA/Script.
-
Editor per gli Oggetti.
-
Un Editor per le Mappe:
-
inserire le Tile
-
Organizzare le Tile su più livelli
-
preview del risultato
-
ottimizzare le mappe
-
posizioni iniziali dei Mob
-
Particolari script (trappole, azioni su ambienti,
collegamenti tra azioni e tile)
-
A questo punto mancano solo il Client e il
Server.
Glossario
DM. Dungeon Master.
Una persona reale che ha il compito di creare avventure e gestire l'ambiente
di gioco
ENTRY. Elemento di una matrice
In questo contesto indica l'elemento (di posizione generica i,j,k)
nella matrice del mondo. Questo deve contenere almeno il codice della tile
che contiene.
MESH.
La Mesh è una collezione di poligoni (normalmente dovrebbero
essere continui e adiacenti) che fanno parte dello stesso oggetto concettualmente.
MOB. Mobile
Un Mob è un qualsiasi personaggio, animale, cosa comandata dalla
IA del Server.
PNG. Personaggio non Giocate
E' un personaggio comandato dal Computer. Ha un set ridotto di azioni
e una limitata interazione con l'ambiente.
Paolo Medici, che ultimamente aveva del tempo da perdere in retorica.
Dalla Serie delle Guide Veloci per fare Software Miliardari |
Tutti i Marchi sono dei rispettivi proprietari: Diablo della Blizzard,
Baldur's Gate della BioWare/TSR/Interplay, Ultima Online della Origin. |
Discuti questo articolo nel forum |
Questo articolo è ha avuto
10073
contatti. Pagina Aggiornata il 24 ottobre 2002