Siamo arrivati all’ultimo capitolo della nostra mini-serie su come creare un roguelike 2D in Unity, ispirata al progetto open source 2DRogueTest.
Nella parte 1 abbiamo impostato il progetto, importato la grafica e realizzato il movimento base del personaggio. Nella parte 2 abbiamo aggiunto nemici, intelligenza artificiale e un sistema di combattimento semplice ma efficace.
Ora, per completare l’esperienza roguelike, ci occuperemo della generazione procedurale delle stanze: un sistema che ci permetterà di avere una mappa diversa ad ogni partita, aumentando la rigiocabilità e il fascino dell’ignoto.
1. Il cuore del sistema: la classe DungeonGenerator
Nel progetto troviamo lo script /Scripts/DungeonGenerator.cs
, responsabile della creazione dinamica delle stanze e della loro disposizione spaziale.
Questo script utilizza una lista di prefab di stanze (presenti in /Prefabs/Rooms/
) e li istanzia a runtime seguendo una semplice logica di espansione.
Ecco una panoramica del codice principale:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
public class DungeonGenerator : MonoBehaviour { public GameObject[] roomPrefabs; public int numberOfRooms = 5; private List<Vector2Int> usedPositions = new(); void Start() { GenerateDungeon(); } void GenerateDungeon() { Vector2Int currentPos = Vector2Int.zero; usedPositions.Add(currentPos); Instantiate(roomPrefabs[0], Vector3.zero, Quaternion.identity); for (int i = 1; i < numberOfRooms; i++) { Vector2Int nextPos; do { nextPos = currentPos + GetRandomDirection(); } while (usedPositions.Contains(nextPos)); usedPositions.Add(nextPos); Vector3 worldPos = new Vector3(nextPos.x * 16, nextPos.y * 10, 0); Instantiate(roomPrefabs[Random.Range(0, roomPrefabs.Length)], worldPos, Quaternion.identity); currentPos = nextPos; } } Vector2Int GetRandomDirection() { Vector2Int[] directions = { Vector2Int.up, Vector2Int.right, Vector2Int.down, Vector2Int.left }; return directions[Random.Range(0, directions.Length)]; } } |
Questo codice crea un layout a griglia, in cui ogni stanza ha coordinate logiche (tipo (0,0)
, (1,0)
, ecc.) e viene collocata a una distanza costante dalle altre per evitare sovrapposizioni.
Le stanze vengono scelte casualmente tra i prefab definiti nel campo roomPrefabs
.
2. Creazione delle stanze prefab
Nel progetto troverai vari prefab di stanze nella cartella /Prefabs/Rooms/
, ad esempio:
Room_Empty
Room_Enemies
Room_Loot
Ogni prefab ha una struttura coerente:
- un suolo (Tilemap o Sprite)
- muri e colliders
- eventuali spawn point per nemici e oggetti
È possibile creare nuovi prefab partendo da un modello base, modificando i contenuti all’interno.
3. Spawn dinamico di nemici e oggetti
Ogni stanza può contenere oggetti speciali o nemici. Per fare ciò, si utilizzano degli Empty GameObject come marcatori/spawn points.
Nel prefab puoi creare un oggetto vuoto chiamato ad esempio EnemySpawn
, e poi uno script come RoomContent.cs
lo rileva in fase di instanziazione:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class RoomContent : MonoBehaviour { public GameObject enemyPrefab; void Start() { foreach (Transform child in transform) { if (child.CompareTag("EnemySpawn")) { Instantiate(enemyPrefab, child.position, Quaternion.identity); } } } } |
Così ogni stanza può personalizzare cosa contiene in modo semplice.
4. Posizionamento del giocatore
Nel progetto, il Player
viene istanziato nella prima stanza generata, che si trova sempre in (0,0)
(posizione Vector3.zero
). Assicurati che il prefab Player
venga aggiunto alla scena all’avvio, oppure tramite script:
1 |
Instantiate(playerPrefab, Vector3.zero, Quaternion.identity); |
Nel progetto 2DRogueTest
, questo avviene nella scena principale tramite prefab.
5. Transizione tra stanze
Le stanze sono statiche ma possono essere collegate con dei trigger di porta o collider con script che spostano il giocatore nella stanza successiva.
Un esempio semplice:
1 2 3 4 5 6 7 |
void OnTriggerEnter2D(Collider2D other) { if (other.CompareTag("Player")) { other.transform.position = new Vector3(16, 0, 0); // posizione della stanza adiacente } } |
Un sistema più avanzato userebbe una mappa di collegamenti tra stanze, ma per ora il prototipo gestisce la continuità in modo lineare.
6. Rifiniture e miglioramenti futuri
Ora che il dungeon è generato proceduralmente, possiamo pensare a vari miglioramenti:
- effetti di transizione tra stanze (fade, animazioni)
- generazione di livelli sempre più ampi
- boss room e checkpoint
- salvataggio della mappa e rigiocabilità
- interfaccia grafica (HUD, HP, minimappa)
Il progetto 2DRogueTest
rimane volutamente semplice, ma la struttura è pensata per essere espandibile.
Conclusione
Con questa terza parte completiamo il nostro percorso nella creazione di un roguelike 2D in Unity. Partendo da un semplice personaggio in una mappa statica, abbiamo costruito:
- un progetto Unity 2D organizzato e riutilizzabile
- un sistema di movimento semplice e fluido
- nemici con IA base e combattimento corpo a corpo
- una struttura di punti vita e morte per tutte le entità
- una generazione procedurale di dungeon a stanze collegate
Il risultato è un prototipo funzionante, solido e adatto a essere ampliato. Il codice sorgente completo è disponibile su GitHub.
Ci auguriamo che questa guida ti abbia ispirato a creare il tuo dungeon personale. Se vuoi migliorare il progetto, pubblicare la tua versione o semplicemente imparare, sei nel posto giusto.
Al prossimo videogioco!