Tip:
Highlight text to annotate it
X
Salve a tutti, eccoci alla quarta puntata sulle basi del motore di gioco Unity 3D; in questa puntata metteremo da parte,
anche se parzialmente, la scena 3D, per parlare degli script in Unity: cosa sono, a cosa servono e come vengono creati e utilizzati.
Attenzione, prima di procedere, una piccola premessa: se siete dei programmatori o avete già programmato
qualcosina in Unity, saltate questo video.
In questa puntata farò infatti un'introduzione agli script in Unity, parlando di elementi come istruzioni, variabili o funzioni,
tutte cose fondamentali ma ben note ai programmatori e che finirebbero per annoiare a morte questi ultimi,
quindi se è il vostro caso, davvero, chiudete questo video e ci vediamo alla prossima puntata,
quando parleremo delle collisioni e di come muovere gli oggetti... altrimenti, iniziamo!
Uno script è una serie di istruzioni da fornire a uno o più oggetti di una scena.
Gli script sono semplici programmi che, più che altro, servono ad interagire con altri programmi o parti di essi;
gli “altri programmi”, che poi sono i programmi applicativi in senso stretto, generalmente sono scritti in un altro linguaggio di programmazione,
tipicamente compilato o ibrido, mentre gli script vengono interpretati dalla macchina e a volte vengono creati proprio dall'utente finale,
che se ne serve per dialogare, per così dire, con i software veri e propri.
Esempi di script sono i comandi batch per il sistema operativo o per certi programmi (come Photoshop) o le macro,
mentre in Unity gli script vengono utilizzati, come detto, per far eseguire azioni o reazioni agli oggetti (quindi Unity
è un programma vero e proprio, un software in senso stretto, che è stato scritto e compilato dai programmatori
e che va installato ed eseguito, mentre quelli che scriveremo noi sono porzioni di codice Javascript,
che Unity interpreterà per gestire gli elementi del gioco).
Gli script servono per le funzioni più disparate: dal mostrare un conto alla rovescia sul monitor
(che è quello che faremo in questa puntata) al definire l'intelligenza artificiale dei bots; in ogni caso, si tratta di “cose da far fare”,
seguendo un ordine e istruzioni ben precise, agli elementi del gioco.
Fatte queste premesse, vediamo come creare il primo, semplice script in Unity, partendo da un progetto completamente vuoto
(e che comprende, quindi, solo la telecamera Main Camera, nella scena).
In questo tutorial useremo il linguaggio JavaScript per realizzare i nostri script; alcuni utenti mi hanno chiesto di fare tutorials
sugli script in linguaggio C#: probabilmente li farò in futuro, ma per iniziare meglio usare JavaScript, meno elegante del C# ma più semplice.
Gli script sono entità a parte ma per funzionare vanno associati a oggetti della scena,
non per forza oggetti visuali ma anche puramente virtuali.
Facciamo le cose con ordine: clicchiamo col tasto destro del mouse sulla scheda Project e scegliamo Create Folder,
creando una cartella che chiameremo, guarda un po', “Script”.
E' buona norma, infatti, organizzare gli elementi del progetto in cartelle, a seconda della loro natura;
qui abbiamo solo uno script, ma è meglio prendere certe buone abitudini da piccoli.
Con la cartella “Script” selezionata, facciamo click destro su tale elemento e scegliamo di creare un JavaScript.
Verrà creato un elemento con nome di default “NewBehaviourScript”, letteralmente “Nuove istruzioni di comportamento”
(dove per “comportamento” intendiamo proprio le cose-da-fare per gli oggetti al quale verrà associato lo script).
Rinominiamolo in “PrimoScript” e premiamo Invio, mantenendolo selezionato.
Nell'Inspector, anziché le schede delle proprietà, come per l'oggetto Main Camera o, come visto nella puntata precedente,
il Terrain, abbiamo un'anteprima del codice dello script.
Questa anteprima è, però, solo un'anteprima, cioè è di sola lettura; per modificarla, facciamo doppio click su PrimoScript,
in Project, oppure premiamo Invio mentre tale oggetto è selezionato.
Apparentemente non succederà nulla, ma dopo un po' (da pochi secondi a un paio di minuti, dipende da molte cose)
si aprirà un altro programma, chiamato MonoDevelop.
Mono è un IDE, ossia un ambiente di sviluppo integrato, che ci consente di definire gli script con estrema facilità.
Con il doppio click su PrimoScript, si aprirà appunto con tale file aperto, pronto per modificarlo.
Lo script JavaScript è infatti un file, creato automaticamente su disco quando l'abbiamo definito nel Project,
per cui potete modificarlo con qualsiasi editor – in realtà, potete utilizzare anche il blocco note, OpenOffice, Word, quello che volete:
uno script JavaScript è una sequenza di istruzioni scritte “in chiaro”, come potete vedere.
Il mio consiglio è quello di utilizzare Mono, per molti motivi che scoprirete strada facendo; ve ne anticipo un paio:
il testo dello script è colorato, con colori diversi se i termini utilizzati sono parole chiave del programma,
inoltre a sinistra in basso abbiamo un riepilogo delle funzioni – vedremo presto cosa sono – e per certi elementi
è previsto un utilissimo sistema di suggerimento automatico.
In questa prima fase, dovete conoscere tre concetti fondamentali di programmazione degli script (ne seguiranno altri,
molto importanti, ma per il momento esaminiamo questi): variabili, istruzioni e funzioni.
Una variabile è, in un certo senso, un... contenitore.
Il nome della variabile è l'etichetta del contenitore, quella che ci serve per prenderlo o indicarlo ad altri, tipo
“passami il contenitore blu” o “passami la scatola rossa”, e così via; il valore della variabile è il suo contenuto.
Il contenuto può essere di vario tipo e il “tipo” qui è detto “classe”, per cui continuando con l'esempio del mondo reale
potremmo dire che il contenitore di nome “variabile1” contiene due mele, ossia due oggetti di classe “mela”.
Le classi di elementi... elementari, nella programmazione JS per Unity, sono i numeri interi, i numeri in virgola mobile e le stringhe;
le loro classi sono chiamate rispettivamente int, float e string.
I numeri vengono indicati scrivendoli direttamente, tipo 10 o, per i numeri con virgola, 2.55, quindi useremo il punto
al posto della virgola; le stringhe, parole o frasi, vanno messe tra virgolette.
Scrivere 23.5 è diverso da scrivere “23.5”: nel primo caso si tratta di una variabile float, da usare con le espressioni matematiche,
nel secondo di una parola, da stampare a video o concatenare a una frase.
Esiste poi il tipo boolean, che ha valori true e false, senza “” (per distinguerli dalle parole, cioè stringhe, “true” e “false”);
le variabili boolean vengono usate all'interno di espressioni logiche, perché vogliono dire “vero” (true) e “falso” (false).
Come anticipato, in questa puntata creeremo un semplice timer.
Se pensate al timer, che tipo di informazione pensate di dover memorizzare e ricavare? Beh... il tempo!
Stabiliamo, in particolare, di voler esprimere il tempo in secondi, per cui ci servono dei numeri interi (quindi il tipo int;
il float invece va bene se vogliamo tenere traccia di decimi e centesimi di secondo).
Il valore del tempo trascorso va messo da qualche parte, perché vogliamo ricavarlo, per saperlo e per mostrarlo a video;
ci serve, quindi, una variabile, di tipo intero, come detto un attimo fa.
Abbiamo detto anche che il contenitore dell'informazione deve avere un'etichetta, un nome, per poterla richiamare in seguito.
Stabiliamo di chiamare il nostro contenitore “tempoRimanente”.
Nella finestra del programma Mono, con il file PrimoScript.js (js sta per “file di tipo JavaScript”, ovviamente),
sto mettendo un paio di righe tra “#pragma” e “function Start”, semplicemente premendo Invio mentre il cursore del mouse è lì, e sto scrivendo:
var tempoRimanente : int = 30;
Fate attenzione agli spazi e a maiuscole e minuscole.
Questa è un'istruzione, il secondo dei tre concetti da sapere.
Un'istruzione è un comando da dare al programma.
E' composta da varie parti, a seconda del tipo di istruzione; questa è un'istruzione di dichiarazione E assegnamento di una variabile.
In JavaScript, un'istruzione termina con il punto e virgola.
Stiamo dicendo a Unity: definiamo una variabile (var) chiamata tempoRimanente di tipo o classe intera
(due punti seguito dal nome della classe, quindi tempoRimanente : int significa “di tipo intero”),
inoltre a questa variabile diamo il valore numerico 30.
Abbiamo appena creato un contenitore con etichetta “tempoRimanente” contenente un numero: 30.
Non stiamo specificando “secondi” o altro: per Unity, quel 30 è solo un numero, siamo noi che lo useremo per contare il tempo...
a Unity non interessa il suo scopo: potrebbero essere 30 ore, 30 capre, 30 mele, è solo un valore, 30,
da usare con qualche espressione algebrica (che verrà resa come istruzione, qui).
Gli script sono successioni di istruzioni, da eseguire in successione o con “salti” a varie parti, come vedremo;
in realtà, anche solo la riga appena scritta costituisce uno script, perché quello che fa è creare una variabile...
il problema è che non fa nient'altro, quindi non serve a molto!!
Le funzioni, il terzo argomento da sapere, sono blocchi di istruzioni all'interno degli script.
A differenza delle istruzioni scritte “in piano”, come quella appena fatta, le istruzioni scritte all'interno di una funzione
vengono eseguite solo se la funzione viene “chiamata” (si può dire anche “invocata”) da qualche parte nel programma.
Una funzione può essere invocata tutte le volte che vogliamo e, ogni volta, eseguirà le operazioni contenute al suo interno.
Quando creiamo uno script, Unity crea automaticamente due funzioni particolari, Start e Update, ossia rispettivamente Avvia e Aggiorna.
Si trovano anche in PrimoScript, come potete vedere.
Ogni funzione ha un nome, che serve appunto a invocarla da altre parti del programma e a far eseguire le sue istruzioni;
in un certo senso, tutti noi eseguiamo delle funzioni: quando prepariamo il caffè, eseguiamo una serie di operazioni che servono, appunto,
a preparare il caffè... chi ha visto il terzo episodio di Guerre Stellari ricorderà invece il famoso “ordine 66”: invocato dall'imperatore,
faceva eseguire delle azioni ai cloni, azioni che quindi erano già scritte nel codice dei cloni ma che dovevano essere invocate per applicarle...
ecco, lo stesso avviene negli script per i nostri elementi: noi le scriviamo, ma se non le invochiamo da qualche parte, non vengono eseguite.
La dichiarazione di una funzione termina con la parentesi graffa chiusa, senza punto e virgola dopo.
NOTA: su concetti e strumenti di programmazione c'è molto, MOLTO altro da dire, visto che esistono costrutti particolari
per gestire il flusso delle operazioni, come gli if-then-else, i cicli for e while ed elementi più complessi come le classi e gli oggetti,
tuttavia per questo video – rivolto a chi non sa proprio nulla di programmazione e sta iniziando proprio con Unity e JavaScript –
ci fermiamo a questi tre: variabili, istruzioni e funzioni.
Se volete, fate un paio di minuti di pausa ora; tra poco, useremo le funzioni per far funzionare il timer.
La dichiarazione di una funzione avviene quindi con la parola chiave function, seguita dall'etichetta della funzione,
da una coppia di parentesi tonde (che servono per fornire dei dati alla funzione... lo vedremo un'altra volta)
e da un coppia di parentesi graffe: tutto quello che finisce racchiuso nelle parentesi è il “corpo” della funzione,
il set di istruzioni da eseguire quando la si invoca.
Se una variabile viene dichiarata dentro una funzione, questa vivrà solo nella funzione, quindi non sarà accessibile da fuori
e perderemo i dati una volta conclusa la funzione: ecco perché ho dichiarato e inizializzato “tempoRimanente”
fuori da qualsiasi funzione, all'inizio dello script.
Ora le funzioni potranno modificarne il valore, perché la variabile è stata definita fuori ed è quindi accessibile a tutte.
Le funzioni Start e Update sono funzioni particolari, in uno script Unity.
La funzione Start viene invocata automaticamente dal programma non appena l'oggetto che possiede lo script prende vita nel progetto,
quindi viene utilizzata per inizializzare qualche caratteristica dell'elemento o svolgere operazioni preliminari.
La funzione Update viene invocata automaticamente dal programma in continuazione, ossia ad ogni “tick”, ad ogni frame
o aggiornamento del gioco, anche 25, 30, 60 o più volte AL SECONDO.
Potremmo pensare di usare la funzione Update e di ritardarla in qualche modo per 1 secondo dopo aver decrementato
il valore della variabile tempoRimanente, realizzando così il nostro Timer... il problema è che la funzione Update NON PUO'
essere rallentata o congelata: per natura, deve scorrere continuamente... possiamo, però, creare una funzione a parte,
da eseguire separatamente, e quella sì che possiamo rallentarla, con un'istruzione particolare... bene, facciamolo!
Scriviamo, in un punto del file fuori da ogni funzione:
function decrementa() {
while(true) { tempoRimanente = tempoRimanente – 1; yield WaitForSeconds(1); 129 0:14:30,000 --> 0:14:37,000 } }
Fate attenzione a maiuscole e minuscole quando digitate nomi o comandi: JavaScript distingue tra minuscole e maiuscole,
All'interno della funzione, c'è un comando particolare, un'istruzione con un suo blocco di comandi: while(true)
Ok, fermi tutti! Che sta succedendo qui?
seguita da una coppia di parentesi graffe (quindi un blocco di istruzioni, proprio come per le funzioni) con dentro due istruzioni.
While è un ciclo, ossia un blocco che può essere ripetuto più e più volte, se certe condizioni (specificate all'interno delle parentesi tonde
o in altri modi, con comandi particolari) vengono soddisfatte.
In questo caso, abbiamo “while(true)”, con true che è una parola chiave ed è anche un valore: significa “vero”, o “1”.
L'istruzione “while(true)” significa quindi: “finché è vero” (ossia: “sempre”) “esegui il contenuto del blocco.”
Il contenuto del blocco sono le due istruzioni
tempoRimanente = tempoRimanente – 1; yield WaitForSeconds(1);
La prima istruzione è chiara: decrementa il valore della variabile “tempoRimanente”.
La seconda sfrutta un comando nativo e una funzione nativa di Unity; il tutto vuol dire “attendi un secondo”.
Unity mette a disposizione molte funzioni e comandi nativi per svolgere varie operazioni.
E' possibile conoscere queste funzioni con la documentazione online, i manuali e... i tutorial: in questo video,
state facendo la conoscenza di “yield WaitForSeconds” e il numero di secondi tra parentesi, ad esempio.
Ok, il senso della funzione è ora chiaro: ripetere in continuazione (“while(true)”) un'operazione di decremento di un valore
di variabile esterna, ritardando il termine di ciascun ciclo while di un secondo, così da fare un decremento al secondo, all'infinito.
Problema: questa funzione, decrementa(), non viene invocata da nessuna parte, quindi non partirà mai.
Per risolvere il problema, all'interno del blocco della funzione Start scriviamo questa istruzione: decrementa();
In pratica, stiamo invocando decrementa all'avvio (Start), alla creazione dell'oggetto che possiede questa funzione...
… già, ma quale oggetto? In realtà, non abbiamo associato lo script ad alcun oggetto della scena, quindi non verrà mai eseguito.
Prima di procedere, fate click sull'icona del dischetto in Mono, in modo da salvare lo script corrente,
anche perché se non salviamo le modifiche queste non verranno applicate, all'avvio del gioco (quando fate una modifica ad uno script,
accanto al suo nome in Mono apparirà un *, come sto mostrando a video; assicuratevi di non avere file con modifiche non salvate,
ossia con l'asterisco, prima di avviare il gioco).
Fatto questo, torniamo a Unity 3D, ancora aperto... consiglio a parte: se avete due monitor, mettete Unity in uno e Mono nell'altro,
anziché passare da una finestra all'altra nello stesso monitor.
Adesso, trasciniamo lo script dal Project sull'oggetto Main Camera, nella Gerarchia.
Selezionando ora Main Camera, vedremo nell'Inspector una scheda Script con il nostro Script: ciò significa che lo script
è associato alla telecamera, che all'avvio eseguirà Start e, poi, tutto il resto.
Non solo: significa anche che uno script, come altri elementi, può essere associato a più oggetti della scena o direttamente a un Prefab,
il che consente di riutilizzare certi codici... giusto per fare un esempio, gli script di intelligenza artificiale di un particolare tipo di nemici
possono essere forniti ai Prefab degli stessi, per cui tutte le istanze (ricordate il discorso Prefab-istanze?),
dicevo tutte le istanze dei nemici avranno quei codici.
Clicchiamo ora sul pulsante Play per avviare il gioco... beh, non succede nulla.
Il motivo è semplicissimo: lo script fa quello che deve fare, ma non c'è alcuna istruzione che gli dica di mostrare a video
il valore della variabile “tempoRimanente”!!
Vi chiedo un ultimo sforzo: devo parlarvi di una funzione nativa di Unity che serve a gestire l'interfaccia grafica della finestra.
Questa funzione ci serve per far apparire una scritta con il valore della variabile.
Riprendiamo lo script e scriviamo:
function OnGUI() { GUILayout.Label(tempoRimanente.ToString()); }
Salviamo lo script. OnGUI è una funzione speciale che contiene le istruzioni per scrivere o disegnare sull'interfaccia grafica 2D
(GUI, Graphic User Interface, interfaccia grafica utente) dell'elemento.
Al suo interno vanno poste istruzioni particolari, perlopiù native di Unity, che si trovano nella documentazione (sezione GUI, appunto)
e servono a inserire elementi visivi sull'interfaccia; in questo caso, stiamo utilizzando la funzione “Label()”,
una funzione che appartiene all'elemento nativo “GUILayout”, per cui stiamo dicendo “usa la funzione Label di GUI Layout”.
Label significa etichetta, per cui si tratta di scrivere sul Layout, a video, quello che c'è tra le parentesi (il parametro o argomento
della funzione Label)... e cosa c'è tra le parentesi?
C'è “tempoRimanente.toString()”.
Sappiamo cos'è tempoRimanente: è una variabile, da noi definita, di tipo numerico. Cos'è toString()?
E' una funzione nativa di Unity e JavaScript, per il tipo intero, che consente di trasformare il valore numerico della variabile che la usa,
nel nostro caso tempoRimanente, in una parola, quindi ora il 30 iniziale è sempre trenta, ma questa volta è una parola, non un numero,
e può essere scritto a video dalla funzione Label di GUILayout.
Salvato lo script, rifacciamo partire il gioco in Unity: questa volta, a video vedremo, stampato in alto a sinistra, il valore della variabile tempoRimanente.
Un'ultima nota a parte (ma non troppo): i commenti in linea.
Se volete prendere appunti nel file di codice, ad esempio per ricordarvi in futuro a cosa serve un'istruzione o una variabile,
potete scrivere dei “commenti”, che sono righe o porzioni di riga che verranno ignorate da Unity e che servono appunto solo a chi legge il codice.
Per scrivere un commento, scrivete due slash, senza spazi, quindi il vostro commento; i due slash e le parole che seguono dovrebbero apparire in verde.
Potete mettere un commento a destra di un'istruzione, per spiegare a cosa serve; a video, sto mettendo il commento
“chiamo la funzione decrementa” accanto all'istruzione decrementa(); , all'interno della funzione Start.
Ok, per questa puntata è tutto; purtroppo è solo la punta dell'iceberg per quanto riguarda gli script in Unity,
e dico purtroppo perché l'argomento è interessantissimo, ma lo riprenderemo – con progetti più interessanti – tra un paio di puntate...
per il momento, chiudiamo questa parentesi (che serviva da primo approccio con il lato scripting di Unity, nient'altro)
e torniamo sul percorso principale, fatto per ora di tanti piccoli elementi separati che però in seguito
ci serviranno per comprendere il “disegno più grande”, per così dire.
Nella prossima puntata vorrei parlarvi di alcuni comandi di base per aggiungere caratteristiche fisiche, come caduta e rimbalzo,
agli oggetti della scena, comunque visto che questa puntata era alquanto particolare, se avete dubbi o richieste di chiarimenti
su quanto appena discusso scrivete tutto nei commenti al video: vedrò di rispondervi all'inizio del prossimo video. A presto!