From e708fb593769bc00277c775cb8ab16fad3c882c1 Mon Sep 17 00:00:00 2001 From: Luca Zoppetti Date: Sat, 6 Jan 2024 22:41:10 +0100 Subject: [PATCH] add fnad Co-authored-by: Lorenzo Redighieri --- .github/workflows/publish.yml | 1 + .gitignore | 4 +- dist/index.md | 3 + tex/cavendish/images/graphS1.pdf | Bin 0 -> 23942 bytes tex/cavendish/images/graphS2.pdf | Bin 0 -> 23444 bytes tex/fnad/fnad.tex | 164 +++++++++++++++++++++++++++++++ 6 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 tex/cavendish/images/graphS1.pdf create mode 100644 tex/cavendish/images/graphS2.pdf create mode 100644 tex/fnad/fnad.tex diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d648a91..2adfc69 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,6 +18,7 @@ jobs: tex/analysis_2/analysis_2.tex tex/bubbles/bubbles.tex tex/cavendish/cavendish.tex + tex/fnad/fnad.tex - name: Move PDFs run: mv ./tex/*/*.pdf dist shell: bash diff --git a/.gitignore b/.gitignore index 87642b6..62cd849 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,5 @@ *.fls *.fdb_latexmk *.out -*.pdf -*.dvi \ No newline at end of file +*.dvi +tex/*/*.pdf \ No newline at end of file diff --git a/dist/index.md b/dist/index.md index 682a57c..951ee53 100644 --- a/dist/index.md +++ b/dist/index.md @@ -14,6 +14,9 @@ In questo sito web sono disponibili i progetti LaTeX che scrivo per i miei studi - [Misura della costante di gravitazione universale tramite un pendolo di torsione](https://luckeedev.github.io/LuTeX/cavendish.pdf) La relazione dell'esperienza di laboratorio conclusiva del primo anno in cui è stata misurata la costante di gravitazione universale. +- [Five nights at DIFA](https://luckeedev.github.io/LuTeX/fnad.pdf) + Descrizione del gioco "Five nights at DIFA", il progetto per l'esame del corso "Programmazione per la Fisica". + ### Secondo anno - [Risalita di bolle in un liquido](https://luckeedev.github.io/LuTeX/bubbles.pdf) diff --git a/tex/cavendish/images/graphS1.pdf b/tex/cavendish/images/graphS1.pdf new file mode 100644 index 0000000000000000000000000000000000000000..288dd988d983aa71f6f6a75b4036bd1bec8d5591 GIT binary patch literal 23942 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 literal 0 HcmV?d00001 diff --git a/tex/fnad/fnad.tex b/tex/fnad/fnad.tex new file mode 100644 index 0000000..de002dc --- /dev/null +++ b/tex/fnad/fnad.tex @@ -0,0 +1,164 @@ +\documentclass{article} +\usepackage{graphicx} +\usepackage{menukeys} +\usepackage{amsmath} +\usepackage[italian]{babel} + +\usepackage{hyperref} +\hypersetup{ + colorlinks, + citecolor=black, + filecolor=black, + linkcolor=black, + urlcolor=blue +} + +\newcommand{\quotes}[1]{``#1''} + +\title{Five nights at DIFA} +\author{Lorenzo Redighieri\\Luca Zoppetti} +\date{12 Giugno 2023} + +\begin{document} + +\maketitle + +\begin{abstract} + Five nights at DIFA è un gioco 2D sviluppato come progetto d'esame per il corso di Programmazione per la Fisica dell'anno accademico 2022-2023. L'obiettivo del protagonista è fuggire dai sotterranei di un edificio in cui imperversa un terribile virus. All'interno del progetto sono implementati il modello SIR e semplici algoritmi utili alla realizzazione di giochi. +\end{abstract} + +\tableofcontents + +\newpage + +\section{Il progetto} +Il gioco è stato realizzato interamente in \verb|C++| con l'ausilio di alcuni strumenti di sviluppo che verranno trattati più in dettaglio nella sezione \ref{section:tools}. Sono state utilizzate due librerie esterne: +\begin{itemize} + \item \verb|SFML|, per il rendering grafico degli elementi di gioco e per la gestione dei movimenti delle entità all'interno del gioco; + \item \verb|tmxlite|, una libreria per la lettura di mappe in formato \verb|.tmx|. +\end{itemize} +Le mappe \verb|.tmx| (quella di gioco e quella di test) che si trovano nell'archivio del progetto sono state disegnate con il programma esterno Tiled, che non è necessario per la compilazione e l'esecuzione del codice. + +L'archivio del progetto è strutturato in diverse sotto-cartelle per tenere organizzati il codice e le risorse utili all'esecuzione del programma: +\begin{itemize} + \item \verb|assets|, contenente le mappe e tutte le \textit{texture} usate nel gioco; + \item \verb|modules|, utilizzata in combinazione con \verb|CMake| per rilevare la libreria esterna \verb|tmxlite|; + \item \verb|src|, contenente tutto il codice sorgente suddiviso in cartelle contenenti una classe ciascuna, la quale è separata in un \textit{header file}, un file sorgente e un file di test; + \item \verb|test|, utilizzata per creare un eseguibile contenente i test con \verb|doctest|. +\end{itemize} + +\subsection{Istruzioni per la compilazione} +La procedura descritta in seguito fa riferimento alla piattaforma Ubuntu \verb|22.04| (o equivalentemente a un sistema Windows con \verb|WSL|), su cui devono essere già installati \verb|CMake, git| e \verb|g++|. Per compilare il programma è necessario installare anche le due librerie esterne. \verb|SFML| è disponibile tramite il package manager di Ubuntu, quindi può essere installato con il seguente comando: +\begin{verbatim} + $ sudo apt install libsfml-dev +\end{verbatim} +Al contrario, \verb|tmxlite| dev'essere scaricato e compilato dalla sua \href{https://github.com/fallahn/tmxlite}{\textit{repository}} di GitHub. È possibile farlo seguendo questi passaggi: +\begin{verbatim} + $ git clone https://github.com/fallahn/tmxlite + $ cd tmxlite/tmxlite + $ mkdir build && cd build + $ cmake .. -DTMXLITE_STATIC_LIB=TRUE + $ sudo make install +\end{verbatim} +Estraendo l'archivio contenente il codice, è ora possibile proseguire con la compilazione. Il codice può essere compilato in \textit{Debug} mode, che abilita l'\textit{address sanitizer}, o in \textit{Release} mode. In entrambi i casi bisogna seguire la medesima procedura di compilazione dalla cartella principale del progetto: +\begin{verbatim} + $ cmake -B build -S . -DCMAKE_BUILD_TYPE=Debug/Release + $ cmake --build build +\end{verbatim} +Per eseguire i test si può lanciare il seguente comando: +\begin{verbatim} + $ build/all.test +\end{verbatim} +Per far partire il gioco si utilizza invece il seguente: +\begin{verbatim} + $ build/fnad +\end{verbatim} +In \textit{Debug} mode vengono segnalati dei \textit{memory leak} dall'\textit{address sanitizer}. Essi sono dovuti a codice presente nelle librerie esterne e non sono dovuti a codice scritto direttamente nel progetto. + +\subsection{Obiettivo del gioco} +Il protagonista si trova catapultato nei sotterranei del Dipartimento di Fisica e Astronomia dell'Università di Bologna. Al suo interno imperversa una feroce epidemia che dilaga seguendo il modello SIR\footnote{Per la descrizione del modello si rimanda alla sezione \ref{section:sir}} e che rende i personaggi presenti nel gioco aggressivi nei confronti del protagonista. Il suo obiettivo è quello di raccogliere tre chiavi entro un tempo limite per poter fuggire da una delle porte presenti nei sotterranei prima che i nemici lo colpiscano tre volte. + +\subsection{Comandi} +Per selezionare il livello all'inizio del gioco si possono utilizzare i tasti da \keys{1} a \keys{5} della tastiera o i tasti da \keys{1} a \keys{5} del tastierino numerico (con \keys{NumLock} attivato). Per muoversi all'interno della mappa sono disponibili i tasti \keys{W}, \keys{A}, \keys{S}, \keys{D} o le freccette (\keys{\arrowkeyup}, \keys{\arrowkeyleft}, \keys{\arrowkeydown}, \keys{\arrowkeyright}). Non è disponibile alcun altro comando\footnote{In realtà nel gioco è presente anche un piccolo \textit{easter egg}.}, le chiavi vengono raccolte automaticamente e per vincere è sufficiente passare sopra una porta dopo aver raccolto tre chiavi. Per chiudere il gioco dopo aver visto la schermata con il risultato si può premere \keys{INVIO}. + +\section{Strumenti di sviluppo}\label{section:tools} +Per migliorare la qualità del prodotto finale sono stati utilizzati diversi software che vengono spiegati dettagliamente nel seguito. +\subsection{CMake} +\verb|CMake| è uno strumento di gestione del processo di \textit{build} che è stato fondamentale per la realizzazione del progetto. Esso ha permesso di organizzare tutti i comandi necessari alla compilazione in un semplice file di configurazione che è riportato nell'archivio del progetto, \verb|CMakeLists.txt|. In breve, il file di configurazione di \verb|CMake| esegue questi passaggi: +\begin{itemize} + \item imposta delle avvertenze da mostrare durante la compilazione per prevenire alcuni errori comuni; + \item abilita l'\textit{address sanitizer} in \textit{Debug} mode per segnalare eventuali \textit{memory leak}; + \item cerca le librerie esterne \verb|SFML| e \verb|tmxlite|; + \item crea un eseguibile per il programma contenente tutti i file sorgente e collega le librerie esterne; + \item costruisce un eseguibile di test unendo tutti i file di test e i rispettivi file sorgente, collegando anche in questo caso le librerie esterne. +\end{itemize} + +\subsection{Clang Format} +Clang Format è uno strumento di formattazione del codice che è stato utilizzato per applicare regole di formattazione comuni a tutti i file del progetto. È stata utilizzata la configurazione \quotes{google} fornita da Clang Format senza apportarvi alcuna modifica. Tutto il codice \verb|C++| del progetto è stato formattato con questo strumento. + +\subsection{Git e GitHub} +Per tenere traccia delle modifiche effettuate nel corso del progetto è stato utilizzato Git, un sistema di \textit{version control} che permette di consolidare insiemi di modifiche apportate al codice in unità dette \textit{commit}. Per la condivisione della \textit{repository} creata con Git è stato utilizzato \href{https://github.com/LuckeeDev/fnad}{GitHub}, un servizio online per la condivisione di codice. GitHub offre diversi strumenti per facilitare la collaborazione, che sono stati usati ampiamente per il completamento del progetto. Ogni modifica è stata effettuata tramite un'apposita \textit{Pull Request}, all'interno della quale sono stati sfruttati gli strumenti di discussione e di revisione del codice offerti dalla piattaforma. Inoltre, fino al momento dell'aggiunta delle \textit{texture} dei personaggi è stata utilizzata una \textit{GitHub Action} apposita per la compilazione del programma e l'esecuzione dei test automatica ad ogni \textit{push} e ad ogni \textit{Pull Request}. Dopo aver aggiunto le \textit{texture} non è stato più possibile adottare questa strategia di testing perché le macchine virtuali di GitHub su cui vengono eseguite le \textit{Actions} non sono dotate di scheda grafica. + +\section{Strategie utilizzate} + +\subsection{Il modello SIR}\label{section:sir} +Il modello SIR è stato implementato nei file \verb|epidemic.hpp| ed \verb|epidemic.cpp|. Sono state create due \verb|struct|: \verb|SIRState|, contenente tre \verb|double|, e \verb|SIRParams|, contenente due \verb|double|. \verb|SIRState| indica lo stato del modello SIR considerando S, I e R come variabili continue, mentre \verb|SIRParams| indica i parametri $\beta$ e $\gamma$ che regolano l'andamento dell'epidemia.\\ +È stata infine creata una classe, chiamata \verb|Epidemic|, avente i seguenti dati membri: +\begin{itemize} + \item \verb|sir_state_| (di tipo \verb|SIRState|), indica lo stato in tempo reale dell'epidemia; + \item \verb|sir_params_| (di tipo \verb|SIRParams|), indica i parametri che regolano l'andamento dell'epidemia; + \item \verb|enemies_| (di tipo \verb|std::vector|), contiene la popolazione di nemici a cui viene applicato il modello SIR; + \item \verb|days_per_second_| (di tipo \verb|double const|), indica quanti giorni del modello SIR passano ogni secondo di gioco (il suo valore è $0.5$). +\end{itemize} +Il costruttore di \verb|fnad::Epidemic| non accetta parametri e quando viene chiamato istanzia un'epidemia di default con i seguenti valori: +\begin{itemize} + \item \verb|sir_state_ = {1., 1., 0.}|, + \item \verb|sir_params_ = {0.7, 0.05}|. +\end{itemize} +Questa scelta permette di impostare l'epidemia in seguito alla scelta del livello di gioco da parte dell'utente. La creazione vera e propria di un oggetto di tipo \verb|fnad::Epidemic| avviene infatti tramite il metodo pubblico \verb|resetSIRState|, il quale prende in input un oggetto \verb|fnad::SIRState|, da cui estrae i valori da assegnare a \verb|sir_state_|, e un oggetto \verb|fnad::Map|, dal quale ottiene i dati necessari per generare i nemici nei punti corretti della mappa. L'oggetto \verb|fnad::Map| possiede un dato membro di tipo \verb|std::vector| (chiamato \verb|spawners_|), il quale contiene una collezione di oggetti \verb|fnad::Spawner| (che verranno chiamati \textit{spawners}). Ogni \textit{spawner} rappresenta una certa regione della mappa ed è dotato di un metodo pubblico (\verb|getSpawnPoint|) che restituisce un punto casuale all'interno di essa. A ciascun nemico viene dunque associato casualmente uno \textit{spawner} con probabilità proporzionale all'area di quest'ultimo, il quale assegna una posizione sulla mappa al nemico tramite il metodo \verb|getSpawnPoint|. + +L'evoluzione dell'epidemia è gestita dal metodo pubblico \verb|evolve|. Questa funzione prende in input un oggetto di tipo \verb|sf::Time| (che chiameremo \verb|dt|) e un altro di tipo \verb|fnad::Character| (che chiameremo \verb|character|). \verb|dt| indica il tempo passato nel gioco, che viene utilizzato per capire di quanto deve evolvere l'epidemia seguendo il modello SIR. Invece \verb|character| serve per determinare il movimento dei nemici infetti. Infatti \verb|evolve|, oltre a gestire l'evoluzione logica della pandemia, ne gestisce anche quella fisica, stabilendo lo spostamento grafico da applicare ad ogni oggetto contenuto nel vettore \verb|enemies_|: i nemici infetti che \quotes{vedono}\footnote{Un nemico \quotes{vede} il personaggio se il segmento che li congiunge non interseca alcun muro della mappa. Questo aspetta verrà trattato nella sezione \ref{section:vision}} il personaggio si muovono verso di lui, mentre tutti gli altri (tranne i rimossi) compiono un moto casuale.\\ +Ogni volta che viene chiamato il metodo \verb|evolve| (quindi ogni \textit{frame} di gioco) viene aggiornato il dato membro \verb|sir_state_| tramite le seguenti equazioni: +\begin{align*} + S_i &= S_{i-1} - \beta \frac{S_{i-1}}{N} I_{i-1} \Delta T\\ + I_i &= I_{i-1} + \left( \beta \frac{S_{i-1}}{N} I_{i-1} - \gamma I_{i-1} \right) \Delta T\\ + R_i &= R_{i-1} + \gamma I_{i-1} \Delta T +\end{align*} +dove $S_i$, $I_i$ e $R_i$ indicano, rispettivamente, i valori delle variabili \verb|sir_state_.s|, \verb|sir_state_.i| e \verb|sir_state_.r| nell'$i$-esimo frame di gioco, $S_{i - 1}$, $I_{i - 1}$ e $R_{i - 1}$ indicano i valori posseduti dalle stesse variabili nel frame precedente, $N$ indica il numero totale di nemici (pari alla dimensione di \verb|enemies_|), $\beta$ e $\gamma$ indicano i valori delle variabili \verb|sir_params_.beta| e \verb|sir_params_.gamma| (costanti durante l'intera partita) e $\Delta T$ indica il valore di \verb|dt|.\\ +Il dato membro \verb|sir_state_| rappresenta lo stato continuo dell'epidemia (\verb|s|, \verb|i| e \verb|r| sono \verb|double|), ma ai fini del gioco serve una rappresentazione discreta di tale stato (avrebbe poco senso infettare un numero non intero di nemici). È dunque necessario tradurre questi \verb|double| in tre \verb|int|. A tale scopo, all'interno di evolve, ciascun dato membro di \verb|sir_state_| viene arrotondato all'intero ad esso più vicino e, confrontando l'\verb|int| così ottenuto con il numero corrente di nemici suscettibili, infetti o rimossi si è in grado di calcolare il numero di nemici da infettare e da rimuovere per aggiornare lo stato dell'epidemia. + +\subsection{Gestione delle collisioni} +Per gestire le collisioni con i muri, assieme alla mappa grafica è stata creata una \quotes{mappa logica} con il programma esterno Tiled. Essa consta di quattro livelli di oggetti rettangolari che vengono interpretati dal programma per creare il mondo di gioco. In particolare, il primo livello è quello contenente i muri, il secondo gli \textit{spawner} di nemici, il terzo le uscite e il quarto le chiavi. Per contenere tutte queste informazioni è stata creata la classe \verb|fnad::Map|, che tiene al suo interno quattro vettori contenenti elementi rispettivamente di tipo \verb|fnad::Wall| (semplicemente un alias per il tipo di \verb|SFML| \verb|sf::FloatRect|), \verb|fnad::Spawner|, \verb|fnad::Exit| (anch'esso un alias di \verb|sf::FloatRect|) e \verb|fnad::Key|. + +L'oggetto di tipo \verb|fnad::Map| viene creato all'interno della classe \verb|fnad::Game|, responsabile della gestione dei loop di gioco, e viene poi passato a ciascuna entità che viene creata dal gioco. La classe \verb|fnad::Entity| conserva una referenza alla mappa e la utilizza ogni volta che viene chiamato il metodo \verb|safeMove| per applicare il movimento richiesto tenendo conto della presenza dei muri. Il metodo sopra citato funziona analizzando le collisioni separatamente sull'asse $x$ e sull'asse $y$, per evitare che un movimento in diagonale possa permettere a un'entità di superare l'angolo di un muro senza essere bloccata. L'algoritmo cerca i muri con cui è avvenuta una collisione tramite il metodo di \verb|SFML| \verb|intersects| e seleziona fra questi il più vicino al punto di partenza dell'entità. Successivamente, calcola la correzione da applicare su ciascuno dei due assi cartesiani per riportare l'entità al bordo del muro. Il metodo \verb|safeMove| ritorna un oggetto di tipo \verb|fnad::Collision| che è poi utilizzato dal metodo di movimento casuale della classe \verb|fnad::Enemy| per invertire la direzione nel caso in cui ci sia stato un contatto con un muro. + +\subsection{Visione dei nemici}\label{section:vision} +Come accennato in precedenza, per determinare il movimento dei nemici infetti è necessario verificare che essi \quotes{vedano} il personaggio. Se non lo vedono si muovono casualmente, altrimenti si dirigono verso di lui.\\ +Per verificare se il personaggio rientra nel campo visivo di un nemico è stato scritto un metodo pubblico di \verb|fnad::Enemy| chiamato \verb|sees|, che prende in input un oggetto di tipo \verb|fnad::Character| (il personaggio) e restituisce un \verb|bool| (\verb|true| se vede il personaggio, \verb|false| se non lo vede). Per determinare questo risultato, viene innanzitutto ricavata l'equazione della retta (che indicheremo con $r$) che congiunge il personaggio e il nemico. Successivamente, per ogni muro della mappa vengono calcolate le equazioni delle rette contenenti i suoi quattro lati al fine di trovare i punti di intersezione tra la retta $r$ e le quattro rette del muro. Se almeno uno di questi punti appartiene al segmento che congiunge il personaggio e il nemico, allora la funzione restituisce \verb|false|, altrimenti restituisce \verb|true|. + +\subsection{Rendering grafico} +\subsubsection{Lo sfondo} +Per ottimizzare il rendering grafico di un componente molto elaborato come lo sfondo è stata utilizzata la classe di \verb|SFML| \verb|sf::RenderTexture|. Essa funziona in modo molto simile a una finestra, permettendo di disegnare qualsiasi elemento si desideri su di essa. Tuttavia, al contrario di una finestra, essa non viene mostrata immediatamente, ma può essere tenuta in memoria ed essere utilizzata in seguito a seconda della necessità, ad esempio per essere disegnata proprio su una finestra. + +La logica della rappresentazione dello sfondo è stata implementata nella classe \verb|fnad::Background|. Il suo costruttore accetta come input una mappa disegnata con Tiled (di tipo \verb|tmx::Map|) e ne legge tutti i \textit{tileset} (delle particolari immagini che contengono tutti i blocchi per costruire il mondo disegnato con Tiled) per caricare le corrispondenti \textit{texture} in una mappa privata all'interno della classe. In seguito, il costruttore procede a leggere tutti i livelli a partire dal quinto in poi. I primi quattro livelli sono infatti riservati alla costruzione logica della mappa, con i muri, gli \textit{spawner} di nemici, le chiavi e le uscite. Ciascuno di questi livelli viene passato alla funzione \verb|drawLayerToBackground| insieme ai \textit{tileset} letti in precedenza. Il livello viene letto a partire dall'angolo in alto a sinistra, scorrendo tutti i blocchi verso destra e scendendo poi alla riga di blocchi successiva. Ciascun blocco viene mappato così a un corrispondente oggetto di tipo \verb|fnad::Tile|, che indica il \textit{tileset} di riferimento e un quadrato utilizzato per \quotes{ritagliare} la \textit{texture} intera del \textit{tileset} all'immagine necessaria per rappresentare il singolo blocco. Per ciascun blocco viene successivamente creato un \verb|sf::Sprite|, ovvero un oggetto dotato di una \textit{texture}, su cui viene caricata la \textit{texture} del blocco e che viene disegnato nella posizione corretta all'interno della \verb|sf::RenderTexture|. In seguito al processo di lettura, la \textit{texture} completa viene stampata su un apposito \verb|sf::Sprite|, che viene poi richiamato dal metodo \verb|Background::draw| per essere disegnato sulla finestra all'interno del \textit{loop} di gioco. + +\subsubsection{I personaggi} +Per animare i personaggi sono state utilizzate delle sequenze di sei immagini per ognuna delle quattro direzioni in cui può muoversi ciascuna entità. Le classi \verb|fnad::Character| e \verb|fnad::Enemy| sono state dotate di un metodo pubblico \verb|animate|, dentro al quale è stata gestita la logica delle animazioni.\\ +Per minimizzare gli sprechi di risorse sono state caricate solo tre \textit{texture}: due per il personaggio (in movimento e in posizione statica) e una per i nemici, che non sono mai fermi. Dato che in una partita viene istanziato un solo oggetto di tipo \verb|fnad::Character| e molti oggetti di tipo \verb|fnad::Enemy|, a quest'ultimo è stato aggiunto un dato membro \verb|static| di tipo \verb|sf::Texture|, così da tenere in memoria un'unica \textit{texture} condivisa fra tutti i nemici. Sono stati aggiunti due dati membri di tipo \verb|sf::Texture| anche a \verb|fnad::Character|, tuttavia non sono stati messi \verb|static| perché in ogni caso viene sempre istanziato un unico oggetto di questo tipo per ogni partita. + +Per l'animazione di \verb|fnad::Character| serve innanzitutto decidere se utilizzare la \textit{texture} statica o quella dinamica. In seguito, in base alla direzione del personaggio, viene scelta una regione della \textit{texture} all'interno della quale vi sono i sei disegni con cui costruire l'animazione. Una volta determinata tale regione di interesse, con l'aiuto di un dato membro di \verb|fnad::Character| di tipo \verb|sf::Clock| e della funzione \verb|setTextureRect|, viene creata l'animazione ridimensionando ogni decimo di secondo la \textit{texture} del personaggio in modo che vengano mostrate in sequenza le sei immagini contenute nella regione di interesse. + +L'animazione di \verb|fnad::Enemy| funziona in maniera pressoché identica a quella di \verb|fnad::Character|, con l'unica differenza che non è necessario controllare se il nemico si stia muovendo o se sia fermo, poiché i nemici sono costantemente in movimento. + +\section{Testing e debugging} +Ciascun componente del gioco è stato accuratamente testato, ove possibile tramite l'utilizzo di \textit{unit tests}. Per scrivere i test è stato utilizzato \verb|doctest|, uno strumento di testing \textit{single-header} il cui codice è scritto nel file \verb|doctest.h| dell'archivio. In alcuni casi, soprattutto per i componenti grafici del gioco, non è stato possibile testare le funzionalità tramite codice, quindi sono state adottate altre strategie. + +\subsection{I test sulle classi di gioco} +I test scritti mirano a verificare che tutta la logica richiamata dalla classe \verb|fnad::Game| per gestire gli eventi di gioco si comporti come previsto. Per quanto riguarda le entità è stata verificata soprattutto la correttezza dei movimenti: il protagonista deve muoversi correttamente secondo l'input dell'utente, i nemici devono spostarsi in modo differente a seconda del loro stato SIR, e tutte le entità in generale non devono collidere con i muri. Inoltre, sono stati scritti dei test per verificare le collisioni fra i nemici e il protagonista, che devono portare a una diminuzione dei punti vita di quest'ultimo. + +Per la classe \verb|fnad::Epidemic| è stato verificato che l'evoluzione di una pandemia d'esempio ritornasse i risultati attesi e che, in seguito a un arbitrario numero di iterazioni, il vettore di nemici rimanesse sincronizzato con lo stato SIR continuo. Per le altre classi di gioco (\verb|fnad::Background|, \verb|fnad::Map|, \verb|fnad::Key|, \verb|fnad::Spawner|) è stato verificato che la loro creazione producesse gli oggetti attesi e che essi interagissero correttamente con il protagonista. + +\subsection{Metodologie di debug grafico} +Per studiare il comportamento del programma dal punto di vista grafico sono state adottate altre strategie. Ad esempio, sono state create diverse mappe con più livelli grafici per verificare che ogni oggetto disegnato con Tiled venisse effettivamente disegnato a schermo. Inoltre, la mappa è stata esplorata più volte per verificare il corretto posizionamento dei muri. Durante il corso del progetto sono stati anche creati piccoli programmi separati per studiare il comportamento di alcune funzionalità specifiche di \verb|SFML|, come i testi e le texture. + +\end{document}