Le migliori pratiche per usare Godot prevedono l'applicazione dei principi della programmazione a oggetti. Scene, nodi e script sono i metodi offerti da Godot per creare oggetti riutilizzabili. Crei i tuoi giochi assemblando nodi, gli elementi base con funzionalità specifiche, raggruppandoli in strutture gerarchiche riutilizzabili, definite scene. Il comportamento di ogni nodo all'interno di una scena è controllato tramite script, ossia i programmi che definiscono la logica e l'interazione degli elementi di gioco.
Un mini-progetto per gli esperimenti
Prima di proseguire con lo studio di GDScript abbiamo bisogno di creare
un progetto dove fare esperimenti di programmazione. Analogamente a quanto visto nell'articolo Godot - Primi passi nell'ambiente di sviluppo, creiamo un nuovo progetto contenente una scena Node2D ed inseriamo uno Sprite2D per l'immagine del drone che vedete qui a sinistra (ha dimensione 64x64 pixel). Impostiamo la dimensione della finestra di gioco a 800x600 pixel e posizioniamo il drone alle coordinate (400, 300), come visto in Godot - Completiamo il tour dell'IDE. Per comodità di visione, cambiamo il colore di sfondo della finestra: dal menù Projects>Projects Settings>Rendering>Environment, click sul colore grigio scuro predefinito a fianco di Default Clear Color. Come nuovo colore scegliamo un grigio chiaro impostando il valore dddddd (6 "d" consecutive) nel campo Hex e premendo <INVIO> (ATTENZIONE: se non si preme <INVIO> il nuovo colore non verrà inserito); infine premere <CLOSE>.
Eseguiamo il progetto e avremo la finestra di gioco con il drone esattamente al centro:
A questo punto creiamo il seguente script per il nodo Sprite2D del drone:
Eseguendo il progetto, oltre alla finestra di gioco, nella parte inferiore dell'IDE verrà aperta una finestra di output con le stampe prodotte dallo script:
Sono state invocate le funzioni _init() e _ready(); la loro esecuzione avviene una sola volta al momento della creazione del nodo. L'ordine con cui sono state scritte nello script è ininfluente: _init() è sempre eseguita per prima, mentre _ready() è eseguita subito dopo.
NOTA BENE: all'interno di uno script le righe vuote sono ininfluenti, non sono necessarie per separare le funzioni che lo compongono. Lo script si può riscrivere nel modo seguente:
Programmazione a oggetti in Godot
Nella programmazione orientata a oggetti, una classe è un concetto che definisce un tipo di dato astratto caratterizzato da:
- un insieme di proprietà (o attributi o dati) che ne definiscono lo stato;
- un insieme di procedure (o funzioni o metodi) che definiscono le funzionalità o il comportamento della classe.
NOTA: invocare (o chiamare) una funzione/metodo significa eseguirne il codice di programmazione. - Esempio: una classe Persona, potrebbe avere:
- attributi come nome, cognome, genere, statura, peso;
- metodi come mangia(), cammina(), studia(), guida() ecc.
Una classe descrive quindi in modo astratto entità del mondo reale o di fantasia (come quelle di un videogame).
Un oggetto è una istanza di classe, ossia è l'entità concreta creata a partire dalla definizione astratta di classe. La creazione di un oggetto a partire da una classe si chiama istanziazione. Quando nel codice di programmazione viene istanziato un oggetto, esso occupa spazio in memoria.
ESEMPIO (NOTA BENE: il corsivo nell'esempio non è codice GDSCript):
- si crea una persona con istruzioni simili a
Persona p = new Persona()
la parola chiave new in molti linguaggi di programmazione serve ad invocare il costruttore della classe (ossia a creare in memoria l'oggetto reale di una certa classe); - si assegna un valore agli attributi
nome="Mario", cognome="Rossi", statura=175, peso=70 - la persona p esegue delle azioni:
p.mangia(patatine), p.studia(matematica)
e così via.
In Godot questi concetti vengono applicati nel modo seguente:
NODI = OGGETTI
I nodi sono le unità fondamentali di costruzione dei giochi. Il game-engine mette a disposizione una serie di classi predefinite, come Node2D e Sprite2D; ad esempio Node2D rappresenta oggetti in 2 dimensioni, con una posizone, una rotazione ed una scala, mentre Sprite2D serve a visualizzare immagini bidimensionali. In Godot esistono molti tipi di nodi, ciascuno con una funzionalità specifica.
Quando si aggiunge un nodo a una scena nell'editor, si sta creando un'istanza di una delle classi predefinite, ossia un oggetto.
SCENE = MODELLI
Una scena è un albero gerarchico di nodi e rappresenta un elemento riutilizzabile nel gioco, come un personaggio, un livello, un'interfaccia utente e così via. Quando si salva una scena nell'editor, Godot crea un file (con estensione .tscn
) che descrive la struttura dei nodi, le loro proprietà e le risorse associate.
Una scena è un modello (template) per scene dello stesso tipo. Ogni nuova istanza è una copia indipendente dei nodi definiti nella scena originale. Si possono creare più istanze di una scena all'interno di altre scene; quando si istanzia una scena, Godot legge il file .tscn
e crea un nuovo albero di nodi in memoria basato su quella descrizione.
Le scene quindi non sono classi nel senso stretto della programmazione del codice, ma svolgono una funzione simile a quella delle classi in termini di riusabilità, poiché definiscono la composizione di un oggetto di gioco, quali nodi lo compongono e come sono organizzati.
SCRIPT = CLASSI
Uno script è un file di codice GDScript che contiene la logica e il comportamento dei nodi. Uno script è una classe: collegare uno script a un nodo significa aggiungere funzionalità all'istanza specifica del nodo. Lo script può definire variabili membro (proprietà) e funzioni (metodi) che controllano il comportamento del nodo.
Uno script in genere estende un'altra classe, di solito uno dei nodi predefiniti, come Sprite2D: nella prima riga degli script ciò è evidenziato dalla parola chiave extends seguita dal nome della classe che si sta estendendo. Ad esempio, nello script creato per il mini-progetto di esperimenti c'è extends Sprite2D. Ciò permette di creare comportamenti specializzati basati su funzionalità esistenti; in programmazione a oggetti questa caratteristica è nota come ereditarietà: una classe derivata da una preesistente ne eredita le funzionalità ed eventualmente ne aggiunge altre.
Riepilogo
I nodi sono istanze di classi predefinite in Godot che forniscono funzionalità specifiche.
Le scene sono alberi di nodi salvati come modelli o "template". Sebbene non siano classi di codice nel senso della programmazione a oggetti, svolgono un ruolo simile alle classi nell'organizzazione e nella riusabilità.
Gli script sono classi che definiscono il comportamento dei nodi a cui sono collegati.
Quindi, mentre i nodi sono istanze di classi e gli script sono classi, le scene sono descrizioni per creare insiemi di nodi. Godot consente di combinare queste entità: si creano scene componendo istanze di nodi e aggiungendo script a tali nodi per programmarne il comportamento specifico, in base a eventi che si verificano durante il gioco.
Le funzioni _init() e _ready()
In Godot qualsiasi funzione viene eseguita al verificarsi di determinati eventi. Negli script non c'è un "punto di entrata" del codice, ossia l'analogo della funzione/metodo main() che troviamo in linguaggi di programmazione come C/C++ o Java, e da cui parte l'esecuzione. Ci sono tuttavia alcune funzioni, come _init() e _ready() che vengono eseguite sempre in modo automatico.
La funzione _init() si comporta come costruttore della classe dello script:
- viene eseguita una sola volta quando l'oggetto (il nodo a cui è attaccato lo script) viene creato in memoria, ossia al momento della sua istanziazione;
- esegue le operazioni di inizializzazione di base che riguardano la struttura interna dell'oggetto, per esempio l'impostazione dei valori degli attributi (variabili membro) dell'oggetto.
La funzione _ready() viene eseguita dopo _init():
- viene invocata una sola volta dopo che il nodo a cui è collegato lo script è stato aggiunto all'albero della scena (e dopo che tutti gli eventuali figli del nodo hanno completato la loro inizializzazione e notificato a loro volta di essere pronti);
- esegue operazioni che dipendono dalla funzione del nodo stesso, per esempio operazioni che dipendono dalla posizione del nodo nella scena;
- la fine dell'esecuzione di questa funzione notifica (al nodo genitore) che il nodo è pronto ad essere usato.
Avendone compreso funzionamento, possiamo sfruttare la funzione _ready() per fare esercizi in GDScript: poiché viene sicuramente eseguita una volta, la useremo come se fosse il punto di entrata del nostro codice. Inoltre, non dovendo eseguire particolari inizializzazioni, negli esercizi possiamo evitare di scrivere la funzione _init(). Modifichiamo lo script precedente così:
Eseguendo il progetto otterremo la seguente finestra di output:
Nei prossimi articoli useremo la stessa tecnica per illustrare le caratteristiche di GDScript.