Insieme a molte altre cose che sono state rapidamente sostituite dalla nostra tecnologia moderna, sembra che il metro a nastro comune possa essere il prossimo ad andare. In questa serie di tutorial in due parti, stiamo imparando come utilizzare la realtà aumentata e la fotocamera sul tuo dispositivo iOS per creare un'app che riporti la distanza tra due punti.
Nel primo post abbiamo creato il progetto dell'app e abbiamo codificato i suoi elementi di interfaccia principali. In questo post, lo completeremo misurando tra due punti nella scena AR. Se non lo hai ancora fatto, segui il primo post per ottenere il tuo progetto ARKit.
Ecco una delle parti più importanti di questo tutorial: la gestione quando l'utente tocca il loro mondo per far apparire una sfera esattamente dove ha toccato. Più tardi, calcoleremo la distanza tra queste sfere per mostrare finalmente all'utente la loro distanza.
Il primo passaggio nel controllo dei tocchi consiste nel creare un riconoscitore del gesto di tocco all'avvio dell'app. Per fare questo, crea un gestore di tap come segue:
// Crea un gestore tap e quindi lo imposta su una costante, lasciando tapRecognizer = UITapGestureRecognizer (target: self, action: #selector (handleTap))
La prima riga crea un'istanza di UITapGestureRecognizer ()
classe e passa in due parametri all'inizializzazione: il bersaglio e l'azione. L'obiettivo è il destinatario delle notifiche inviate da questo strumento di riconoscimento e noi le vogliamo ViewController
classe per essere l'obiettivo. L'azione è semplicemente un metodo che dovrebbe essere chiamato ogni volta che c'è un tocco.
Per impostare il numero di tocchi, aggiungere questo:
// Imposta la quantità di tap necessari per attivare il gestore tapRecognizer.numberOfTapsRequired = 1
Successivamente, l'istanza della classe che abbiamo creato in precedenza deve sapere quanti tap sono effettivamente necessari per attivare il riconoscimento. Nel nostro caso, abbiamo solo bisogno di un tocco, ma in altre app, potrebbe essere necessario avere più (ad esempio un doppio tocco) per alcuni casi.
Aggiungi il gestore alla vista scena in questo modo:
// Aggiunge il gestore alla vista scena sceneView.addGestureRecognizer (tapRecognizer)
Infine, questa singola riga di codice aggiunge semplicemente il riconoscitore di gesti al sceneView
, che è dove faremo tutto. Qui è dove sarà l'anteprima della fotocamera e ciò che l'utente direttamente toccherà per far apparire una sfera sullo schermo, quindi ha senso aggiungere il riconoscitore alla vista con cui l'utente interagirà.
Quando abbiamo creato il UITapGestureRecognizer ()
, potresti ricordare che abbiamo impostato a handleTap
metodo per l'azione. Ora, siamo pronti a dichiarare questo metodo. Per fare ciò, aggiungi semplicemente quanto segue alla tua app:
@objc func handleTap (mittente: UITapGestureRecognizer) // Il tuo codice va qui
Sebbene la dichiarazione di funzione possa essere abbastanza auto-esplicativa, ci si potrebbe chiedere perché ci sia un @objc
taggagli di fronte. A partire dalla versione corrente di Swift, per esporre i metodi a Objective-C, è necessario questo tag. Tutto ciò che devi sapere è questo #selettore
ha bisogno che il metodo indicato sia disponibile per l'obiettivo-C. Infine, il parametro method ci permetterà di ottenere la posizione esatta che è stata toccata sullo schermo.
Il prossimo passo per far apparire le nostre sfere dove l'utente ha toccato è rilevare la posizione esatta che hanno sfruttato. Ora, questo non è semplice come ottenere la posizione e posizionare una sfera, ma sono sicuro che lo padronerai in pochissimo tempo.
Inizia aggiungendo le seguenti tre righe di codice al tuo handleTap ()
metodo:
// Ottiene la posizione del rubinetto e lo assegna a una costante let location = sender.location (in: sceneView) // Cerca oggetti reali come superfici e filtri su superfici piane lascia hitTest = sceneView.hitTest (posizione, tipi : [ARHitTestResult.ResultType.featurePoint]) // Assegna il risultato più accurato a una costante se è non-nil guardia lascia risultato = hitTest.last else return
Se ricordi il parametro che abbiamo preso nel handleTap ()
metodo, è possibile ricordare che è stato nominato mittente
, ed era di tipo UITapGestureRecognizer
. Bene, questa prima riga di codice prende semplicemente la posizione del rubinetto sullo schermo (relativamente alla vista scena) e la imposta su una costante chiamata Posizione
.
Successivamente, stiamo facendo qualcosa chiamato hit test sul SceneView
si. In termini semplici, ciò che fa è controllare la scena per oggetti reali, come tavoli, superfici, muri, pavimenti, ecc. Questo ci consente di ottenere un senso di profondità e di ottenere misurazioni accurate tra due punti. Inoltre, stiamo specificando i tipi di oggetti da rilevare e, come puoi vedere, gli stiamo dicendo di cercare featurePoints
, che sono essenzialmente superfici piatte, il che ha senso per un'app di misurazione.
Infine, la riga di codice prende il risultato più accurato, che nel caso di hitTest
è l'ultimo risultato e controlla se non lo è zero
. Se lo è, ignora il resto delle righe in questo metodo, ma se c'è davvero un risultato, verrà assegnato ad una costante chiamata risultato
.
Se ripensi alla tua lezione di algebra del liceo, potresti ricordare le matrici, che all'epoca non erano così importanti come allora. Sono comunemente usati nelle attività relative alla grafica del computer e li vedremo in questa app.
Aggiungi le seguenti linee al tuo handleTap ()
metodo, e li esamineremo in dettaglio:
// Converte matrix_float4x4 in una SCNMatrix4 da utilizzare con SceneKit let transform = SCNMatrix4.init (result.worldTransform) // Crea un SCNVector3 con alcuni indici nella matrice let vector = SCNVector3Make (transform.m41, transform.m42, transform. m43) // Crea una nuova sfera con il metodo creato let sphere = newSphere (at: vector)
Prima di entrare nella prima riga di codice, è importante capire che l'hit test che abbiamo fatto prima restituisce un tipo di matrix_float4x4
, che è essenzialmente una matrice quattro per quattro di valori float. Dal momento che siamo dentroSceneKit, però, avremo bisogno di convertirlo in qualcosa che SceneKit può capire - in questo caso, in a SCNMatrix4
.
Quindi, useremo questa matrice per creare un SCNVector3
, che, come suggerisce il nome, è un vettore con tre componenti. Come avrai intuito, quei componenti sono X
, y
, e z
, per darci una posizione nello spazio. transform.m41
, transform.m42
, e transform.m43
sono i valori delle coordinate rilevanti per i vettori a tre componenti.
Infine, usiamo il newsphere ()
metodo che abbiamo creato in precedenza, insieme alle informazioni sulla posizione che abbiamo analizzato dall'evento touch, per creare una sfera e assegnarla a una costante chiamata sfera
.
Ora, potresti aver realizzato un leggero difetto nel nostro codice; se l'utente continua a toccare, una nuova sfera continua a essere creata. Non vogliamo questo perché rende difficile determinare quali sfere debbano essere misurate. Inoltre, è difficile per l'utente tenere traccia di tutte le sfere!
Il primo passo per risolverlo è creare una matrice nella parte superiore della classe.
var sphere: [SCNNode] = []
Questa è una matrice di SCNNodes
perché è il tipo da cui siamo tornati newsphere ()
metodo che abbiamo creato all'inizio di questo tutorial. In seguito, metteremo le sfere in questo array e controlleremo quanti ce ne sono. Sulla base di ciò, saremo in grado di manipolare i loro numeri rimuovendoli e aggiungendoli.
Successivamente, useremo una serie di istruzioni if-else e per i loop per capire se ci sono delle sfere nell'array o no. Per iniziare, aggiungi il seguente collegamento facoltativo alla tua app:
se preferisci first = spheres.first // Il tuo codice va qui else // Il tuo codice va qui
Per prima cosa, controlliamo se ci sono qualunque articoli in sfere
array, e se no, esegui il codice nel file altro
clausola.
Successivamente, aggiungi quanto segue alla prima parte (il Se
ramo) del tuo if-elsedichiarazione:
// Aggiunge una seconda sfera all'array spheres.append (sfera) print (sphere.distance (a: first)) // Se ne sono presenti più di due ... if spheres.count> 2 // Iterate attraverso l'array di sfere per la sfera in sfere // Rimuovi tutte le sfere sphere.removeFromParentNode () // Rimuovi sfere estranee sfere = [sfere [2]]
Dato che siamo già in un evento tap, sappiamo che stiamo creando un'altra sfera. Quindi, se esiste già una sfera, dobbiamo ottenere la distanza e mostrarla all'utente. Puoi chiamare il distanza()
metodo sulla sfera, perché in seguito creeremo un'estensione di SCNNode
.
Successivamente, abbiamo bisogno di sapere se ci sono già più del massimo di due sfere. Per fare questo, stiamo usando solo la proprietà count del nostro sfere
array e an Se
dichiarazione. Effettuiamo l'iterazione attraverso tutte le sfere dell'array e le rimuoviamo dalla scena. (Non ti preoccupare, ne riparliamo più tardi).
Finalmente, visto che siamo già nel Se
dichiarazione che ci dice che ci sono più di due sfere, possiamo rimuovere la terza nell'array in modo che ci assicuriamo che ne restino solo due nell'array in ogni momento.
Finalmente, nel altro
clausola, sappiamo che il sfere
array è vuoto, quindi quello che dobbiamo fare è aggiungere semplicemente la sfera che abbiamo creato al momento della chiamata al metodo. Dentro il tuo altro
clausola, aggiungere questo:
// Aggiungi la sfera sferica.append (sfera)
Sìì! Abbiamo appena aggiunto la sfera al nostro sfere
array, e il nostro array è pronto per il prossimo tocco. Ora abbiamo preparato il nostro array con le sfere che dovrebbero essere sullo schermo, quindi ora aggiungiamole all'array.
Per iterare e aggiungere le sfere, aggiungi questo codice:
// Iterate attraverso l'array di sfere per sfere in sfere // Aggiungi tutte le sfere dell'array self.sceneView.scene.rootNode.addChildNode (sphere)
Questo è solo un semplice per
loop, e stiamo aggiungendo le sfere (SCNNode
) Come figlio del nodo radice della scena. In SceneKit, questo è il modo preferito per aggiungere cose.
Ecco cosa è il finale handleTap ()
il metodo dovrebbe assomigliare a:
@objc func handleTap (mittente: UITapGestureRecognizer) let location = sender.location (in: sceneView) let hitTest = sceneView.hitTest (posizione, tipi: [ARHitTestResult.ResultType.featurePoint]) guardia let result = hitTest.last else return let transform = SCNMatrix4.init (result.worldTransform) let vector = SCNVector3Make (transform.m41, transform.m42, transform.m43) lascia sphere = newSphere (a: vector) se preferisci first = spheres.first spheres.append ( sfera) stampa (sphere.distance (to: first)) if spheres.count> 2 for sphere in spheres sphere.removeFromParentNode () spheres = [spheres [2]] else spheres.append (sphere) for sphere in spheres self.sceneView.scene.rootNode.addChildNode (sphere)
Ora, se ti ricorderai, abbiamo chiamato a distanza (a :)
metodo sul nostro SCNNode
, la sfera, e sono sicuro che Xcode ti sta urlando contro per aver usato un metodo non dichiarato. Finiamola ora, creando un'estensione del SCNNode
classe.
Per creare un'estensione, fai semplicemente quanto segue al di fuori del tuo ViewController
classe:
estensione SCNNode // Il tuo codice va qui
Questo ti consente semplicemente di modificare la classe (è come se stessimo modificando la classe effettiva). Quindi, aggiungeremo un metodo che calcolerà la distanza tra due nodi.
Ecco la dichiarazione della funzione per farlo:
func distance (to destination: SCNNode) -> CGFloat // Il tuo codice va qui
Se vedrai, c'è un parametro che è un altro SCNNode
, e restituisce a CGFloat
come risultato. Per il calcolo effettivo, aggiungi questo al tuo distanza()
funzione:
let dx = destination.position.x - position.x let dy = destination.position.y - position.y let dz = destination.position.z - position.z let inches: Float = 39.3701 let meters = sqrt (dx * dx + dy * dy + dz * dz) return CGFloat (metri * pollici)
Le prime tre righe di codice sottraggono le posizioni x, y e z della corrente SCNNode
dalle coordinate del nodo passato come parametro. In seguito inseriremo questi valori nella formula della distanza per ottenere la loro distanza. Inoltre, poiché voglio il risultato in pollici, ho creato una costante per il tasso di conversione tra metri e pollici per una facile conversione in seguito.
Ora, per ottenere la distanza tra i due nodi, ripensa alla classe di matematica della scuola media: potresti ricordare la formula della distanza per il piano cartesiano. Qui, lo applichiamo ai punti nello spazio tridimensionale.
Infine, restituiamo il valore moltiplicato per il rapporto di conversione in pollici per ottenere l'unità di misura appropriata. Se vivi fuori dagli Stati Uniti, puoi lasciarlo in metri o convertirlo in centimetri se lo desideri.
Bene, questo è un involucro! Ecco come dovrebbe essere il tuo progetto finale:
Come puoi vedere, le misure non sono perfette, ma pensa che un computer da 15 pollici sia di circa 14.998 pollici, quindi non è male!
Ora sai come misurare le distanze usando la nuova libreria di Apple, Arkit
. Questa app può essere utilizzata per molte cose, e ti sfido a pensare a modi diversi in cui ciò può essere usato nel mondo reale, e assicurati di lasciare i tuoi pensieri nei commenti qui sotto.
Inoltre, assicurati di controllare il repository GitHub per questa app. E mentre sei ancora qui, dai un'occhiata agli altri tutorial di sviluppo iOS qui su Envato Tuts+!