Programmare in C# e Unity3D – L’input da mouse, touch e sensori
Proseguendo con il discorso sull’input, parliamo ora degli input provenienti da mouse, oppure dall’accelerometro o dal touch screen di un device.
Input del mouse
Così come abbiamo fatto per l’input da tastiera, anche per il mouse è disponibile una funzione che rileva la pressione dei vari tasti, a cui è necessario passare solo l’indice del tasto premuto (0 per il sinistro, 1 per il destro, e così via).
Ad esempio:
void Update ()
{
if(Input.GetMouseButton(0))
{
Debug.Log("Mouse");
}
if(Input.GetMouseButtonDown(0))
{
Debug.Log("Bottone premuto");
}
if(Input.GetMouseButtonUp(0))
{
Debug.Log("Bottone rilasciato");
}
}
Da notare che la funzione Input.GetMouseButton(0)
rileva anche il touch su cellulare, ma solo il primo tocco che viene effettuato (ovvero, non funziona se sullo schermo è posizionato più di un dito).
Posizione del mouse
Come visto, rilevare la pressione di un tasto del mouse è abbastanza semplice. Come possiamo però sapere “dove” l’utente sta cliccando? I metodi sono diversi, vediamone i due principali.
Innanzi tutto, per sapere dove l’utente ha cliccato, abbiamo la posizione del mouse in pixel, in un Vector2
leggibile dalla variabile Input.mousePosition. Poiché non sappiamo a priori quale sarà la risoluzione dello schermo dell’utente, non possiamo operare sui pixel (un utente che clicca al centro potrebbe star cliccando a 400px su un netbook, ma anche a 910px in un full HD).
Per questo motivo, è necessario convertire la posizione del mouse in un raggio
che viene sparato dalla camera nella direzione che corrisponde al punto
cliccato sullo schermo. Possiamo fare tutto ciò con la funzione ScreenPointToRay, a cui dobbiamo solo passare le coordinate del mouse. In seguito, come visto in una lezione precedente, mediante Physics.Raycast
possiamo scoprire se il raggio tocca un Collider nella scena, il che
vuol dire che il mouse ha cliccato effettivamente su qualcosa. Va da sé
che qualunque oggetto cliccabile dovrà essere fornito di un Collider
attivo della dimensione necessaria.
Facciamo tutto ciò in congiunzione con l’uso del tasto sinistro del mouse come tasto “di fuoco”:
void Update()
{
if(Input.GetButtonDown(0))
{
Ray mouseRay = Camera.main.ScreenPointToRay(Input.mousePosition);
if(Physics.Raycast(mouseRay))
{
// Qualcosa è stato colpito!
}
}
}
In questo esempio, appena il tasto sinistro viene premuto viene creato un raggio (mouseRay
) a partire dalla camera principale della scena (Camera.main
), e se questo colpisce qualcosa possiamo impostare il risultato del click nell’if
.
Se dovessimo aver bisogno di oggetti con un Collider ma non cliccabili, basterà spostarli su un layer differente e poi fare il controllo sul raggio mascherando il raycast con una maschera di livello, come visto nella lezione sul raycasting.
Input touch e multitouch
In modo molto simile a come viene rilevato l’input del mouse, possiamo leggere l’input di uno schermo touch screen. Inoltre, la lettura del touch screen viene facilitata in Unity perché indipendentemente dalla piattaforma (che sia iOS o Android) Unity fornisce un array di elementi Touch (ovvero Input.touches) che corrisponde a tutti i tocchi attivi in un certo momento, quindi possiamo trattare i tocchi allo stesso modo indipendentemente dal device.
Ognuno di questi elementi Touch possiede molte informazioni utili: la
posizione a schermo, la fase del tocco (iniziato, mantenuto, terminato,
molto simili a GetKeyDown
, GetKey
e GetKeyUp
), ed il numero del dito che sta producendo il tocco.
Vediamo un esempio:
foreach(Touch t in Input.touches)
{
switch(t.phase)
{
case TouchPhase.Began:
Debug.Log ("Nuovo tocco: " + t.fingerId + " pos: " + t.position.x + " " + t.position.y);
break;
case TouchPhase.Moved:
case TouchPhase.Stationary:
Debug.Log ("Tocco continuato: " + t.fingerId + " pos: " + t.position.x + " " + t.position.y);
break;
case TouchPhase.Ended:
case TouchPhase.Canceled:
Debug.Log ("Tocco terminato: " + t.fingerId + " pos: " + t.position.x + " " + t.position.y);
break;
}
}
In questo esempio, scorriamo l’array dei tocchi in un dato momento (ammesso che ce ne siano!) tramite un ciclo foreach e stampiamo a schermo le coordinate di tutti i tocchi presenti sul touch screen, distinguendoli (mendiante uno switch) in base alla fase in cui si trova il tocco (t.phase
).
Lavorare con il multitouch
È importante notare come il device, ovviamente, non ha una percezione
di quale dito sta toccando lo schermo. Se ad esempio premiamo lo
schermo con l’indice, questo tocco sarà il primo dell’array Input.touches
, ed avrà fingerId
0. Se premiamo anche con il medio, questo sarà il secondo tocco nell’array Input.touches, ed avrà fingerId
1.
Se ora solleviamo l’indice, il tocco che corrisponde al medio, essendo l’unico rimasto, sarà il primo dell’array (Input.touches[0]
). Per riconoscere che è sempre lo stesso, torna utile la variabile fingerId, che avrà ancora valore 1.
Allo stesso modo, premendo con tre dita nell’ordine indice, medio ed
anulare, avremo un array di tre elementi. Se solleviamo l’indice ed il
medio, e poi successivamente ripremiamo con l’indice, avremo un array di
due elementi (che nella realtà sono medio ed anulare) in cui non per
forza Input.touches[0]
sarà il medio e Input.touches[1]
l’anulare, quindi dobbiamo basarci sul fingerId per sapere quale tocco
corrisponde a quale dito.
In definitiva, lavorare con il multitouch presenta una serie di difficoltà relative alla gestione dei singoli tocchi, che richiede una considerevole quantità di codice.
Unity Remote: testare le caratteristiche mobile su desktop
Poiché il tocco viene registrato solo su mobile ma lì non abbiamo modo di usare Debug.Log, in mancanza di un computer che rilevi il tocco (come un tablet Surface), per poter testare i tocchi direttamente nell’editor l’unico modo è ricorrere a Unity Remote.
Unity Remote è una piccola app per iOS o Android, che permette di collegare un device (che sia un telefono o un tablet) ad una istanza di Unity in esecuzione sul computer tramite wi-fi, ed inviare al gioco input dal touch screen o dall’accelerometro. Unity a sua volta invia al device un video a bassa risoluzione del gameplay, in modo da poter effettuare l’input in maniera informata.
L’app è gratuita e scaricabile dagli store (App Store o Google Play) ed è uno strumento fondamentale per lo sviluppo di app gioco su questi device. L’alternativa sarebbe creare una build ogni volta che facciamo una piccola modifica, un’operazione lunga e troppo costosa nel processo di sviluppo.
In definitiva però, prima di pubblicare un gioco bisogna sempre testarlo sul device di riferimento, anche perché l’input di Unity Remote arriva con molto ritardo al computer, quindi sul device si potrebbero avere risultati leggermente diversi.
Rilevare i movimenti dell’accelerometro
Così come per il touch, anche per l’accelerometro Unity fornisce un’interfaccia unificata indipendentemente dal device: Input.acceleration, che consiste in un Vector3 che corrisponde all’accelerazione gravitazionale del device. Purtroppo, l’orientamento di questo vettore può variare di device in device, quindi a volte sarà necessario procedere per esperimenti e scoprire come è orientato.
Ad esempio poggiando il device su un tavolo, se la componente z di questo vettore assume un valore positivo mentre le altre due componenti valgono 0, vuol dire che il vettore è orientato con Z verso lo schermo, Y verso l’alto, e X verso il lato destro del telefono.
L’orientamento degli assi va però testato device per device e, in caso di differenze, sarà necessario inserire nel codice un controllo sul tipo di device utilizzato, e riordinare gli assi.
Facendo delle prove ci si renderà conto che l’accelerometro restituisce valori molto sporchi, dove i numeri non sono precisi ma tendono ad oscillare anche se il device viene tenuto su una superficie stabile. Questo perché l’accelerometro per sua natura è impreciso, ed inoltre risente di tutte le forze magnetiche anche minime nell’area circostante. Per questo motivo spesso l’accelerometro non viene usato per controllare personaggi con precisione, ma solo come misuratore degli scossoni dati al telefono.
Ad esempio:
void Update()
{
Vector3 deviceAcceleration = Input.acceleration;
if(deviceAcceleration.magnitude > 10f)
{
Debug.Log("Il device è stato scosso forte");
}
}
Questo semplice codice legge il valore della magnitudine del vettore di accelerazione, per ricavare la forza con cui è stato scosso il device. Se questa supera 10 (un valore di esempio a caso), possiamo considerare che sia stato scosso forte.
Anche per l’accelerometro vale il discorso di Unity Remote di cui sopra: poiché non sarebbe possibile testare i valori del sensore se non facendo una build, questa app permette di testare rapidamente questo metodo di controllo durante lo sviluppo.