• Non ci sono risultati.

3.2 Moduli e componenti

3.2.4 Entity Manager

SessionResults Al termine della sessione, il server invia ai client questo messaggio, contenente il punteggio raggiunto da ogni utente. Tale messaggio viene interpretato dai client come indicazione che la sessione è terminata, pertanto non è stato definito un messaggio appositamente per quello scopo.

ServerContentMessageHandler e ClientContentMessageHandler

Le classi ServerContentMessageHandler e ClientContentMessageHandler differiscono sol-tanto nel fatto che, lato server, è possibile specificare il destinatario quando si vuole inviare un messaggio e, quando si riceve un messaggio, accedere al mittente. Al contrario, per il client, che può inviare e ricevere messaggi solo dal server, tali informazioni non sono necessarie.

Poiché tutto il codice per la gestione dei pacchetti è contenuto in ContentMessage-Handler, non c’è duplicazione di codice.

e in un Prefab (B.5) contenente tutti gli elementi costituenti l’entità.

Al fine di gestire in modo ordinato le entità di gioco, è stato introdotto il modulo Entity Manager, che in parte ha la funzione di contenitore di entità, e in parte svol-ge alcune importanti funzioni quali la creazione e distruzione di entità, l’aggiornamento delle stesse, e la sincronizzazione del loro stato tra server e client. Inoltre, tale classe contiene alcuni metodi di uso comune per l’interazione delle entità; ad esempio FindInte-ractable_Contact() e FindInteractable_Ray() permettono di trovare un oggetto che possa interagire con un’entità, rispettivamente tramite contatto diretto e a distanza.

Entity

La classe Entity è una delle più importanti del framework, in quanto la complessità del codice dell’applicazione risiede in gran parte in quelle classi che derivano da Entity e/o dipendono da essa, ad esempio l’ApplicationMode e le classi da esso derivate.

Classe Entity.Definition Poiché ogni tipologia di entità avrà le sue caratteristiche peculiari, si è reso necessario trovare una soluzione flessibile che permettesse, da una parte, di definire come l’entità è fatta, e, dall’altra, di trasferire tale definizione, o descrizione, dal server, in cui tale definizione è generata, al client, con un intervento minimo da parte dello sviluppatore.

La classe Entity.Definition, di seguito illustrata, è la soluzione proposta:

p u b l i c c l a s s D e f i n i t i o n {

p u b l i c b y t e R e s o u r c e I d { get; }

p u b l i c V e c t o r 3 S t a r t P o s i t i o n { get; } p u b l i c Q u a t e r n i o n S t a r t R o t a t i o n { get; }

p u b l i c D i c t i o n a r y<byte, object> P a r a m e t e r s { get; } = new D i c t i o n a r y<byte, object>() ;

// ...

}

Il campo ResourceId identifica il percorso del prefab dell’entità, che può essere caricato a runtime tramite la funzione UnityEngine.Resource.Load<T>(string path).

Dopo aver creato un’istanza del prefab, si ottiene il componente Entity tramite metodo UnityEngine.GameObject.GetComponent<T>(), eseguendo, quindi, il metodo virtuale Entity.Init(byte id, Definition definition, EntityManager entityManager), che permette di configurare l’entità con i parametri forniti dalla definizione data.

All’occorrenza, lo sviluppatore potrà ridefinire, tramite override, il metodo Init() di una classe che deriva da Entity, in modo da configurarla in modo opportuno.

Il campo Parameters di Entity.Definition permette allo sviluppatore di passare al me-todo Init() dei parametri di configurazione dell’Entity (ad esempio l’altezza, piuttosto che la velocità).

L’uso incoraggiato è definire un enum per ogni classe che deriva da Entity, per indicare il tipo di parametro. Di seguito è mostrato un esempio di questo approccio:

p u b l i c c l a s s R o b o E n e m y : E n e m y // N o t a : E n e m y d e r i v a da E n t i t y . {

p u b l i c e n u m D e f i n i t i o n P a r a m e t e r : b y t e {

A v e r a g e S p e e d = 0 , M a x M o v e m e n t R a d i u s , };

p u b l i c o v e r r i d e v o i d I n i t (b y t e id , D e f i n i t i o n d e f i n i t i o n , E n t i t y M a n a g e r e n t i t y M a n a g e r )

{

b a s e. I n i t ( id , d e f i n i t i o n , e n t i t y M a n a g e r ) ; m _ S t a r t P o s i t i o n = d e f i n i t i o n . S t a r t P o s i t i o n ; m _ R i g h t = t h i s. t r a n s f o r m . r i g h t ;

m _ U p = t h i s. t r a n s f o r m . up ; var a v g S p e e d I n d e x =

(b y t e)D e f i n i t i o n P a r a m e t e r. A v e r a g e S p e e d ; m _ A v e r a g e S p e e d =

(f l o a t) d e f i n i t i o n . P a r a m e t e r s [ a v g S p e e d I n d e x ];

var m a x M o v R a d i u s I n d e x =

(b y t e)D e f i n i t i o n P a r a m e t e r. M a x M o v e m e n t R a d i u s ; m _ M a x M o v e m e n t R a d i u s =

(f l o a t) d e f i n i t i o n . P a r a m e t e r s [ m a x M o v R a d i u s I n d e x ];

} // ...

}

Sincronizzazione dello stato Al fine di minimizzare lo sforzo richiesto allo sviluppa-tore per sincronizzare lo stato delle entità tra il server e i client, è stata introdotta la classe Entity.Image, che, traendo vantaggio dallo strumento della reflection del linguaggio C#, ha permesso di semplificare il codice per la sincronizzazione.

L’idea alla base del funzionamento della classe Entity.Image è l’utilizzo di attributi per qualificare in modo opportuno quelle variabili, campi e proprietà, che devono essere sincronizzati tra le varie parti in gioco. A tal fine, sono stati definiti degli attributi per specificare le variabili da sincronizzare, e/o le funzioni da eseguire sia nel server che nei client.

TransformInterpolator Il TransformInterpolator è una classe derivata da MonoBeha-vior [B.1], che può essere assegnata a un qualunque GameObject del prefab di un Entity per sincronizzare la posizione e/o la rotazione del GameObject stesso tra server e client.

Tale classe è stata pensata per supportare il meccanismo dell’interpolazione delle entità descritto in [3.1.4], da cui il nome.

Dopo aver assegnato il componente in esame ad un GameObject, nell’Inspector di Unity è possibile indicare se si vuole sincronizzare la sola posizione, la sola rotazione, o entrambe (come illustrato in figura 3.6).

Figura 3.6. Nell’inspector di Unity è possibile selezionare il tipo di TransformInterpola-tor, e, quindi, quali informazioni sincronizzare tra server e client.

Message System

Il modulo Entity Manager contiene un campo di tipo event denominato OnEntityMessa-geEvent, che viene lanciato tutte le volte in cui un’entità invia un nuovo messaggio; se uno degli altri moduli o l’ApplicationController è interessato ad ascoltare i messaggi delle entità, può iscriversi a tale evento.

Un esempio di questo tipo è l’ApplicationMode, che in funzione dei messaggi che gli giungono dalle entità, stabilisce come far avanzare lo stato della sessione.

La scelta di adottare questo pattern di programmazione ha permesso di separare la logica delle entità dalla logica dell’ApplicationMode, cioè della modalità di applicazione

o di gioco, rendendo possibile l’aggiunta di nuove modalità con un intervento minimo o nullo sulla code-base esistente.

ServerEntityManager

Ciò che distingue maggiormente la variante dell’EntityManager presente sul server, dalla classe base, è la presenza dei seguenti metodi:

a) GenerateWorldImage: genera lo stato del mondo, che contiene lo stato delle entità.

b) GenerateCumulativeWorldImage: combina più stati del mondo per ridurre la dimen-sione complessiva, senza perdere informazione.

c) ExecuteUserActorRemoteCommand: esegue il comando remoto proveniente da un client.

ClientEntityManager

Il ClientEntityManager mette a disposizione due metodi aggiuntivi per la sincronizzazione dello stato delle entità (SyncEntities()) e l’esecuzione, in locale, del comando che l’utente ha impartito all’entità che egli controlla (ExecuteUserActorActions()).

Documenti correlati