Questo è il secondo di due videotutorial sulla realizzazione di uno script di tipo "Look At" in Unity 2022 per far sì che un oggetto punti verso un altro.
Trascrizione del video
Salve a tutti!
Questo è il secondo di due videotutorial sulla realizzazione di uno script di tipo "Look At" in Unity 2022 per far sì che un oggetto punti verso un altro.
In particolare, nella prima parte abbiamo analizzato il problema e realizzato una soluzione che prevedeva una rotazione istantanea dell'oggetto; in questo tutorial, invece, realizzeremo una rotazione più lenta e graduale, utilizzando i Quaternioni e il metodo Slerp.
In questo tutorial darò per conosciuti argomenti come Time.deltaTime e il funzionamento del metodo Quaternion.Slerp, in quanto li ho trattati in altri videotutorial; se non conoscete questi argomenti, vi consiglio di dare prima un'occhiata a quei tutorial (trovate il link in descrizione).

Il modello 3D che utilizzerò in questo tutorial è lo stesso del primo video: "Surveillance Camera 1"; anche in questo caso, comunque, non è necessario scaricare quest'asset: io lo sto utilizzando per mostrare un esempio pratico, ma potete guardare il tutorial, e imparare, senza dover scaricare questo particolare modello 3D!
Nel video precedente abbiamo parlato degli assi globali e locali della telecamera, sia per individuare l'asse che identifica il suo orientamento che per indicare l'asse intorno al quale effettuare le rotazioni.
Abbiamo, poi, ridotto il problema al piano bidimensionale e calcolato l'angolo di rotazione intorno al quale far ruotare il modello 3D della telecamera di sorveglianza. Nel nostro script, quest'angolo si chiama deltaAngle ed è misurato in gradi.
Per poter utilizzare Slerp, abbiamo bisogno di tre cose:
il quaternione che indica l'orientamento iniziale;
il quaternione che indica l'orientamento target, da raggiungere;
un metodo per indicare, a ciascun frame dell'esecuzione del gioco, a che punto siamo (in un range da 0 a 1) nel passare dall'orientamento iniziale a quello finale.
Il primo punto è il più semplice: la proprietà rotation di transform, infatti, esprime l'orientamento dell'oggetto proprio in quaternioni, per cui per questo campo utilizzeremo proprio transform.rotation.
Il secondo punto non è poi così complicato: dato un orientamento iniziale espresso in quaternioni, infatti, il quaternione che esprime l'orientamento target ottenuto facendo effettuare una rotazione a quello iniziale è dato dal prodotto tra l'orientamento iniziale e la rotazione da effettuare.
La rotazione da effettuare, nel nostro script, è deltaAngle, che però è espressa in gradi: noi abbiamo bisogno che sia espressa sotto forma di Quaternione...
Per fortuna, la funzione Euler della classe Quaternion fa proprio al caso nostro: essa prende tre parametri (ossia gli angoli di rotazione intorno agli assi X, Y e Z locale dell'oggetto) e restituisce appunto un quaternione che rappresenta questa trasformazione.
Come detto poco fa, per ottenere il quaternione target dobbiamo moltiplicare quello iniziale a quello di rotazione, per cui in Update posso scrivere:
Quaternion targetRotation = (transform.rotation * (Quaternion.Euler(0f, 0f, deltaAngle)));
Subito dopo, posso aggiornare l'orientamento dell'oggetto assegnando a transform.rotation il quaternione calcolato da Quaternion.Slerp, con:
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, evaluationTime);
Ovviamente, adesso dobbiamo definire evaluationTime: un valore float, compreso tra 0 e 1, che rappresenta il momento in cui ci troviamo nel passare dall'orientamento iniziale a quello finale.
Per prima cosa, definisco ed inizializzo questa variabile, scrivendo:
private float evaluationTime;
all'inizio della classe dello script e:
evaluationTime = 0f;
all'interno del metodo Start.

La variabile evaluationTime va quindi aumentata ad ogni frame, cosa che può essere fatta scrivendo:
evaluationTime += Time.deltaTime;
subito dopo l'istruzione contenente Slerp.
Questo è, ovviamente, solo un esempio; il mio consiglio è quello di moltiplicare Time.deltaTime per una variabile, il cui valore potrà essere tarato in tempo reale nell'Inspector, fino ad arrivare ad una velocità di rotazione ottimale. Ho parlato di questo argomento nel mio tutorial su Time.deltaTime.
In questo modo, comunque, ad un certo punto evaluationTime arriverà a 1.0, perché viene incrementata ad ogni fotogramma di gioco senza un'opzione di reset... già, ma quand'è che dovremo resettarla?
La domanda può essere posta in questo modo: quand'è che siamo pronti per far partire una nuova rotazione del modello? La risposta è: quando il modello sta già puntando il Player, in attesa che questo si muova.
Normalmente vi direi che, quando il modello della telecamera di videosorveglianza punta il Player, e il Player resta fermo, allora deltaAngle vale 0, perché non c'è alcuna rotazione da effettuare; tuttavia, con i valori float, soprattutto in caso di conversioni tra diverse unità di misura, è meglio mantenere un certo margine, per cui diremo che l'animazione è finita (e può iniziarne una nuova, non appena il Player si muoverà) quando il valore assoluto di deltaAngle sarà minore di 1f.
Subito prima della creazione di targetRotation, all'interno del metodo Update, scrivo quindi:
if(Mathf.Abs(deltaAngle) < 1f) evaluationTime = 0f;
dopodiché metto le tre istruzioni seguenti (ossia: la definizione del quaternione target, il cambio di orientamento con Slerp e l'incremento di evaluationTime) all'interno del blocco else di questo if.

Salvo lo script così modificato, torno all'editor di Unity e premo Play per osservare il risultato. In questo caso, la velocità di rotazione è data esclusivamente da Time.deltaTime e può essere diminuita o aumentata, come detto precedentemente, moltiplicando Time.deltaTime con una variabile da tarare interattivamente.

Ricapitolando: in questo video abbiamo visto come convertire l'angolo di rotazione in Quaternioni così da calcolare il Quaternione che rappresenta l'orientamento target, quindi abbiamo utilizzato Slerp per effettuare delle rotazioni più lente, la cui velocità può essere tra l'altro tarata ma mantenendola indipendente dalla piattaforma di esecuzione, mediante Time.deltaTime.
Abbiamo inoltre definito una condizione di reset per Slerp, così da far ripartire la rotazione quando il Player è fermo e pronto per muoversi nuovamente.
Per questa mini-serie di due tutorial, quindi, è tutto! A presto!