Per comprendere l’architettura delle moderne piattaforme per lo sviluppo dei videogiochi è utile la rappresentazione del modello a strati, che caratterizza anche i sistemi di elaborazione e in generale i sistemi complessi. I sistemi complessi sono caratterizzati da un insieme di componenti(anche centinaia o migliaia) che interagiscono fra loro. I modello a strati consiste nel suddividere il sistema in vari livelli di astrazione: ciascun livello è costituito da vari componenti che svolgono le azioni all’interno del livello stesso e sfruttano i servizi offerti dai livelli inferiori.
Ad esempio il modello a strati di un computer rappresenta le funzioni mediante una gerarchia di livelli di astrazione, dal livello applicativo fino al livello hardware di base: ogni funzione di un dato livello viene implementata utilizzando servizi offerti da livelli inferiori, senza preoccuparsi dei dettagli tecnici, spesso molto complessi, dell’implementazione dei livelli sottostanti. Ad esempio qualunque operazione aritmetica o logica prevista in un programma applicativo, viene in realtà implementata tramite opportuni circuiti elettronici codificati nei transistor della CPU del computer, ma questi dettagli sono del tutto trasparenti al programmatore.
Questa suddivisione modulare permette di comprendere meglio l’architettura e le funzioni del sistema, e facilita grandemente l’attività di manutenzione dei sistemi.
Per uno studio approfondito dell’architettura dei moderni game engine sono utili i libri menzionati a piè di pagina[1][2][3].
Componenti di un moderno sistema per videogiochi
Lo sviluppo dei videogiochi è un’attività molto complessa e sarebbe quasi proibitiva senza una architettura modulare e l’ausilio di ambienti di sviluppo efficienti. Allo stato attuale non esiste uno standard riconosciuto per l’architettura dei sistemi per lo sviluppo dei videogiochi. Tuttavia, nonostante le differenze, i vari prodotti presenti sul mercato hanno in comune una struttura composta dai seguenti livelli di astrazione:
LIVELLO 5 – Il gioco: descrizione, logica, regole, obiettivi
Un videogioco può essere definito come un insieme di attività di risoluzione di problemi, orientate a raggiungere certi obiettivi, nel rispetto di precise regole. Lo sviluppo di un videogioco parte da una idea iniziale che passa attraverso varie fasi di creatività: incubazione, approfondimento, valutazione ed analisi di dettaglio. L’analisi di dettaglio porta allo sviluppo del documento di disegno che descrive i vari aspetti del videogioco: regole, obbiettivi, attori, risorse, livelli, suono, musica, interfaccia utente, ecc.
Il documento di disegno è l’input per lo sviluppatore, il quale tramite il framework software associato al game engine può tradurre l’idea iniziale in un gioco effettivo sule varie piattaforme.
LIVELLO 4 – Il game engine e i suoi sottosistemi
L’attività dei programmatori sarebbe estremamente complessa e quasi proibitiva senza l’ausilio dei moderni motori grafici per videogiochi (game engine), i quali forniscono un ambiente integrato (framework software) composto da strumenti di sviluppo e da componenti software riutilizzabili, che consentono uno sviluppo veloce e semplificato.
Il game engine permette di astrarre dai dettagli hardware e software di basso livello (grafica, fisica, sistemi operativi, ecc.); lo sviluppatore può concentrarsi maggiormente sulle regole del gioco e sulla interazione fra il giocatore (player) e gli altri soggetti presenti nell’ambiente del gioco. Ad esempio un programmatore che deve disegnare un personaggio 3D in una scena del gioco, non deve preoccuparsi delle complesse operazioni che avvengono a livello hardware necessarie per preparare l’immagine nel buffer e inviare i pixel sullo schermo. I game engines supportano l’utilizzo dei moderni linguaggi di programmazione (C++, C#, Java, ecc.) i quali permettono di sviluppare le varie applicazioni web e multimediali, tra le quali i video giochi. Esempi di motori di grande successo sono Unreal Engine e Unity3D.
I motori grafici hanno un’architettura modulare; i principali sottosistemi sono i seguenti:
La rappresentazione del mondo 3D e il game loop
L’universo del gioco è l’ambiente 2D/3D in cui vivono i vari oggetti: caratteri, videocamere, automobili, props, ecc.
Una funzione fondamentale del game engine è quella di fornire un modello astratto dell’universo del gioco e di tutti gli oggetti che sono all’interno. Per ogni oggetto bisogna registrare le varie proprietà e lo stato in ogni istante.
L’aggiornamento periodico dello stato dei vari oggetti è uno dei compiti fondamentali del Game Loop. Sostanzialmente il game loop in ogni ciclo aggiorna lo stato dei vari oggetti (posizione, collisioni, ecc.) ed effettua il rendering grafico degli oggetti (GameObjects) della frame.
Ogni GameObject è composto da un insieme di componenti: l’oggetto Transform che registra la posizione, la rotazione e la scala, il renderer grafico, la fisica, lo script nel quale è codificata la logica, ecc.
La scena rappresenta un livello in un gioco, coordina e gestisce i GameObjects e i componenti loro attaccati (nemici, terreni, luci, videocamere, ecc). La struttura di una scena può essere di questo tipo:
Alcuni motori utilizzano una tradizionale gerarchia di classi per rappresentare i vari tipi di oggetti collegati tramite il meccanismo dell’ereditarietà per riutilizzare il codice e le funzionalità. Tale architettura può essere inefficiente nel caso di giochi particolarmente complessi nei quali vanno gestite mote eccezioni. In tali situazioni può essere utile l’approccio basato sull’architettura per componenti, nella quale si privilegia la composizione di classi, piuttosto che l’ereditarietà.
Gestione input
L’utente interagisce con il gioco in modo intenso, tramite i dispositivi di input: tastiera, mouse, joysticks, touch, ecc. Il motore deve riconoscere i comandi inviati dall’utente ed effettuare i necessari aggiornamenti sui dati e le proprietà degli oggetti interessati. L’input vien gestito in due modalità fondamentali: gestione ad eventi o gestione del polling. La prima modalità prevede la possibilità di intercettare in tempo reale i vari eventi (es. la pressione di una tasto della tastiera) ed eseguire l’azione appropriata. La tecnica del polling consiste nell’interrogazione periodica dello stato degli oggetti (es. la posizione sullo schermo del mouse) e mettere a disposizione del programmatore i valori per le opportune elaborazioni.
Gestione della grafica 2D/3D
Il motore grafico deve preparare e visualizzare sullo schermo le immagini dei vari oggetti e degli ambienti nei quali si svolge il gioco. Nella grafica 2D vengono utilizzate le textures per formare le immagini della scena. In ambiente 3D le textures vengono utilizzate per rivestire gli oggetti 3D (le mesh).
Per avere una simulazione verosimile della realtà è necessaria un’alta frequenza di aggiornamento delle frame, in modo che l’azione sia svolta in modo continuo, senza effetti di sfarfallio.
I motori grafici utilizzano delle API che richiamano prodotti software diffusi come OpenGL e Direct3D, i quali interfacciano con l’hardware della varie schede grafiche. Durante ogni ciclo del Game Loop gli oggetti vengono preparati e ridisegnati sullo schermo.
I giochi 3D utilizzano in genere degli asset creati con prodotti specifici come Blender, Maya, 3ds Max, mentre per la grafica 2D si usano in genere prodotti come PhotoShop. Gli asset realizzati vengono poi importati nel game engine, con gli opportuni adattamenti e conversioni di formato. Una volta importati, gli oggetti possono essere aggiunti al gioco e modificati creando ambienti e attori realistici.
Il valore utilizzato in genere per la frequenza di aggiornamento delle frame (frame rate) è di 30 fps (frame per secondo). Questo comporta che ogni fotogramma rimane sullo schermo per circa 0,0333 secondi prima di essere aggiornato con il successivo frame, e quindi un comando di input impiega questo tempo prima di esser eseguito e mostrare suoi effetti. In alcune situazioni il frame rate può essere anche superiore (ad esempio 60 fps) per gli oggetti che si muovono velocemente; questo permette di avere una migliore responsività. Naturalmente lo schermo video sul quale viene visualizzata la scena deve essere predisposto per gestire il frame rate, altrimenti non si noterà una differenza utilizzando frame rate più alti.
Gestione audio (audio engine)
Il suono e la musica sono parte integrale di ogni gioco. Il sottosistema audio genera la musica di sottofondo e gli effetti sonori del gioco. I programmi del sottosistema possono utilizzare la CPU oppure processori dedicati e si possono appoggiare ad un livello di astrazione tramite API, come Open-AL, Direct3D, ecc. Gli effetti sonori vengono inseriti nell’ambiente 3D del gioco e il volume può essere regolato in funzione della distanza del proprio carattere relativa al suono.
La gestione del suono è una procedura complessa; per avere maggiore realismo si possono aggiungere effetti di modulazione della frequenza e di riverbero per simulare il suono prodotto dalle riflessioni sulle pareti.
La produzione del suono non deve interferire con la altre funzioni del gioco o creare ritardi nelle operazioni di aggiornamento e rendering grafico. Per questo il suono dovrebbe essere sempre caricato nella memoria RAM che è molto veloce e non caricato dal disco, poiché comporterebbe dei ritardi. Tuttavia la memoria RAM è limitata e questo aumenta la complessità della gestione. Gli algoritmi dovrebbero essere in grado di mantenere in memoria solo le parti di suono necessari in una data situazione del gioco, rilasciando quelle non necessarie.
Nell’ambiente Unity ci sono due componenti per gestire il suono: un AudioListener che ha la funzione di ascolto e un AudioSource che genera il suono. Si può collegare un AudioSource ad ogni oggetto del gioco, assegnare un suono specifico e tramite uno script associato generare il suono tramite la funzione Play(). Per generare il suono sono naturalmente necessari degli asset audio nei vari formati: .mp3, .wav ecc.
Un altro aspetto importante da considerare è la distinzione fra suoni in ambiente 2D o 3D. Il suono 2D il volume è indipendente dalla distanza dell’AudioListener. Nell’ambiente 3D il volume del suono può variare in funzione della distanza fra la sorgente audio e la video camera alla quale è attaccato l’AudioListener.
Motore fisico
Nei moderni videogiochi la fisica è molto importante. Il motore fisico è un sottosistema che simula gli effetti della gravità e delle altre leggi della fisica classica (le collisioni fra oggetti, la riflessione e rifrazione della luce, la dinamica dei fluidi, ecc.). Gli oggetti della scena del gioco si comportano come se fossero corpi materiali immersi in un ambiente reale nel quali valgono le leggi della fisica.
Il framework Unity utilizza il motore fisico PhysX per gestire le simulazioni fisiche. Il motore fisico può essere controllato tramite gli scripts associati ai game objects. In questo modo possiamo determinare il comportamento dinamico di un veicolo, di un proiettile, ecc.
La gestione della fisica degli oggetti richiede grande potenza elaborativa; è quindi opportuno limitare il numero degli oggetti materiali che interagiscono ed utilizzare forme geometriche semplici (esempio la forma sferica).
Gestione collisioni
In molti giochi deve essere gestito il fenomeno della collisione fra due oggetti. La collisione fra due corpi rigidi si verifica quando la distanza fra le due forme geometriche raggiunge un valore sotto una data soglia, fissata dallo sviluppatore in base alle esigenze specifiche del gioco. La rilevazione della collisione è una informazione necessaria in diverse situazioni: per sapere se ci si sta avvicinando troppo ad un confine pericoloso o ad una parete, se un proiettile sparato ha colpito il bersaglio, ecc.
Uno dei metodi più semplici per intercettare la collisione fra due oggetti è di rappresentare gli oggetti con forma circolare o sferica e intercettare il momento nel quale la distanza fra i due centri è inferiore alla somma dei due raggi. La forma sferica permette di rilevare la collisione in modo molto semplice: basta confrontare la distanza afra i due oggetti con la somma dei due raggi.
Animazione personaggi
In ogni scena di un gioco sono presenti vari personaggi: esseri umani, robot, animali, automobili, ecc. Tra gli elementi da considerare, oltre ai personaggi, vanno inclusi anche le sorgenti di luce, i colori, i punti di vista, le ombre, ecc. Scopo del sottosistema di animazione è di simulare il movimento di ogni elemento della scena caratteri in modo che appaia il più naturale possibile.
Il processo di animazione è assai complesso, ed è stato perfezionato nel corso degli anni. Il movimento viene reso possibile attraverso una sequenza di immagini, con una frequenza adeguata al cervello umano (24 frame per secondo).
La tecnica attualmente più usata è l’animazione a scheletro (skeletal animation); viene disegnata una struttura articolata, simile ad uno scheletro, dell’oggetto, mediante una struttura ad albero di articolazioni, ognuna rappresentata mediante una matrice di trasformazione che memorizza la posizione e l’orientamento.
La superficie dell’oggetto viene collegata allo scheletro mediante la tecnica dello skinning. Lo skinning è un metodo utilizzato per rivestire lo scheletro con una mesh deformabile, che è in grado di adattarsi ai movimenti delle ossa. Per avere un maggiore realismo, vengono anche disegnate e definite le caratteristiche estetiche attraverso le texture e lo shading.
Intelligenza artificiale (AI)
Qualche forma di intelligenza artificiale è spesso inclusa nei giochi più complessi. Il gioco è più interessante in quanto i personaggi sembrano in grado di controllare le loro azioni in funzione della situazione. Per l’utilizzo di funzioni AI nel gioco si possono utilizzare librerie apposite esterne, oppure algoritmi forniti dal game engine oppure, sviluppati all’interno del progetto, mediante il linguaggio di Scripting (C++, C#, ecc.).
I framework più importanti (Unity3D, Unreal Engine, ecc.) mettono a disposizione dei prodotti che permettono di definire le azioni e i comportamenti degli attori anche tramite strumenti grafici di decisione, senza dover scrivere complicate righe di codici.
Una situazione tipica è quella di controllare dei caratteri NPC (non-player character) in modo che le loro azioni si adattino al contesto, da sembrare intelligenti. Il carattere NPC deve rispondere alle azioni del giocatore (player). Un algoritmo molto usato per questo si basa sull’utilizzo delle macchine a stati finiti (FSM). Vengono individuate le varie possibili situazioni (stati), e per ognuna vengono programmate le azioni opportune.
Ad esempio, per rappresentare le azioni di un carattere NPC che insegue il Player, si può usare il seguente grafico:
Un’altra problematica interessante è rappresentata dallo sviluppo di algoritmi di world-navigation o path-finding, cioè di ricerca dei percorsi più efficienti per gli agenti. I protagonisti dei giochi, caratteri, animali, veicoli, devono muoversi secondo certi obiettivi e devono essere in grado di identificare i percorsi più efficienti dalla partenza all’arrivo. Naturalmente questo richiede di conoscere la geografia locale, i muri, le pareti, i ponti, i fiumi, i palazzi, la posizione delle altre persone, ecc. Uno dei migliori algoritmi di path-finding è l’algoritmo A*.
Altre forme di AI sono le reti neurali e la logica fuzzy, utilizzate per prendere decisioni in condizione di incertezza.
Gestione risorse
Ogni gioco utilizza risorse di vario tipo, registrate su archivi esterni, che devono essere caricate in memoria al momento opportuno. Il sottosistema Gestione risorse mantiene l’inventario di tutte le risorse utilizzate, gestisce il caricamento in memoria dei vari assets(livelli, scene, sprite, texture, audio, ecc.) e deve evitare i problemi di diminuzione delle prestazioni o cadute di sistema. L’obiettivo principale è di rendere disponibili le risorse al momento necessario, compatibilmente con la memoria principale disponibile e la velocità di trasferimento dati con i supporti di memoria esterna.
Un altro aspetto importante è la possibilità di sospendere il gioco ad un certo punto, e poter riprendere in una fase successiva dalla stessa situazione. Il sistema deve esser in grado di salvare lo stato dei vari oggetti al momento della interruzione e ripristinare l’esatta configurazione in un momento successivo. L’utente deve avere la possibilità tramite un opportuno menu, di poter scegliere se riprendere il gioco dall’interruzione, oppure ripartire di nuovo dall’inizio.
Gestione della rete
Molti giochi prevedono la possibilità di avere una pluralità di giocatori collegati in rete. Lo schermo può essere suddiviso in varie componenti. L’architettura principale per i giochi multiplayer è rappresentata dal modello client/server: ogni giocatore è un “cliente” che comunica con un computer centrale, il “server”, sul quale viene effettivamente eseguito il gioco. Il problema fondamentale è quello di riuscire a replicare lo stato del gioco su tutte le macchine collegate in rete, e quindi mantenere i vari giocatori tutti sincronizzati con lo stato del server.
Tra i fattori necessari per il successo di questo tipo di giochi ci sono i seguenti:
- collegamento ad alta velocità e ad alta affidabilità
- grande potenza elaborativa delle CPU/GPU dei dispositivi
Interfaccia utente (GUI)
Tutti i motori per videogiochi hanno un sottosistema che gestisce l’interfaccia grafica con l’utente: componenti visuali, menu, pulsanti, dialoghi, slider, layout, ecc. Una GUI è semplicemente una lista di testi o immagini che possono essere cliccate dall’utente tramite la tastiera, il mouse o il gamepad.
Scripting
La logica del gioco viene scritta dal programmatore tramite delle classi associate ai vari oggetti. Tra i linguaggi più utilizzati ci sono il C++ (Unreal Engine) e il C# e JavaScript (Unity3D). Quest linguaggi sono portabili senza cambiamenti su una pluralità di piattaforme: PC, Mac, Web, dispositivi iOS e Android, Playstation, Xbox, ecc.
In Unity3D ad ogni GameObject sono associati vari componenti. Uno dei componenti è rappresentato dagli scripts, che ereditano in genere dalla classe MonoBehaviour. Ogni script contiene varie funzioni o metodi, per gestire i vari eventi: start, update, gestione di collisioni fra particelle, movimento della videocamera, manipolazione della illuminazione, ecc.
UnrealEngine4 utilizza un sistema di scripting visuale chiamato Blueprint, che permette di definire il comportamento di classi e oggetti. In teoria è possibile sviluppare un gioco senza scrivere una riga di codice. Tuttavia il sistema permette al programmatore di scrivere anche direttamente del codice nel linguaggio C++.
LIVELLO 3 – Astrazione Hardware (HAL)
I sottosistemi del game engine in genere effettuano chiamate al componenti del livello hardware sottostante HAL (Hardware Abstraction Layer) come OpenGL o DirectX. Le chiamate vengono effettuate mediante della API (Application Programming Interface) standard.Uno strato di astrazione hardware permette ad un programma applicativo di comunicare con un hardware specifico. Lo scopo principale del componente HAL è di nascondere le differenti architetture hardware e fornire una interfaccia uniforme ai programmi.
LIVELLO 2 – Il sistema operativo
Una funzione fondamentale dei game engines è la possibilità di effettuare uno sviluppo multipiattaforma; un gioco sviluppato con il framework può essere esportato su una miriade di piattaforme diverse: Pc Windows, Mac, IOS, Android, Xbox, console, ecc.
I principali motori (Unreal Engine, Unity3D) hanno il supporto integrato di facile utilizzo per la pubblicazione di un gioco su diverse piattaforme.
LIVELLO 1 – Driver dei dispositivi hardware
I driver dei dispositivi (device driver) sono componenti software che il sistema operativo utilizza per interagire con i vari tipi di dispositivi (scheda grafica, scheda audio, modem, tastiera, mouse, joystick, ecc.). Le funzioni presenti nei drivers contengono il codice specifico per i singoli dispositivi hardware, che interagiscono con il video game.
LIVELLO 0 – Hardware
Il livello hardware rappresenta il livello fisico. L’hardware è lo schermo video che visualizza le immagini del gioco, la tastiera, il joystick, il microfono, lo smarthphone, ecc. I moderni videogiochi possono esser eseguiti su una molteplicità di piattaforme hardware (PC, Mac, Xbox, smarthphone, ecc).
Conclusione
I moderni motori sono uno strumento indispensabile per gestire lo sviluppo e il test dei videogiochi. Essi mettono a disposizione i materiali riutilizzabili di base, comuni a tutti i giochi, che lo sviluppatore può utilizzare per costruire il suo gioco. Il programmatore può così concentrare la sua creatività sulla parte applicativa: la scena, i caratteri, le texture, l’interazione fra gli oggetti, ecc.
Diversi motori per videogiochi sono disponibili sul mercato; tra i più importanti ricordiamo Unity3D, Unreal Engine, CryEngine, OGRE3D (quest’ultimo open source). Essi differiscono per qualità, facilità di utilizzo, prestazioni, prezzo.
Bibliografia
[1]Jason Gregory, Game Engine Architecture, (CRC Press, 2014)
[2]Robert Nystrom, Game Programming Patterns,(genever benning, 2014)
[3]Eric Lengyel, Mathematics for 3D Game Programming and Computer Graphics, (Course Technology, 2012)
0 commenti