Fin da quando inserivo monetine nei cabinati dei bar del mio paese i videogiochi hanno avuto su di me un fascino perverso e quando ho cominciato a digitare comandi su una tastiera il mio obiettivo era solo uno: programmare videogames. Poi, si sa, la vita ti porta spesso per vie che non ti aspetti e, sebbene continui a digitare comandi su una tastiera, l’output prodotto non sono pixel e poligoni ma, molto più freddamente, numeri e cifre. Mi ritaglierò quindi questo spazio per approfondire e trattare temi di game development che tanto mi stanno a cuore ma che non fanno propriamente parte del mio ambito lavorativo quotidiano incentrato prevalentemente sui gestionali.
Inauguro questo spazio con la generazione procedurale di mappe e lo farò attraverso l’uso di un Cellular Automata ovvero di un modello matematico utilizzato per descrivere l’evoluzione di sistemi complessi. In particolare prenderemo in esame il Game of life (gioco della vita) sviluppato dal matematico inglese John Conway e, attraverso le semplici regole descritte dal modello ed adeguando le stesse, vedremo come riuscire a creare mappe (come quella mostrata in figura) da utilizzare in un videogames in maniera procedurale.

Un esempio di quello che vogliamo ottenere alla fine di questa serie di articoli

Un esempio di quello che vogliamo ottenere alla fine di questa serie di articoli

Il gioco della vita descrive una griglia di celle dove ognuna di esse è circondata da altre 8 celle. Ogni cella può assumere due stati (viva o morta) e tutta la griglia si evolve ad intervalli discreti al termine dei quali ogni singola cella cambia o meno il suo stato a seconda delle seguenti regole:

  • Una cella morta torna in vita se è circondata da 3 vicini
  • Una cella viva sopravvive solo se è circondata da 2 o 3 vicini, altrimenti muore

Il tutto può essere rappresentato molto semplicemente attraverso una matrice (di booleani o di interi o di enumeratori a due stati) generata casualmente sulle cui celle applicheremo le due regole descritte sopra
Cominciamo a rappresentare la nostra classe MapGenerator che si occuperà di elaborare, gestire ed infine rappresentare la nostra mappa. Per farlo utilizzeremo C# che sarà il linguaggio che utilizzerò prevalentemente per gli articoli del blog, e che è anche il linguaggio utilizzato per scriptare in Unity3D strumento che utilizzeremo alla fine per rendere la nostra mappa giocabile.


public class MapGenerator
{
   bool[,] map;
   int width, height;
   int percentageOfLife;

   public MapGenerator(int width, int height, int percentageOfLife)
   {
       this.width = width;
       this.height = height;
       this.percentageOfLife = percentageOfLife;
       this.map = new int[width, height];
       generateMap();
   }
}

Rappresenteremo quindi la mappa come una matrice di booleani per cui la cella viva assumerà valore true mentre la cella morta assumerà valore false. Al costruttore passeremo le dimensioni della matrice ed una valore che rappresenta la percentuale che una cella si trovi nello stato “vivo” nel momento in cui la mappa viene generata per la prima volta. Il costruttore si commenta abbastanza da se, non fa altro che valorizzare le variabili private e richiamare un metodo di generazione della mappa che sarà il seguente:

private void generateMap()
{
   Random rnd = new Random();
   for (int x = 0; x < width; x++)
       for (int y = 0; y < height; y++)
           map[x, y] = rnd.Next(1, 101) <= percentageOfLife;
}

Anche su questo c’è poco da dire. Il metodo semplicemente cicla la matrice generando per ogni cella un numero random tra 1 e 100 e se questo numero è minore o uguale alla nostra percentuale di vita la cella assumerà valore true altrimenti valore false. Avremo quindi ottenuto la nostra griglia di celle cui poter far giocare il gioco della vita.

Ma se volessimo rappresentare la nostra mappa primordiale generata e quindi governata dal caos?

Lo scopo finale di questa serie di articoli sarà quella di ottenere una mappa da abbinare ad una scena di Unity3D  e attraversata da un pg controllato dall’utente, per ora ci accontenteremo di una rappresentazione molto spartana attraverso una serie di caratteri in cui un cancelletto (“#”) rappresenterà una cella viva mentre uno spazio vuoto (” “) rappresenterà una cella morta. Andiamo quindi a scrivere il metodo DrawMap che ci restituirà la nostra matrice di caratteri


public string DrawMap()
{
   StringBuilder mapbuilder = new StringBuilder();
   for (int y = 0; y &lt; height; y++)
   {
       for (int x = 0; x &lt; width; x++)
       {
           mapbuilder.Append(map[x, y]? "#": " ");
       } 
       mapbuilder.Append(Environment.NewLine);
   }
   return mapbuilder.ToString();
}

Ora non dovremo fare altro che creare un progetto console o windows form, istanziare il nostro MapGenerator, fornendogli le dimensioni e la percentuale di vita iniziale delle celle, e richiamare il metodo DrawMap per ottenere un risultato simile a quello in figura

Cellular1

Mappa di dimensioni 100×30 percentuale di vita delle celle 40

Il risultato è decisamente caotico e a guardarlo così sembra impossibile riuscire a pensare che questo miscuglio casuale di carattere possa avvicinarsi anche lontanamente alla mappa mostrata all’inizio dell’articolo. Ma siamo solo al primo passo…

 

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *