Università degli Studi di Padova - SIAGAS

Pagina creata da Nicolo Proietti
 
CONTINUA A LEGGERE
Università degli Studi di Padova - SIAGAS
Università degli Studi di Padova
 Dipartimento di Matematica "Tullio Levi-Civita"
                Corso di Laurea in Informatica

       Gameplay Programming: analisi e
      progettazione di un gioco strategico
                       Tesi di laurea triennale

Relatore
Prof. Paolo Baldan

                                                        Laureando
                                                  Marco Focchiatti

                     Anno Accademico 2017-2018
Università degli Studi di Padova - SIAGAS
Marco Focchiatti: Gameplay Programming: analisi e progettazione di un gioco strategi-
co, Tesi di laurea triennale, c 27 Settembre 2018.
Università degli Studi di Padova - SIAGAS
Dedicato alla mia famiglia e ai miei parenti.
Università degli Studi di Padova - SIAGAS
Università degli Studi di Padova - SIAGAS
Sommario

Il presente documento descrive il lavoro da me svolto durante il periodo di stage, della
durata di trecentoventi ore, presso l’azienda Ubisoft.

Lo scopo principale dello stage consisteva nella mia integrazione in due task force[g] di
ricerca e sviluppo riguardanti l’analisi di problematiche di un gioco strategico a turni.
In primo luogo era richiesta la conoscenza dell’engine e delle convenzioni di codifica
aziendali, prerequisito fondamentale per poter analizzare i problemi e progettarli.

In secondo luogo era richiesta una continua interazione con i team di design per
il feedback sul risultato da ottenere e con il team di engine developer per la compatibi-
lità con quest’ultimo.

L’applicativo su cui ho lavorato è l’engine Snowdrop, engine proprietario di Ubisoft,
sviluppato da Massive Entertainment.

                                           v
Università degli Studi di Padova - SIAGAS
Università degli Studi di Padova - SIAGAS
“No matter what happen I’ll keep on moving.
                                      Until this life runs out of me I’ll keep on walking”
                                                           — Allen Walker, D-Gray Man

Ringraziamenti

Innanzitutto, vorrei esprimere la mia gratitudine alla Professore Paolo Baldan, relatore
della mia tesi, per l’aiuto e il sostegno fornitomi durante la stesura del lavoro.

Ringrazio sentitamente Tiziano Sardone, mio tutor aziendale, per avermi aiutato a inte-
grami nel gruppo di lavoro, e ringrazio il team di development per l’aiuto e la disponibilà.

Padova, 27 Settembre 2018                                                Marco Focchiatti

                                            vii
Università degli Studi di Padova - SIAGAS
Università degli Studi di Padova - SIAGAS
Indice

1 Introduzione                                                                                                                            1
  1.1 L’azienda . . . . . . . . . . . . . .              .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    1
       1.1.1 Ubisoft Milan Studios SRL                   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    1
  1.2 L’idea . . . . . . . . . . . . . . . .             .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    2
       1.2.1 Obiettivi . . . . . . . . . .               .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    2
  1.3 Organizzazione del testo . . . . . .               .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    2
  1.4 Resoconto Orario . . . . . . . . . .               .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    3

2 Strumenti e Tecnologie                                                                                                                  5
  2.1 Tecnologie . . . . . . . . .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    5
      2.1.1 C++ 11 e C++ 14          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    5
  2.2 Strumenti . . . . . . . . .    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    9
      2.2.1 Visual Studio . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    9
      2.2.2 Perforce . . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   10
      2.2.3 Skype for business       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   10
      2.2.4 Confluence . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   11
      2.2.5 Mattermost . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   11
      2.2.6 Jira . . . . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   12
      2.2.7 Swarm . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   12
      2.2.8 Snowdrop . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   13

3 Introduzione al progetto                                                                                                               15
  3.1 Pattern architetturali . . . . . . . .             .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   15
       3.1.1 Entity-Component-System .                   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   15
       3.1.2 ECS Strict . . . . . . . . .                .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   16
  3.2 Confronto ECS e OOP . . . . . . .                  .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   16
  3.3 Struttura del gioco e modularità .                 .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   16
  3.4 Character . . . . . . . . . . . . . .              .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   17
  3.5 Layers . . . . . . . . . . . . . . . .             .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   17
  3.6 Character life cycle . . . . . . . . .             .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   18

4 Combat System                                                                                                                          19
  4.1 Interfaccia del sistema di combattimento . .                           .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   19
      4.1.1 Console . . . . . . . . . . . . . . . .                          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   19
      4.1.2 Element Factory . . . . . . . . . . .                            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   19
      4.1.3 Combat Interface . . . . . . . . . . .                           .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   20
  4.2 I componenti del sistema di combattimento                              .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   20
  4.3 I Player . . . . . . . . . . . . . . . . . . . .                       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   22

                                                 ix
Università degli Studi di Padova - SIAGAS
x                                                                                                                             INDICE

    4.4   Elementi e Fighter . . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   22
          4.4.1 Interfaccia degli elementi        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   23
    4.5   Sistemi . . . . . . . . . . . . . . .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   23
    4.6   Azioni . . . . . . . . . . . . . . .    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   24

5 AI                                                                                                                                  27
  5.1 Finite State Machine . . . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   27
       5.1.1 Vantaggi . . . . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   27
       5.1.2 Svantaggi . . . . . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   27
  5.2 Behavior Tree . . . . . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   28
       5.2.1 Vantaggi . . . . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   29
       5.2.2 Svantaggi . . . . . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   29
  5.3 Goal Oriented Action Planning           .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   29
       5.3.1 Vantaggi . . . . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   30
       5.3.2 Svantaggi . . . . . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   30
  5.4 Prototipazione . . . . . . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   30
       5.4.1 Flow dell’AI . . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   30
       5.4.2 Comportamento . . . .            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   30
  5.5 Planner . . . . . . . . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   31
  5.6 A* . . . . . . . . . . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   31
  5.7 Iterative-Deepening A* . . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   32
  5.8 Ricerca Euristica . . . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   32
  5.9 Depth-First Branch-And-Bound            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   32
  5.10 Recursive Best-First Search . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   32
  5.11 MiniMax . . . . . . . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   33
  5.12 MiniMax AlphaBeta pruning .            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   34
  5.13 MonteCarlo Tree Search . . . .         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   34
  5.14 Confronto riassuntivo . . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   36
       5.14.1 Scelta dell’algoritmo . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   36

6 Conclusioni                                                                                                                         37
  6.1 Raggiungimento degli obiettivi          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   37
      6.1.1 ECS . . . . . . . . . . .         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   37
  6.2 Conoscenze acquisite . . . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   38
  6.3 Valutazione personale . . . . .         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   38

Glossario                                                                                                                             39

Acronimi                                                                                                                              45

Bibliografia                                                                                                                          47
Elenco delle figure

 1.1   Logo Ubisoft . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .      1

 2.1   Dichiarazione Lambda . . . . . . . . . . . . . . . . . . . . . . . . . . .        6
 2.2   Schermata di Visual Studio . . . . . . . . . . . . . . . . . . . . . . . .       10
 2.3   Logo Perforce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .    10
 2.4   Logo Skype for business . . . . . . . . . . . . . . . . . . . . . . . . . .      11
 2.5   Schermata di Confluence . . . . . . . . . . . . . . . . . . . . . . . . . .      11
 2.6   Schermata di Mattermost . . . . . . . . . . . . . . . . . . . . . . . . .        12
 2.7   Logo di Jira . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .   12
 2.8   Logo di Swarm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .      13
 2.9   Logo di Snowdrop . . . . . . . . . . . . . . . . . . . . . . . . . . . . .       13

 3.1   ECS idea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     16

 4.1   Gerarchia dell’interfaccia di combattimento . . . . . . . . . . . . . . .        20
 4.2   Ciclo di Update del Combat . . . . . . . . . . . . . . . . . . . . . . . .       21
 4.3   Ciclo di vita di un azione . . . . . . . . . . . . . . . . . . . . . . . . .     22
 4.4   Implementazione dell’Elemento . . . . . . . . . . . . . . . . . . . . . .        24
 4.5   Interfaccia di un sistema . . . . . . . . . . . . . . . . . . . . . . . . . .    24
 4.6   Interfaccia di un Azione . . . . . . . . . . . . . . . . . . . . . . . . . .     25

 5.1   Esempio di Finite State Machine . . . . . . . . . . . . . . . . . . . . .        28
 5.2   Esempio di Behavior Tree . . . . . . . . . . . . . . . . . . . . . . . . .       28
 5.3   Esempio di Goal Oriented Action Planning . . . . . . . . . . . . . . .           29
 5.4   Esempio di algoritmo A* . . . . . . . . . . . . . . . . . . . . . . . . . .      32
 5.5   Esempio di algoritmo Branch and Bound . . . . . . . . . . . . . . . . .          33
 5.6   Esempio di algoritmo MiniMax applicato al gioco del TicTacToe . . .              34
 5.7   Esempio di algoritmo Montecarlo . . . . . . . . . . . . . . . . . . . . .        35

                                           xi
xii                                                           ELENCO DELLE TABELLE

Elenco delle tabelle

      1.1   Resoconto Orario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .   3

      5.1   Resoconto finale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .   36
Capitolo 1

Introduzione

1.1      L’azienda
Ubisoft è stata fondata nel 1986 (chiamata fino al 2003 Ubi Soft Entertainment), ed è
un’azienda multinternazionale francese sviluppatrice ed editrice di videogiochi, con sede
centrale a Montreuil. La società è presente nel mondo con venticinque studi principali
in circa diciassette paesi, e vanta titoli fra i quali Far Cry, Assassins Creed, Mario
+ Rabbids Kingdom Battle, Tom Clancy’s Ghost Recon, For Honor e Tom Clancy’s
Rainbow Six.

                               Figura 1.1: Logo Ubisoft

1.1.1     Ubisoft Milan Studios SRL
Ubisoft Milan è stata fondata nel 1998 a Milano. Lo studio ha iniziato sviluppando
giochi per Console portatili[g] quali: Rayman per Game Boy Color[g] e Lara Croft
Tomb Raider: The Prophecy per Game Boy Advance[g] . Lo studio ha anche fatto da
supporto per altri studi Ubisoft nello sviluppo di giochi tra i quali: Beyond Good &
Evil, Tom Clancy’s Rainbow Six: Rogue Spear, Tom Clancy’s Rainbow Six 3: Athena
Sword, Tom Clancy’s Splinter Cell: Chaos Theory, Assassin’s Creed III: Liberation,

                                            1
2                                                   CAPITOLO 1. INTRODUZIONE

Assassin’s Creed Rogue e Just Dance. Lo studio si è poi mosso verso giochi che usavano
Motion control[g] come: MotionSports e Raving Rabbids: Alive and Kicking assieme
agli studi di Barcelona e Parigi. L’ultimo obbiettivo raggiunto è stato lo sviluppo come
team in co-development con lo studio di Parigi del primo gioco in collaborazione con
Nintendo per la piattaforma Switch[g] : Mario + Rabbids: Kingdom Battle.

1.2      L’idea
Lo stage prevedeva l’inserimento dello studente con il ruolo di Junior Gameplay[g]
Programmer; sarei stato inserito nel team coprendo il ruolo di ricerca e sviluppo,
finalizzato alla comprensione della fattibilità dell’utilizzo di un sistema ECS nello
sviluppo di giochi.

1.2.1     Obiettivi
Gli obiettivi dello stage erano quelli di:

    1. Ampliare le conoscenze e le competenze nel linguaggio di programmazione C++;

    2. imparare a interagire con tutti i team necessari allo sviluppo di un videogioco,
       per capire in profondità le relazioni tra di essi;

    3. imparare ad utilizzare tutti gli strumenti necessari per sviluppare un videogioco;

    4. comprendere ed applicare gli standard di qualità aziendali per la scrittura del
       codice e il ciclo di vita che questo ha nello sviluppo;

    5. svolgere attività di ricerca e sviluppo in modo autonomo.

    Lo stage infatti prevedeva l’inserimento dello studente nel team di sviluppo per la
ricerca di problematiche sullo sviluppo di un Gioco strategico a turni[g] , focalizzandosi
in particolare su temi quali AI[g] e Combat System.

1.3      Organizzazione del testo
Il secondo capitolo descrive le tecnologie che ho utilizzato durante il periodo di
      stage e con cui ho acquisito familiarità;

Il terzo capitolo approfondisce le tematiche dello stage che ho affrontato;

Il quarto capitolo descrive le transizioni tra le modalità per la creazione di un gioco
     modulare;

Il quinto capitolo approfondisce l’analisi effettuata sul sistema di combattimento
     che guida un videogioco strategico a turni;

Il sesto capitolo approfondisce la tematica degli algoritmi di Intelligenza Artificiale
      analizzati;

Nel settimo capitolo vengono riportate le conclusioni tratte dal periodo di stage.

   Riguardo la stesura del testo, relativamente al documento sono state adottate le
seguenti convenzioni tipografiche:
1.4. RESOCONTO ORARIO                                                                 3

  • gli acronimi, le abbreviazioni e i termini ambigui o di uso non comune menzionati
    vengono definiti nel glossario, situato alla fine del presente documento;

  • per la prima occorrenza dei termini riportati nel glossario viene utilizzata la
    seguente nomenclatura: parola[g] ;
  • i termini in lingua straniera o facenti parti del gergo tecnico sono evidenziati con
    il carattere corsivo;

  • i titoli dei videogiochi saranno riportati con il carattere corsivo.

1.4     Resoconto Orario

 Fase           Ore              Obiettivo
 Formazione     30               Formazione sull’engine
                30               formazione su C++11 e C++14
                10               formazione sugli strumenti aziendali
 Combat Sy-     40               Analisi e discussione del problema con il team di
 stem                            design
                40               Progettazione del sistema di Combattimento
                8                Discussione e presentazione del sistema con il team
                                 di sviluppo e di design
                10               Discussione e presentazione del sistema al team
                                 dell’Engine
                12               Ristrutturazione del sistema dati i feedback
                20               Sviluppo del prototipo di sistema
 AI             50               Analisi e discussione dei problemi con il team di
                                 design
                16               Analisi e studio degli algoritmi di ricerca
                24               Analisi e studio di FSM,Behavior tree,GOAP
                10               Ideazione prototipo
                20               Discussione e presentazione delle scelte

                           Tabella 1.1: Resoconto Orario
Capitolo 2

Strumenti e Tecnologie

2.1      Tecnologie
Per il processo di sviluppo, ho ampliato le mie conoscenze del linguaggio di program-
mazione C++ apprese in ambito accademico con le nuove funzionalità introdotte nelle
versioni C++ 11 e alcune di C++ 14.

2.1.1     C++ 11 e C++ 14
C++ è un linguaggio di programmazione orientato agli oggetti, con tipizzazione statica.
È stato sviluppato da Bjarne Stroustrup ai Bell Labs nel 1983 come un miglioramento
del linguaggio C tramite l’introduzione del paradigma di programmazione a oggetti,
funzioni virtuali, overloading degli operatori, ereditarietà multipla, template e gestione
delle eccezioni. [5] Il linguaggio venne standardizzato nel 1998 (ISO/IEC 14882:1998
"Information Technology - Programming Languages - C++", aggiornato nel 2003).
C++11, conosciuto anche come C++0x, è il nuovo standard per il linguaggio di
programmazione C++ che sostituisce la revisione del 2003, mentre C++14 è stata una
revisione minore nel 2014, l’attuale standard è C++17. Nella versione 11 le principali
modifiche sono state:
   • espressioni Lambda;
   • utility per template;
   • deduzione di tipo;
   • smart Pointer;
   • costruttore di Move e rvalue reference.
Mentre nella versione 14 sono state introdotte migliorie nelle espressioni lambda
quali la deduzione di tipo e valori di default, shared Mutex[g] ed altre feature per il
Multithreading[g] .

Espressioni lambda
In C++11 un’espressione lambda, spesso definita semplicemente lambda, costituisce
un modo efficace per definire un oggetto funzione anonima nella posizione in cui viene
richiamato o passato come argomento a una funzione. In genere le lambda vengono

                                            5
6                                      CAPITOLO 2. STRUMENTI E TECNOLOGIE

usate per incapsulare alcune righe di codice passate agli algoritmi o ai metodi asincroni.
Le espressioni lambda sono composte da sei parametri:

    1. Clausola di acquisizione: nota anche come lambda-introducer nella specifica C++,
       che specifica le variabili da acquisire e se l’acquisizione viene effettuata per valore
       o per riferimento, esistono due modalità di acquisizione automatica: = e &. =
       acquisirà tutte le variabili necessarie per copia mentre & per reference.

    2. Elenco parametri: facoltativo. Una lambda può accettare parametri di input.
       L’elenco di parametri (lambda declarator nella sintassi standard) è facoltativo e
       da molti punti di vista è simile all’elenco di parametri per una funzione.

    3. Specifica modificabile: facoltativa. La specifica modificabile consente al corpo di
       un’espressione lambda di modificare le variabili acquisite per valore.

    4. Exception-specification: facoltativa, utilizzata per indicare che l’espressione
       lambda non generi alcuna eccezione

    5. Trailing-return-type: facoltativo. Il tipo restituito di un’espressione lambda viene
       dedotto automaticamente. Non è necessario usare la parola chiave auto a meno
       che non venga specificato un trailing-return-type. Trailing-return-type è simile
       alla parte del tipo restituito di un metodo o funzione ordinaria. Tuttavia, il tipo
       restituito deve seguire l’elenco di parametri ed è necessario includere la parola
       chiave di trailing-return-type -> prima del tipo restituito.

    6. Corpo dell’espressione lambda: può contenere qualsiasi elemento che può essere
       contenuto nel corpo di un metodo o di una funzione ordinaria.

                             Figura 2.1: Dichiarazione Lambda

Deduzione di tipo
Spesso la dichiarazione di una variabile non è molto agevole, soprattutto quando si
tratta di tipi definiti all’interno di particolari Template[g] ; prendiamo ad esempio la
seguente dichiarazione

    std::vector iterator = myVector.begin()
2.1. TECNOLOGIE                                                                          7

Come si può notare, è una dichiarazione lunga e intricata, motivo per cui è facile cadere
in errore.
Con la keyword auto informiamo il compilatore che il tipo della variabile da noi
dichiarata sarà uguale al tipo che verrà ritornato dall’espressione assegnata. In questo
modo la variabile può essere dichiarata semplicemente con
                         auto iterator = myVector.begin()
permettendo così una scrittura più veloce del codice con meno possibilità di errore.

Gli utilizzi di questa keyword sono diversi:
  1. Inizializzazione di variabile: auto aVar = anotherVar;
  2. trailing return: auto func() -> aType;
  3. dichiarazione di funzione: auto func();
  4. dichiarazione di tipo: decltype(auto) a = aVar of function;

Smart Pointers
La gestione dell’allocazione dinamica della memoria è sempre stata, fin dai primi
computer, un punto delicato della programmazione. Molti moderni linguaggi di
programmazione (tipo il Java) offrono strumenti per la gestione automatica della
memoria.
   Per aiutare la gestione della memoria sono stati introdotti diversi Smart pointer[g] :
   • Unique pointer: non è copiabile o copia-assegnabile, due istanze di unique
     ponter non possono gestire lo stesso oggetto. Un non const unique pointer può
     trasferire la proprietà dell’oggetto gestito in un altro unique pointer, quando
     viene distrutto l’oggetto puntato viene distrutto;
   • Shared pointer: la proprietà dell’oggetto viene ripartita egualmente a tutte le
     copie, all’ultima istanza rimasta viene delegata la responsabilità di distruggere
     l’oggetto;
   • Weak pointer: non incide sul ciclo di vita dell’oggetto puntato, questo significa
     che in ogni momento è possibile che il weak pointer venga invalidato. In questo
     modo è permesso a qualsiasi funzione o classe di mantenere un riferimento ad
     un oggetto senza influenzarne il ciclo di vita, a discapito però di una maggiore
     difficoltà di implementazione del codice.

Move constructor e rvalue reference
La rvalue reference contiene riferimenti a un rvalore e la sua dichiarazione è la seguente:
                                       Type && a;
Questi riferimenti possono essere utilizzati da funzioni per ritornare oggetti temporanei
creati all’interno della stessa senza doverli allocare nello heap, grazie a operatori quali
std::forward e il costruttore di move. Il move contructor prende le informazioni di un
oggetto e la sua ownership, trasferendole nell’oggetto corrente, lasciando però l’oggetto
derubato in uno stato valido.
La sua dichiarazione è la seguente:
8                                     CAPITOLO 2. STRUMENTI E TECNOLOGIE

                        Class(Class &&another_Class_item);
Questo permette di ridurre al minimo costruzioni di copia là dove il passaggio per
riferimento di una variabile non è possibile, ottimizzando così il codice come mostrato
nel seguente esempio.

using namespace std ;

vector < int > doubleValues ( const vector < int >& v )
{
          vector < int > new_values ;
          new_values . reserve ( v . size () ) ;
          for ( auto itr = v . begin () , end_itr = v . end () ; itr
              != end_itr ; ++ itr )
          {
                    new_values . push_back ( 2 * * itr ) ;
          }
          return new_values ;
}

int main ()
{
         vector < int > v ;
         for ( int i = 0; i < 100; i ++ )
         {
                   v . push_back ( i ) ;
         }
         v = doubleValues ( v ) ;
}
                                                                                               
In questo esempio si vuole dato un vettore creare un nuovo vettore con valori raddoppiati
senza modificare il vettore di origine, in questo esempio si devono effettuare 3 costruzioni
la prima per il vettore vuoto che poi dovrà essere copiato quando verrà restituito e
copiato nel vettore v, il compilatore ottimizzerà queste ultime due copie copiando
direttamente in v.

const vector < int >&& doubleValues ( const vector < int >& v )
{
        vector < int > new_values ;
        new_values . reserve ( v . size () ) ;
        for ( auto itr = v . begin () , end_itr = v . end () ; itr
           != end_itr ; ++ itr )
        {
                   new_values . push_back ( 2 * * itr ) ;
        }
        return std :: move ( new_values ) ;
}

int main ()
{
2.2. STRUMENTI                                                                          9

              vector < int > v ;
              for ( int i = 0; i < 100; i ++ )
              {
                        v . push_back ( i ) ;
              }
              v = doubleValues ( v ) ;
}
                                                                                             
   Con la move semantics invece verrà solamente costruito il vettore new values e la
sua area di memoria verrà spostata in v al ritorno della funzione evitando ogni copia
aggiuntiva del vettore.

2.2      Strumenti
L’azienda ha imposto vari strumenti che devono essere utilizzati, sia per lo sviluppo
che per la coordinazione interna. In particolar modo è stato indicato Visual Studio
2015 come IDE[g] di sviluppo software e i seguenti strumenti per la coordinazione:
    • Perforce;
    • Skype for business;

    • Confuence;
    • Mattermost;
    • Jira;
    • Swarm;

    • Snowdrop.

2.2.1     Visual Studio
Visual studio è un ambiente di sviluppo integrato sviluppato da Microsoft, che supporta
attualmente diversi tipi di linguaggio, quali C, C++, C#, F#, Visual Basic .Net, Html
e JavaScript.
Questo ambiente di sviluppo è tra i migliori per il linguaggio di programmazione C++
, supporta C++17 e offre diversi plugin per agevolare lo sviluppo quali Visual Assist[g] ,
controllore della sintassi Clang[g] C++, Profiling Tools[g] per la memoria CPU[g] e
GPU[g] . Supporta Windows 10, 8.1 SDK[g] , e per questo è tra i software più usati tra
le aziende produttrici di giochi.
10                                  CAPITOLO 2. STRUMENTI E TECNOLOGIE

                        Figura 2.2: Schermata di Visual Studio

2.2.2    Perforce
Perforce è un software di Version control[g] venduto sotto il brand di Helix and Hansoft.
Questo è largamente utilizzato nell’industria dei videogiochi rispetto a Git e altri
competitor grazie ad una gestione migliore dei file binari, come Texture[g] e Asset[g] ,
molto presenti nello sviluppo di un gioco.

                               Figura 2.3: Logo Perforce

2.2.3    Skype for business
Skype for business è un client di messaggistica istantanea, ideato per funzioni di
comunicazione aziendale. A differenza di Windows Live Messenger, infatti, ha funzioni
differenti ed è stato sviluppato e progettato appositamente per le esigenze aziendali e
soprattutto per l’introduzione del telelavoro. Tra le funzioni implementate, permette
di collaborare attraverso una pagina vuota per note, disegni o immagini importate sui
quali i partecipanti alla riunione possono lavorare insieme. Inoltre possono interagire
simultaneamente durante una presentazione in PowerPoint e l’organizzatore della
riunione può creare dei sondaggi a cui i partecipanti alla riunione possono votare e
visualizzare i risultati. Molto utili sono la condivisione desktop e la condivisione di
applicazioni. In questo modo, viene di molto agevolato il poter effettuare riunioni con
partecipanti di altre sedi dell’azienda stessa.
2.2. STRUMENTI                                                                        11

                          Figura 2.4: Logo Skype for business

2.2.4    Confluence
Confluence è un software di collaborazione sui contenuti sviluppato da Atlassian,
utilizzato per produrre la documentazione del lavoro svolto da condividere con tutte
le sedi aziendali. Confluence permette l’utilizzo e lo sviluppo di macro per inserire
grafici, code snippet, link e riferimenti interni alla documentazione e di strutturare il
documento.

                         Figura 2.5: Schermata di Confluence

2.2.5    Mattermost
Mattermost è una soluzione Open source[g] , con server privato alternativa a Slack[g] .
Come alternativa a software proprietari di messaggistica, Mattermost offre tutte le
funzionalità di comunicazione, rendendole ricercabili e reperibili ovunque. Questo
strumento è stato usato per il coordinamento del team di lavoro e sviluppo e risulta
facile ed intuitivo utilizzarlo.
12                                   CAPITOLO 2. STRUMENTI E TECNOLOGIE

                          Figura 2.6: Schermata di Mattermost

2.2.6      Jira
Jira è un software proprietario per il tracking di problemi, sviluppato da Atlassian.
Jira prevede funzionalità di Bug Tracking[g] , Issue Tracking[g] e Project Management[g] .
Jira è al giorno d’oggi il più popolare tra i tools di tracking.

                                Figura 2.7: Logo di Jira

2.2.7      Swarm
Swarm è un software del brand Helix and Hansoft per revisione di codice, compatibile
con Perforce. Tra i benefici della code review ci sono:

     • Imporre gli standard di codifica, migliorando così leggibilità e affidabilità del
       codice;

     • condivisione di esperienza e conoscenza;

     • scoperta di errori preventivamente;

     • miglioramento del codice scritto.

Swarm offre queste funzionalità senza aggiungere troppo overhead di lavoro al team di
sviluppo.
2.2. STRUMENTI                                                                13

                           Figura 2.8: Logo di Swarm

2.2.8    Snowdrop
Snowdrop è un engine proprietario di Ubisoft sviluppato da Massive Entertainment
per lo sviluppo di giochi su Microsoft Windows, Nintendo Switch, PlayStation 4, e
Xbox One. L’engine è principalmente sviluppato in C++ e offre scripting basato su
nodi per lo sviluppo di AI, UI[g] ed eventi.

                          Figura 2.9: Logo di Snowdrop
Capitolo 3

Introduzione al progetto

L’universo dei videogiochi è ampio e variegato, poiché sono moltissime le categorie che ne
fanno parte. Ognuna di esse presenta problematiche peculiari, ma molte sono condivise.

Il progetto di stage è focalizzato nell’analisi dei problemi che riguardano in maniera
specifica la creazione di una battaglia a turni, presente in tutti i giochi strategici
a turni, ponendo particolare attenzione al mantenere la compatibilità con l’engine
Snowdrop e soddisfacendo i vincoli di design posti e il pattern architetturale richiesto
(ECS in questo caso). Il mio compito è stato quello di verificare se un sistema ECS
fosse applicabile a un gioco strategico a turni.

3.1     Pattern architetturali
Nella programmazione molti sono i pattern architetturali applicabili a uno specifico
software. La scelta di uno rispetto a un altro è dato dall’equilibrio di più fattori,
come perseguire la manutenibilità del prodotto, la testabilità delle parti, per seguire
una necessità di divisione tra dati e logica oppure per mantenere separati i compiti
di classi diverse. Per questo motivo si presenta una scelta che deve essere fatta in
maniera oculata: in questo caso, è ricaduta sul pattern Entity-Component-System
(ECS), che permette una divisione tra la logica del software e i suoi dati, permettendo
il Data-Driven Development[g] .

3.1.1     Entity-Component-System
L’Entity-Component-System (ECS d’ora in poi) è un Pattern architetturale[g] che è
maggiormente usato nello sviluppo di giochi. ECS segue il principio di composition
over inheritance (cioè viene preferita la composizione di classi che l’ereditarietà
fra esse) così da permettere una maggiore flessibilità nel definire entità, tema molto
importante soprattutto nel campo dei videogiochi dove ogni oggetto nella scena è un
entità. Un esempio di entità può essere una qualsiasi figura che prende parte in un
combattimento.
In ECS, ogni entità viene arricchita da uno o più componenti che le conferiscono
proprietà o comportamenti aggiuntivi, permettendo così la capacità di modificare
a runtime il suo comportamento. Questa modifica è facilmente attuabile tramite
un’aggiunta o rimozione di componenti, eliminando così problemi di ereditarietà troppo
profonda e gerarchie troppo grandi, difficili da comprendere, mantenere ed estendere.

                                           15
16                               CAPITOLO 3. INTRODUZIONE AL PROGETTO

ECS è molto utilizzato nell’ambito videoludico per la grande compatibilità con il
data-driven development, dove il codice deve definire comportamenti in base ai dati
forniti esternamente.

                                 Figura 3.1: ECS idea

3.1.2    ECS Strict
ECS strict è una specifica del pattern ECS che punta alla divisione totale dei dati
dalla logica con la creazione di entity composte da components, in modo da creare
contenitori di dati che non definiscono nessun comportamento e sistemi stateless che
ottengono le entity con specifici components e applicano la logica sulla entity stessa,
modificandola.
Questo porta i massimi vantaggi derivanti dal scegliere ECS derivanti dalla completa
separazione delle componenti dati da quelle logiche, permettendo di raggiungere un
elevato livello di testabilità e manutenibilità.

3.2     Confronto ECS e OOP
Nella programmazione orientata agli oggetti () la separazione fornita da ECS non è
possibile ottenerla dato che dovrebbero esistere oggetti che conoscono come elaborare
dati di altre classi, portando così altre classi ad essere solamente raccolte di dati.
Se si volessero rispettare i paradigmi della OOP, porterebbe a una grande quantità
di duplicazione di codice e inefficienza, poiché ci sarebbero moltissimi oggetti che
dovrebbero essere aggiornati ad ogni frame, a differenza di ECS nel quale solo i systems
devono essere aggiornati.
Per questi motivi, l’OOP nel game developing non è la scelta ottimale da perseguire
per ottenere la massima testabilità e manutenibilità del codice, permettendo un lavoro
di manutenzione più preciso, rapido e mirato.

3.3     Struttura del gioco e modularità
Ogni gioco strategico a turni è possibile vederlo come un insieme di due modalità
di gioco differenti: una battaglia a turni (tipo scacchi) e una modalità di gestione
3.4. CHARACTER                                                                           17

del gioco. Solitamente alcuni dati tra esse sono condivisi, ma la maggior parte non
è necessario che vengano condivisi tra i due sistemi. Questo poiché solitamente nel
combattimento non è necessario conoscere tutte le risorse che il giocatore ha disponibili
al di fuori dalla battaglia, escluse quelle scelte per quello specifico combattimento. Ad
esempio, un giocatore sceglie una squadra di eroi da portare in battaglia e solo i loro
dati sono necessari in quel momento, non serve invece conoscere tutto l’arsenale che il
giocatore ha a disposizione.
Date queste precondizioni, abbiamo pensato di strutturare le entità in maniera tale
che offrano le informazioni e funzionalità strettamente necessarie per la modalità di
gioco corrente, strutturando il gioco in moduli. Ogni modulo definisce una specifica
entità che contiene solamente i dati necessari in un dato istante.

3.4      Character
Il Character è l’entità comune tra tutte le modalità di gioco. Contiene tutti i dati
condivisi tra le modalità necessari ai sistemi base per funzionare, per esempio la texture
del personaggio. Il Character viene trasformato nell’entità specializzata all’ingresso
della modalità di gioco specifica, per esempio entrando in combat il Character diventa
un combattente, offrendo così i dati e le funzionalità richieste dai sistemi specifici del
combattimento. Al termine della modalità di gioco le informazioni modificati nell’entità
specifica di gioco dovranno essere trasportate nel Character per essere propagate nelle
altre modalità di gioco: per esempio la vita del personaggio potrebbe essere richiesta
al di fuori del combattimento per usare differenti animazioni o per essere guariti al di
fuori di esso.

    Per ottenere tale obiettivo abbiamo creato un’entità specifica per il combat, cioè
il Combattente. Questa espone solamente i dati e le funzionalità del personaggio
utilizzabili e utili nel combat, escludendo le informazioni delle altre fasi di gioco non
necessarie.
    Questa divisione permette di sviluppare entità specifiche per ogni modalità senza
dover contenere anche le funzionalità delle altre modalità di gioco. Questo permetterà
anche di lavorare in parallelo sulle varie feature e una maggior semplicità nello unit-
testing grazie a un accoppiamento ridotto.

3.5      Layers
Affinché la divisione tra modalità sia definita, il gioco deve essere strutturato per far sì
che ogni modalità sia ben delineata e organizzata:

   • Una struttura base (Game) deve definire tutto ciò che è condiviso tra le modalità
     di gioco e i Character esistono come entità solamente in questo layer;

   • Sopra al Game poi vengono costruite le varie modalità di gioco, in cui vengono
     definite le specifiche regole per quella modalità. Di conseguenza devono saper
     costruire la propria entità da un Character e saperlo decomporre in un Character.

   Questo approccio permette una grande flessibilità per quanto riguarda lo sviluppo
del gioco, poichè permette di poter lavorare su una qualsiasi modalità di gioco in
maniera modulare o per specializzazione di modalità già sviluppate. In questo modo,
ogni modalità può funzionare esclusivamente su un tipo di entità specifica ed avere
18                                CAPITOLO 3. INTRODUZIONE AL PROGETTO

delle funzionalità caratteristiche, creando così un ambiente totalmente modulare. Ogni
intervento fatto sul gioco potrà quindi essere totalmente indipendente, non andando
a interferire con altre parti, creando una altissima variabilità che può essere espansa
facilmente oppure modificata senza danneggiare altri moduli.

3.6      Character life cycle
Quando si entra in combat, un Character viene elaborato in un combattente, alcuni
dati precedentemente posseduti verranno mantenuti, ad esempio la quantità di vita
rimanente. Durante il combat, i dati non vengono modificati nel Character ma nel
combattente, che alla fine della battaglia dovrà essere sincronizzato con il Character,
che poi verrà trasformato nella prossima entità specifica per la successiva modalità di
gioco.
Il passaggio per il Game layer tra le modalità e quindi la transizione dell’entità specifica
nel generico Character è un vincolo posto per evitare il passaggio "nascosto" di dati
tra le diverse modalità e oltretutto semplifica la modularità delle modalità di gioco
consentendo solo la trasformazione da Character ad entità specifica e viceversa.
Capitolo 4

Combat System

In questo capitolo si parlerà del sistema di gestione delle azioni nel combattimento.
Il sistema è stato diviso in modo tale che i Fighter siano raccolte di dati e possano
richiedere delle azioni, che verranno eseguite dai sistemi durante l’Update[g] .

4.1     Interfaccia del sistema di combattimento
L’interfaccia del sistema di combattimento è divisa in Combat, Console e Factory,
che sono classi astratte da implementare. Il Combat ha la responsabilità di gestire
l’ingresso e l’uscita dal combattimento e di chiamare l’update su tutti i suoi sistemi.
La Console contiene tutte le azioni che un Fighter può richiedere e gestisce il loro
smistamento verso i sistemi. La Factory è l’unico modo per creare i Fighter e i loro
component.
    Il Combat estende privatamente le altre in modo da essere l’unico punto d’accesso
per poter costruire il Fighter ed i player visto che questi richiederanno una console e
l’unico possessore di questa sarà il Combat.

4.1.1    Console
La classe Console contiene i comandi che gli elementi e i Fighter possono richiedere.
Questa serve ai Fighter per richiedere azioni che poi verranno smistate verso i sistemi
di combattimento corretti per essere elaborate. Un azione potrebbe essere la richiesta
di un movimento o la creazione di un componente aggiuntivo per il Fighter.

Extended Console
L’ExtendedConsole è una classe che unisce la elementFactory e la Console per per-
metter ai player che ne avranno accesso di creare azioni e creare o rimuovere elementi
per costruire i Fighter. In questo modo ogni player potrà gestire i propri Fighter nel
combattimento.

4.1.2    Element Factory
La ElementFactory può essere usata dai sistemi per iniettare nuovi elementi nel
combat se necessario, poiché ai sistemi non è permesso distruggere elementi o effettuare
azioni, ma un azione potrebbe essere creata come risultato della creazione di un nuovo

                                          19
20                                                CAPITOLO 4. COMBAT SYSTEM

elemento. La ElementFactory è anche usata dal CombatLoader per creare istanze
di tutti i combatElement all’inizio del combattimento, permettendo la transizione da
Character a Fighter all’inizio della modalità di gioco.

4.1.3    Combat Interface
Il Combat implementa privatamente l’ExtendedConsole in modo da fornire l’unico
punto di accesso per costruire Players, Fighter, Entità e Sistemi attraverso i suoi
metodi factory. La CombatInterface è un implementazione privata per fare in modo
che nessuno oltre a lei sia consapevole che essa è un ExtendedConsole forzando così
gli utilizzatori a usare i suoi metodi factory. La CombatInterface ha la responsabilità
della creazione dei componenti fondamentali per la gestione del combattimento, assicura
la loro corretta serializzazione, permettendo di salvare lo stato del combattimento e
dei Fighter su supporto fisico, e all’uscita del combat effettua la transizione da Fighter
a Character.

                 Figura 4.1: Gerarchia dell’interfaccia di combattimento

4.2     I componenti del sistema di combattimento
Basandosi sulle interfacce sopra descritte il sistema di combattimento creerà i Fighter e
gli elementi alla creazione del contesto di combattimento, lasciando poi la gestione del
flow del combat ai sistemi che gestiranno le azione dei componenti del combat, passate
4.2. I COMPONENTI DEL SISTEMA DI COMBATTIMENTO                                          21

dalla Console. Ogni azione verrà richiesta da un Fighter, un player o un element

                        Figura 4.2: Ciclo di Update del Combat

tramite la propria Console, poi questa azione verrà data al Combat che la salverà nel
Log, per avere un resoconto delle azioni effettuate in tutta la battaglia, per infine darla
al sistema competente per essere eseguita alla successiva update.
    Questo porta il Combat ad essere suddiviso nelle seguenti parti:

   • Player;

   • Elementi;

   • Fighter;

   • Sistemi;

   • Azioni.
22                                                 CAPITOLO 4. COMBAT SYSTEM

                          Figura 4.3: Ciclo di vita di un azione

4.3      I Player
Il Player è un astrazione del giocatore reale, permette quindi di gestire le risorse della
squadra e gli input. Un player contiene la raccolta di Fighter della sua squadra e
permette la gestione dei turni, passando da un player all’altro, e la gestione delle azioni
dei Fighter dello stesso team. Il suo compito è quello di fornire funzionalità per la
gestione della squadra e gestire gli input per i propri Fighter.

4.4      Elementi e Fighter
Gli elementi e i Fighter sono le entità che entrano a far parte del combattimento. Essi
infatti non sono altro che raccolte di componenti che possono richiedere di svolgere
azioni e sono coloro che subiranno gli effetti di queste.
    Un elemento è la parte fondamentale del Fighter mentre un Fighter è una composi-
zione di elementi, che a loro volta sono composizioni di componenti base che descrivono
il suo stato.
    Gli elementi contengono i dati e descrivono le capacità del Fighter e il suo stato.
Un elemento contiene la lista dei suoi componenti ed espone metodi per aggiungerne e
ottenerli. Un elemento non permette di modificare i propri componenti che possono
essere modificati solo tramite le azioni elaborate dai sistemi, questo per relegare la
logica solo nelle azioni e nei sistemi.
    Sia gli elementi che i Fighter possono richiedere un azione e rimuovere se stessi o un
4.5. SISTEMI                                                                              23

elemento in loro possesso, ma non possono crearne di nuovi. Rimuovere i componenti
dal Fighter permette di rispecchiare meglio il suo stato, per esempio se il Fighter viene
disarmato perderà la sua arma dai componenti. Invece richiedere un’azione permette
di modificare lo stato dei componenti o di richiederne di nuovi, alterando lo stato del
Fighter stesso.
    Poiché è stato richiesto che il combattimento sia deterministico, quindi che dato uno
stato ed una lista di azioni lo stato risultante sia sempre lo stesso, le azioni dovrebbero
essere progettate e pensate considerando solo lo stato interno dei componenti.
    Questo si concretizza in un Fighter che possiede degli elementi quali un arma e
una vita che sono descritti dai loro componenti. In tal modo è possibile espandere
un Fighter aggiungendo elementi per descrivere in maniera più dettagliata lo stesso,
per esempio se si volesse rendere colpibile in due punti diversi il bersaglio basterebbe
dividere il corpo dalla testa e assegnare due componenti che possano essere mirati, in
questo modo il sistema di mira riconoscerebbe che nel Fighter esistono due punti al
quale mirare. Un elemento può contenere altri elementi per rendere ancora più flessibile
il sistema e, tornando all’esempio precedente, ora si può pensare che se miriamo al
braccio del bersaglio, che ora ha una vita, il colpo danneggerà in parte il bersaglio, ma
sarà possibile "distruggere" il braccio rendendo il Fighter incapace di effettuare certe
azioni che solo il braccio poteva richiedere.

4.4.1     Interfaccia degli elementi
Per dividere i dati base come la posizione o la console dei comandi consentiti all’elemento,
che non saranno mai duplicati in un element, dai dati forniti dai Component è stata
creata un interfaccia : CoreElement. L’Elemento contiente un UniquePointer privato
del suo CoreElement che verrà usato dal sistema per gestire proprietà intrinseche del
combat come la posizione o per richiedere comandi fondamentali all’elemento.

Shared pointer vs Handle
Per gestire la lista degli elementi si è dovuto scegliere tra shared pointer e Handle[g] , la
cui principale differenza risiede nel fatto che uno shared pointer non distrugge l’oggetto
puntato alla distruzione mentre un handle lo fa. La scelta di usare gli shared pointer
è derivata dal fatto che, potendo un elemento essere bersaglio di un’azione in corso,
se il Fighter vuole rimuovere quell’elemento non potrà farlo poiché sarà l’altra azione
a tenere in vita l’elemento finché questa non sarà completata. In pratica cancellare
un elemento trasforma lo shared pointer all’interno del Fighter in un weak pointer,
in modo che appena ogni utilizzatore di tale elemento termina le operazioni su tale
elemento, questo verrà cancellato.

4.5      Sistemi
I sistemi contengono la logica fondamentale per lo svolgimento delle azioni e per ogni
sistema ad ogni frame del combat verrà chiamata la funzione di update.
    Un sistema ha il compito di prendere tutti i Fighter e gli elementi che devono
gestire o che hanno richiesto l’azione, per poi applicare gli effetti della logica su di
essi. Per esempio il sistema di animazione farà aggiornare del delta time trascorso
l’animazione di ogni Fighter che avrà l’ipotetico component Animable. La selezione
viene fatta tramite un filtro sui componenti, per aggiornare solo il subset minimo
di Fighter. Essendo i componenti la raccolta di dati necessaria al sistema, filtrando
24                                               CAPITOLO 4. COMBAT SYSTEM

                      Figura 4.4: Implementazione dell’Elemento

per componenti si può rimuovere ogni controllo da parte del sistema sul Fighter
per verificare che questi contengano le informazioni necessarie. Un Sistema ha un
riferimento alla ElementFactory che offre la possibilità di istanziare nuovi elementi.
Anche se un Sistema può creare Elementi, non ha la possibilità di cancellarli, poiché
questa responsabilità è delegata al Fighter o all’elemento che contiene tale elemento,
dato che essi hanno il possesso dell’elemento. Perciò ogni elemento generato dal sistema
deve essere capace di gestire il proprio ciclo di vita indipendentemente, notificando ai
sistemi tramite la callback OnUnregisterElement quando viene distrutto.

                         Figura 4.5: Interfaccia di un sistema

4.6     Azioni
Le azioni sono le richieste che vengono fatte dagli Elementi e dai Fighter ad un sistema
per poter alterare il loro stato, per esempio muoversi in un punto modificherà l’elemento
4.6. AZIONI                                                                              25

di posizione del Fighter che l’ha richiesto. Vengono implementate partendo da un
interfaccia.

                           Figura 4.6: Interfaccia di un Azione

    Ogni azione verrà sempre eseguita al ciclo di update successivo, e non è possibile
inviare azioni durante l’update dei sistemi. L’interfaccia delle azioni permette di control-
lare l’ID di quest’ultima, il sistema che la elaborerà e un identificativo. L’identificativo
sarà una stringa usata nella serializzazione del azione.
Capitolo 5

AI

In questo capitolo parleremo delle possibilità analizzate per lo sviluppo di un’Intelligenza
Artificiale (AI) per il combattimento che dia l’impressione ai giocatori di prendere
scelte di gruppo e collaborative. Per AI in questo capitolo si intende la capacità da
parte del computer di analizzare lo stato del gioco e decidere delle mosse in base ad
esso.
    Per questo motivo sono stati analizzati i seguenti metodi per creare un AI:
   • Finite State Machine;
   • Behavior Tree;
   • Goal Oriented Action Planning.

5.1      Finite State Machine
Una macchina a stati finiti (finite state machine), o FSM in breve, è un modello
computazionale basato su una macchina ipotetica fatta di uno o più stati. Solo un
singolo stato può essere attivo allo stesso tempo, per cui la macchina deve passare
da uno stato all’altro per eseguire diverse azioni. Una macchina a stati finiti è un
modello utilizzato per rappresentare e controllare un flusso di esecuzione. È uno dei
primi metodi usati per produrre un AI nei giochi poiché pur rimanendo semplice da
implementare produce buoni risultati comportamentali da parte dell’AI.

5.1.1     Vantaggi
   • Una macchina a stati finiti è facilmente implementabile;
   • rende semplice il debug conoscendo lo stato in cui il Combattente dell’AI si trova;
   • è veloce da espandere, basta creare un nuovo stato e le sue transizioni.

5.1.2     Svantaggi
Il principale svantaggio di una FSM è la sua flessibilità, infatti, Una macchina a
stati finiti definisce gli stati nel quale ogni combattente si trova e le transizione che
può prendere da quello stato valutando il contesto nel quale si trova. Questo non è
sufficientemente flessibile per supportare un obiettivo che può cambiare nel corso della

                                            27
28                                                                  CAPITOLO 5. AI

                      Figura 5.1: Esempio di Finite State Machine

battaglia. Può esplodere e diventare enorme se si espande per ogni nuova necessità. Per
implementare una collaborazione tra nemici si dovrebbe implementare una gerarchia
di FSM, così facendo verrebbe meno il principale vantaggio di questo metodo: la
semplicità implementativa e di lettura del codice.

5.2       Behavior Tree
I Behavior tree[g] si concentrano nell’aumentare la modularità degli stati. L’albero è
composto da tre elementi principali:

     • Sequence: L’AI analizzerà tutti i figli del nodo in sequenza, si interrompe se si
       fallisce un operazione;

     • Selector: L’AI prenderà il primo figlio la cui esecuzione è possibile;

     • Foglia: è un azione che viene svolta dal combattente.

Ogni combattente parte dalla radice del proprio behavior tree scorrendolo e così
pianificando le sue azioni da svolgere.

                          Figura 5.2: Esempio di Behavior Tree
5.3. GOAL ORIENTED ACTION PLANNING                                                     29

5.2.1     Vantaggi
   • Gli alberi possono essere espansi e permettono agli sviluppatori di creare sotto-
     alberi che gestiscano particolari comportamenti che possono essere concatenati
     per creare un AI. Questo permette di creare anche un processo incrementale nel
     quale si può creare un albero che gestisca un comportamento base per poi creare
     branch per gestire casi ed obiettivi specifici;

   • La sua possibilità di essere data-driven permette ai designer di creare l’AI
     personalmente senza dover interagire con il team di programmazione.

5.2.2     Svantaggi
Per creare un comportamento abbastanza completo l’albero potrebbe diventare molto
profondo e grande rendendo difficile il Debugging[g] .

5.3      Goal Oriented Action Planning
Goal Oriented Action Planning o GOAP è un sistema AI che permette ad ogni
combattente di prendere una decisione dato il proprio bacino di azioni e gli obiettivi che
deve perseguire. La particolare sequenza di azioni non dipende soltanto dall’obiettivo
ma anche dallo stato attuale del mondo, poiché uno stato diverso potrebbe portare ad
un diverso bacino di azioni per l’entità che porterebbe a una diversa pianificazione.

                 Figura 5.3: Esempio di Goal Oriented Action Planning
30                                                                       CAPITOLO 5. AI

5.3.1       Vantaggi
Con GOAP è possibile, definendo specifici obiettivi, far pianificare dal sistema le
azioni da concatenare per ottenerlo nel miglior modo possibile. Il sistema può essere
progettato per permettere la definizione di obiettivi tramite dati, portando così a
data-driven-development. Il coordinatore del sistema GOAP pianifica le azioni dei
combattenti facendoli interagire tra loro.

5.3.2       Svantaggi
GOAP è il più complesso dei sistemi da sviluppare e non porta sempre ad azioni
prevedibili. Il combattente deve poter calcolare le proprie azioni e gli effetti sul mondo
ed il Planner deve riuscire a comporre tali azioni nel miglior modo possibile molto
rapidamente.

5.4        Prototipazione
Dopo l’analisi delle precedenti tecnologie è stato scelto di utilizzare il GOAP per la
capacità di essere data-driven e perché intrinsecamente gestiva già tutti i casi richiesti
dall’obiettivo iniziale.

5.4.1       Flow dell’AI
Data la scelta si è pensato al seguente flow generale per un turno dell’AI nel combat:

     1. All’inizio del turno dell’AI un Decision tree[g] , valutando lo stato attuale, computa
        lo SquadDesire cioè l’obiettivo che la squadra ha in questo turno;

     2. dato lo SquadDesire, un FighterBias (un modificatore del comportamento
        standard) è computato ed assegnato ad ogni combattente della squadra;

     3. il Coordinator richiede ad ogni combattente quali sono le azioni che vorrebbe
        e potrebbe svolgere nello stato corrente e inizia la simulazione per decidere che
        azione prendere;

     4. il pianificatore esplora lo spazio delle azioni e produce un piano di azioni, e a
        ogni azione pianificata si ritorna al punto 3;

     5. creato il piano questo viene dato al coordinatore che dice a ogni combattente
        nell’ordine corretto l’azione da svolgere;

     6. dopo ogni esecuzione di un azione, se lo stato risultante differisce dallo stato
        pianificato, si ritorna al punto 3.

5.4.2       Comportamento
Per permettere la creazione di "classi" diverse di nemici che agiscano secondo i loro
ideali, per esempio un guerriero che cerca sempre di attaccare, si è deciso di dare ad
ogni combattente un comportamento standard rappresentato da un insieme di valori
che rappresentano quanto ogni combattente pesa il risultato di un’azione rispetto a
un’altra. Un esempio per il guerriero che vuole attaccare potrebbe essere che la sua
maschera pesi le azioni di attacco a 1 e quelle difensive a 0, portando quindi il guerriero
5.5. PLANNER                                                                             31

a voler attaccare piuttosto che difendersi. Standardizzando questa maschera per ogni
combattente è possibile creare degli archetipi di combattenti che, se non influenzati,
prenderanno le stesse scelte.

SquadDesire e FighterBias
Il FighterBias è una maschera di bit che si applica al comportamento di base di un
combattente per modificarne la scelta delle azioni. Questo viene calcolato dato uno
SquadDesire, cioè un’altra maschera che dichiara quali sono gli obiettivi del team
data una situazione, uno stato del combat o un combat in generale. Applicando
il FighterBias a un combattente è così possibile alterare il comportamento base al
FighterBias per portarlo a una scelta di azioni che mirino allo stesso obiettivo della
squadra. Il FighterBias potrebbe sia essere un incentivo a svolgere altre azioni rispetto
agli standard, sia un obbligo in caso il gameplay richieda una certa azione per il
combattente, permettendo un maggiore controllo da parte del team di design.

5.5      Planner
Il planner, dopo che il coordinatore ottiene le azioni possibili, deve pianificare le scelte
da prendere. Questo può essere visto come un problema di esplorazione di grafi, nel
quale si parte dallo stato di gioco attuale e ad ogni scelta corrisponde un nuovo stato di
gioco. Definita una funzione che valuti uno stato di gioco è quindi possibile utilizzare
diversi algoritmi per l’esplorazione del grafo.

   • A*;

   • Iterative-Deepening A*;

   • Pure Heuristic Search;

   • Depth-First Branch-And-Bound;

   • Recursive Best-First Search;

   • MiniMax AlphaBeta pruning;

   • MonteCarlo Tree Search.

5.6      A*
A* è un algoritmo che ricerca il primo miglior risultato. In questo algoritmo ad ogni
nodo è associato un costo f(n) = g(n) + h(n) dove g(n) è il costo del percorso dalla
radice al nodo n e h(n) è il costo euristicamente stimato per raggiungere l’obiettivo.
Quindi, f(n) stima il minor costo totale di ogni soluzione che passa attraverso tale
nodo. Ad ogni iterazione il nodo con il minor valore viene scelto per l’espansione, in
caso di parità verrà scelto il nodo con minore h. L’algoritmo termina quando verrà
raggiunto l’obiettivo.
32                                                                  CAPITOLO 5. AI

                         Figura 5.4: Esempio di algoritmo A*

5.7     Iterative-Deepening A*
In questa variazione di A* ogni iterazione del algoritmo è una ricerca in profondità
che tiene traccia del costo f(n) = g(n) + h(n), di ogni nodo generato. Se il costo
del nodo generato eccede il limite della iterazione il branch viene tagliato. Il costo
della iterazione viene inizializzato alla stima euristica dello stato iniziale ed in ogni
successiva iterazione è aumentato al costo totale del nodo a minor costo che è stato
scoperto durante l’iterazione precedente. L’algoritmo termina quando un obiettivo è
stato raggiunto e non eccede il limite.

5.8     Ricerca Euristica
La ricerca Euristica[g] espande i nodi in ordine al loro valore h(n), mantenendo una
lista dei nodi che sono già stati espansi e una lista di quelli che sono stati generati
ma non ancora espansi. L’algoritmo inizia con la radice come nella lista dei nodi non
espansi, ad ogni iterazione un nodo con il minimo valore euristico h(n) viene espanso,
generando i suoi figli e infine viene spostato nella lista dei nodi chiusi. La funzione
euristica viene applicata ai figli che poi vengono messi nella lista dei nodi ancora da
analizzare. L’algoritmo continua fino al raggiungimento dello stato scelto.

5.9     Depth-First Branch-And-Bound
L’idea della ricerca depth-first branch-and-bound (DFBnB) è quella di mantenere traccia
della soluzione a costo minore ottenuta. Dato che il costo di un cammino parziale è la
somma dei costi degli archi camminati fino a quel punto, se il costo di tale cammino
supera il costo della soluzione trovata tale cammino può essere eliminato. Oltretutto,
con un funzione euristica come il costo del minimo Spanning tree[g] dei rimanenti nodi,
può essere aggiunta al costo per velocizzare l’eliminazione dei branch.

5.10      Recursive Best-First Search
La ricerca Recursive best-first funziona mantenendo nello stack di ricorsione il percorso
completo al nodo corrente che viene espanso e a tutti i nodi fratelli diretti dei nodi in
5.11. MINIMAX                                                                             33

                  Figura 5.5: Esempio di algoritmo Branch and Bound

quel percorso, assieme al costo del miglior nodo nel sotto-albero esplorato sotto ogni
fratello. Ogni qualvolta il costo del nodo corrente superi quello di qualche altro nodo
nella regione espansa precedente dell’albero, l’algoritmo ritorna all’antenato comune
più profondo, e continua la ricerca lungo il nuovo cammino. Effettivamente, l’algoritmo
mantiene una soglia separata per ogni sotto-albero che diverge dal cammino di ricerca
corrente.

5.11       MiniMax

L’algoritmo MiniMax è costituito da una funzione di valutazione posizionale che misura
la bontà di una posizione (o stato del gioco) e indica quanto è desiderabile per il dato
giocatore raggiungere quella posizione; il giocatore fa poi la mossa che minimizza il
valore della migliore posizione raggiungibile dall’altro giocatore. Quindi l’algoritmo
MiniMax assegna un valore ad ogni mossa legale, proporzionale a quanto essa diminuisce
il valore della posizione per l’altro giocatore. Per valutare le posizioni finali di vittoria
e di sconfitta, un metodo è assegnare infinito alle mosse che portano alla vittoria
finale e -infinito a quelle di sconfitta. Il valore per il giocatore A di ogni mossa non
immediatamente vincente è poi il valore minimo di tutte le possibili contromosse di
B. Questo presuppone che sia possibile per chi computa valutare tutto l’albero delle
mosse possibili del gioco; in realtà questo si può fare solo per giochi molto semplici, e
in generale si può solo calcolare una stima della probabilità che una data mossa porti
alla vittoria di uno dei giocatori.
    In pratica il lavoro dell’algoritmo MiniMax è di esplorare tutti i possibili nodi
dell’albero di gioco: il numero medio di nodi figlio per ogni nodo in esame è detto
fattore di diramazione, ed è pari al numero medio di mosse possibili in una generica
posizione del gioco.
Puoi anche leggere