Come sviluppare video games con Unity: introduzione

Unity è una game engine per lo sviluppo di videogiochi 2D e 3D sviluppata da Unity Technologies.

La sua larga diffusione è dovuta principalmente a:

  • Semplicità di utilizzo: Unity offre un set di componenti e un’interfaccia di base che permettono anche a non-programmatori di creare dei semplici prototipi.
  • Portabilità: grazie al cross compiler di mono, un gioco sviluppato in Unity può essere esportato per un grandissimo numero di piattaforme, anche talvolta difficilmente accessibili, con relativamente poco sforzo.
  • Flessibilità: È uno dei pochissimi tool che permette di creare praticamente qualsiasi tipologia di gioco, dal 2D, al 3D, alla realtà aumentata, XR, VR ecc…
  • Assets Store: Unity offre uno store in cui è possibile trovare tantissimi plugin e librerie che ne espandono il funzionamento in ogni forma.
  • Personalizzazione dell’editor stesso: Unity offre agli sviluppatori un’intera API che permette di creare nuove finestre, interfacce o utility integrate nel tool, per creare un ambiente quanto più affine possibile alla tipologia di gioco che si vuole sviluppare.

 

L’ingrediente base di Unity: I GameObject

I videogiochi sono software con specifiche molto diverse tra loro, ma Unity introduce alcuni livelli di astrazione che permettono agli sviluppatori di sbizzarrirsi nella creazione di meccaniche di gioco più disparate.

Il pattern adottato da Unity, e che è di fatto quasi standard nello sviluppo di videogiochi in generale, è il pattern component.

In Unity, l’elemento chiave da cui è possibile creare qualsiasi elemento di gioco sono i cosiddetti GameObject. Un GameObject è un oggetto generico, senza identità, a cui però possono essere applicati un qualsiasi numero di componenti. Ogni componente modifica o espande le capacità di questo Game Object, trasformandolo di fatto in qualsiasi cosa: un personaggio, una luce ambientale, un emettitore di suoni, GameManager o qualsiasi altro elemento del gioco.

I GameObject vivono all’interno delle Scene, che non sono altro che contenitori logici che raggruppano i GameObject che saranno attivi nello stesso contesto. Una scena può essere ad esempio un livello del nostro videogioco.

 

Il componente Transform

A ogni GameObject, al momento della creazione, viene immediatamente applicato un componente speciale, chiamato `Transform`, che non può essere rimosso. Il transform fornisce al GameObject due comportamenti principali:

  • Delle coordinate spaziali, inclusive di rotazione e scala nello spazio 3D
  • Una struttura gerarchica ad albero all’interno della Scena di gioco

Infatti i GameObject possono essere innestati in una struttura gerarchica padre-figlio grazie al componente Transform. La gerarchia tra GameObject ha alcune proprietà:

  • La posizione, scala e rotazione di un GameObject è funzione di quella del suo genitore. Ad esempio, spostando un certo GameObject nello spazio 3D, anche tutti i figli si muoveranno di conseguenza.
  • Distruggendo un GameObject padre, si distruggeranno di conseguenza anche tutti gli elementi figli

Questa struttura apparentemente contorta risulta molto utile in molti contesti. Ipotizziamo ad esempio di avere un personaggio che in mano ha una pistola. Muovendo il personaggio, vorremmo che anche la pistola si muova di conseguenza, essendo nella sua mano. Questo può essere fatto, ad esempio, impostando il GameObject “pistola” come figlio del GameObject “personaggio”.

 

Attributi e Proprietà

A ogni GameObject, oltre che aggiungere dei componenti, è possibile associare un nome, un tag e un layer. Il tag e il nome possono essere usati come parametri di ricerca (è possibile cercare i GameObject per nome e tag), i layer come filtro (ad esempio è possibile configurare che una camera riprenda certi oggetti e non altri, basandosi sul layer, oppure che certi oggetti fisici interagiscano con certi layer e non con altri).

La ricerca per nome è sicuramente comoda, ma in genere ne è sconsigliato l’utilizzo se si tratta di un’operazione da fare in modo molto frequente, in quanto può creare problemi di performance.

I layer al contrario sono molto leggeri, ma è possibile crearne un numero limitato, in quanto sono rappresentati tramite una bitmask.

I tag invece tornano utili anche per discriminare categorie di GameObject. Per esempio, potremmo taggare tutti i nemici col tag Enemy, e infliggere loro danno con l’arma solo se hanno questo tag.

Infine, è possibile attivare o disattivare un certo GameObject tramite la checkbox in alto a sinistra nell’ “Inspector”. Disattivandolo, un GameObject resterà nascosto nella scena, ma sarà pronto da essere riattivato a comando. Ad esempio, potremmo creare un menu di gioco che appare solo alla pressione di un tasto, o una trappola che compare solo quando entriamo in una certa area d’azione.

 

L’interfaccia

Per comprendere di cosa stiamo parlando, proviamo a tastare con mano l’interfaccia del programma.

Alla prima apertura di Unity, dopo aver creato il nostro primo progetto vuoto, ci troveremo davanti una cosa del genere.

Unity Editor Window

In basso nella finestra Project possiamo vedere i file e le cartelle presenti nel nostro progetto. È possibile crearne di nuovi, eliminarli o importare file tramite drag and drop.

Unity Project
La tab Project

In alto a sinistra c’è la Hierarchy, che lista tutti i GameObject della scena corrente. Di default, nelle nuove scene, Unity crea un GameObject chiamato Main Camera, che non è altro che un normale GameObject con ad esso associato un componente di tipo Camera. Parleremo di cos’è una camera in seguito.
Dalla Hierarchy è possibile creare nuovi GameObject, rinominarli, innestarli nella gerarchia ed eliminarli.

Unity Hierarchy view
La Hierarchy della Scena corrente

Selezionando un GameObject nella Hierarchy, noterete il menu di destra cambierà aspetto. Quello è l’Inspector. Questo menu serve a mostrare i dettagli dei GameObject selezionati, aggiungere nuovi componenti e modificarne i parametri.
Selezionando invece un asset (file) nella tab Project, l’inspector ne mostra i dettagli, e permette di modificare le proprietà dell’oggetto.

Unity Inspector View
L’inspector quando la Main Camera è selezionata

In centro vediamo la tab Scene, che è la finestra in cui Unity mostra graficamente gli elementi della scena nello spazio 3D. Tramite i tool in alto a sinistra, è possibile spostare, ruotare o scalare i vari oggetti della scena. In questa visualizzazione è presente una griglia. Ogni cella è larga esattamente 1 unit, un’unità di misura, che è sostanzialmente l’equivalente dei metri nella vita reale e, per convenzione, 100 pixel nel mondo bidimensionale.

La tab Game, invece, è una preview del gioco vero e proprio. Premendo il pulsante Play entreremo in play mode, e potremo testare il gioco direttamente dall’editor.

ATTENZIONE: Quando il gioco si trova in play mode, tutte le modifiche fatte alla scena non verranno salvate e si resetteranno uscendo da questa modalità.

Rimando alla documentazione ufficiale per i dettagli sulle altre finestre e tab.

 

I componenti

Unity offre di default un’enorme quantità di componenti con le funzionalità più disparate. Dal rendering 3D, all’audio direzionale, alla gestione delle UI, agli effetti di post-processing, luci, fisica e qualsiasi cosa che vi può venire in mente per un gioco.

Di molti componenti ne esistono due versioni, una pensata per giochi 2D, una per giochi 3D.

Ecco alcuni esempi:

  • MeshRenderer/SpriteRenderer: sono due componenti che associano al GameObject corrente un oggetto 2d o 3d, come ad esempio un personaggio, uno sprite, un modello 3d, un terreno o altro.
  • AudioSource: è un componente che emette un suono o una musica. Può essere un suono 2D o 3D.
  • Camera: è un componente che serve a indicare l’area di visione del giocatore sullo schermo. Proprio come una vera telecamera, spostandola e ruotandola nello spazio 3D è possibile inquadrare diversi oggetti su schermo. Possono esserci più camere attive nella scena che inquadrano tipologie di oggetti diversi. Ad esempio, potremmo avere una camera che inquadra solo le UI e una per gli oggetti 3d della scena.
  • Rigidbody / Rigidbody2D: è un componente che aggiunge delle caratteristiche fisiche ad un certo game object. è possibile ad esempio definire la gravità, il peso, e possiamo farlo interagire con altri GameObject nella scena tramite interazioni fisiche.
  • Collider / Collider2D: permettono di definire un volume o area di collisione ad un certo GameObject. Se combinati ad un Rigidbody, ad esempio potremmo creare un muro, degli scalini, personaggi, o altri oggetti fisici che interagiscono tra loro.

Unity permette inoltre di creare componenti personalizzati, programmabili in C#. I componenti sono di fatto quasi l’unico modo con cui gli sviluppatori possono intervenire sul funzionamento del gioco, ma ne parlerò in dettaglio nella seconda parte.

I componenti interagiscono tra loro e, spesso, hanno delle dipendenze. Come nel caso già detto, un Rigidbody ha bisogno di almeno un collider per funzionare correttamente.
Un MeshRenderer ha bisogno di un componente MeshFilter per gestire i modelli 3D e così via.

I componenti, infine, esattamente come i GameObject, possono essere disattivati tramite la checkbox in alto a sinistra nell’Inspector vicino al nome del componente. Questo permette di attivare o disattivare il funzionamento di un certo componente in base alle necessità. Ad esempio, potremmo attivare o disattivare un AudioSource per interrompere o far sentire un certo suono durante il gioco.

 

 

Rendering e grafica

Per gestire i complicati processi di cui è composto il rendering della grafica 3D, Unity introduce un’astrazione per semplificarne la gestione e normalizzarla.

Per renderizzare un oggetto 3D sono necessari vari ingredienti, che si compongono per ottenere il risultato finale:

  • Una mesh, aka un “modello 3D” di partenza, creato con un programma di modellazione 3D (es: Blender, Maya, ecc…). È possibile anche trovarne di gratuiti su siti come https://free3d.com per fare le nostre prove.
  • Una o più texture da applicare alla mesh. Una texture è una normale immagine PNG o JPEG che verrà applicata sul nostro modello 3D
  • Uno o più shader, ovvero un piccolo programmino che decide come la mesh e la texture andranno effettivamente disegnati sullo schermo. Unity fornisce già molti Shader built-in.

Oltre a questi elementi, già tipici di qualsiasi renderer 3D, Unity introduce il concetto di Material.

Un Material non è altro che un asset creabile nella tab Project che unisce assieme uno shader, i suoi parametri, e una o più Texture. Il nome Material sta proprio ad indicare che ogni shader andrà a disegnare una certa (o più di una) texture sul modello 3D per dargli un certo aspetto, o farlo assomigliare ad un certo “materiale” (es: metallo, legno, pietra, ecc…).

Ricapitolando, abbiamo una Mesh a cui vengono associati uno o più Material. Ogni material è un’associazione tra uno shader e una o più texture.

Può sembrare contorto, ma una volta capito il meccanismo, è abbastanza chiaro.

Operativamente consiste in:

  • Creare un GameObject
    • Aggiungerci un componente di tipo Renderer (ad esempio: MeshRenderer)
    • Aggiungere un componente per definire la Mesh di riferimento (ad esempio: un MeshFilter)
  • Creare uno o più Material, a cui assoceremo:
    • Uno degli shader forniti da Unity o uno creato da noi
    • Una o più texture (ad esempio un’immagine PNG di un pavimento, di un muro o qualsiasi altro materiale stiamo simulando)
  • Associare al MeshRenderer i/il Material così creato

Ad esempio, se volessimo renderizzare una statuetta dorata in gioco, dovremmo:

  • Importare la nostra mesh della statuetta, ad esempio: https://free3d.com/3d-model/eagle-34606.html
  • Creare un nuovo Material che chiameremo “Gold“. Per farlo, premiamo col destro nella tab “Project” e selezioniamo Create -> Material.
    • Associarci uno shader che simula l’effetto metallico. Ad esempio usiamo lo shader Standard, alzando il parametro Metallic e abbassiamo Smoothness
    • Associarci una texture color oro, trascinandola dove c’è scritto “Albedo” nel Material (vedi sotto)

Ecco come apparirà nell’editor:

 

E con questo abbiamo creato il nostro primo oggetto 3D in Unity!

 

Cosa abbiamo imparato?

In questa prima parte abbiamo introdotto i concetti base di Unity: I GameObject, i Componenti, il Transform e come funziona il rendering 3D. Nella seconda parte introdurremo la programmazione in C#, creeremo il nostro primo componente personalizzato e impareremo ad usare i Prefab.


Hai un progetto da sviluppare?