Codificați o aplicație de măsurare cu ARKit Interacțiune și măsurare

Împreună cu multe alte lucruri care au fost repede înlocuite de tehnologia noastră modernă, se pare că măsura comună de bandă poate fi următoarea. În această serie de instrucțiuni din două părți, învățăm cum să folosim realitatea augmentată și camera pe dispozitivul iOS pentru a crea o aplicație care va raporta distanța dintre două puncte.

În primul post, am creat proiectul de aplicație și am codificat elementele principale ale interfeței. În acest post, o vom termina prin măsurarea între două puncte din scenă AR. Dacă nu ați făcut-o încă, urmați împreună cu primul post pentru a vă crea proiectul ARKit.

Manevrarea robinetelor

Iată una dintre cele mai mari părți ale acestui tutorial: manipularea atunci când utilizatorul se deconectează de pe lumea lor pentru a obține o sferă pentru a apărea exact unde au apucat. Mai târziu, vom calcula distanța dintre aceste sfere pentru a afișa în final utilizatorului distanța dintre ele.

Apăsați pe Gesture Recognizer

Primul pas în verificarea robotului este crearea unui dispozitiv de recunoaștere a gestului la apăsare când apare aplicația. Pentru a face acest lucru, creați un handler de robinet după cum urmează:

// Creează un handler de robinet și apoi îl stabilește la o constantă să fie tapRecognizer = UITapGestureRecognizer (țintă: auto, acțiune: #selector (handleTap))

Prima linie creează o instanță a UITapGestureRecognizer () clasa și trece în doi parametri la inițializare: ținta și acțiunea. Ținta este destinatarul notificărilor pe care acest recunoscător le trimite și dorim ca noi ViewController clasa a fi țintă. Acțiunea este pur și simplu o metodă care trebuie apelată de fiecare dată când există o atingere.

Pentru a seta numărul de robinete, adăugați aceasta:

// Setează cantitatea de robineți necesare pentru declanșarea handler-ului tapRecognizer.numberOfTapsRequired = 1

Apoi, instanța clasei pe care am creat-o mai devreme trebuie să știe câte robineți sunt de fapt necesare pentru a activa recunoașterea. În cazul nostru, avem nevoie doar de o atingere, dar în alte aplicații, poate fi necesar să aveți mai multe (cum ar fi o atingere dublă) pentru unele cazuri.

Adăugați dispozitivul de manipulare la vizualizarea de scenă astfel:

// adaugă handler-ul la vizualizarea scenelor sceneView.addGestureRecognizer (tapRecognizer)

În cele din urmă, această singură linie de cod doar adaugă recunoașterea gestului la sceneView, unde vom face totul. Acesta este locul în care previzualizarea camerei va fi la fel de bine ca și ceea ce utilizatorul va atinge direct pentru a obține o sferă care să apară pe ecran, deci este logic să adăugați recunoașterea la vizualizarea cu care utilizatorul va interacționa.

Trageți metoda de atingere

Când am creat UITapGestureRecognizer (), vă puteți aminti că am stabilit o handleTap metodă pentru acțiune. Acum, suntem gata să declarăm această metodă. Pentru aceasta, trebuie doar să adăugați următoarele în aplicația dvs.:

@objc func handleTap (expeditor: UITapGestureRecognizer) // Codul dvs. merge aici 

Deși declarația funcției poate fi destul de auto-explicativă, vă puteți întreba de ce există @objc etichetă în fața acestuia. Din versiunea curentă a lui Swift, pentru a expune metodele la Obiectiv-C, aveți nevoie de această etichetă. Tot ce trebuie să știți este asta #selector este necesar ca metoda menționată să fie disponibilă pentru obiectivul C. În cele din urmă, parametrul metodei ne va permite să obținem locația exactă care a fost atinsă pe ecran.

Detectarea locației

Următorul pas în obținerea sferelor noastre să apară în cazul în care utilizatorul a fost apucat să detecteze poziția exactă pe care au accesat-o. Acum, acest lucru nu este la fel de simplu ca obținerea locației și plasarea unei sfere, dar sunt sigur că o veți stăpâni în cel mai scurt timp. 

Începeți prin adăugarea următoarelor trei linii de cod la dvs. handleTap () metodă:

// Obține locația robinetului și îl atribuie unei locații constante, lasă loc = sender.location (în: sceneView) // Căutări pentru obiecte din lumea reală, cum ar fi suprafețele și filtrează suprafețe plane permiteți hitTest = sceneView.hitTest (locație, tipuri : [ARHitTestResult.ResultType.featurePoint]) // Alocă rezultatul cel mai precis la o constantă dacă este non-nul pază let rezultat = hitTest.last altceva return

Dacă vă aduceți aminte de parametrul pe care l-am luat handleTap () , vă puteți aminti că a fost numit expeditor, și era de tip UITapGestureRecognizer. Ei bine, această primă linie de cod duce pur și simplu poziția robinetului pe ecran (în raport cu vizualizarea scenei) și o fixează pe o constanță numită Locație.

Apoi, facem ceva numit test de succes SceneView în sine. Ceea ce face acest lucru, în termeni simpli, este de a verifica scena pentru obiecte reale, cum ar fi mese, suprafețe, pereți, podele etc. Aceasta ne permite să avem un sentiment de adâncime și să obținem măsurători destul de precise între două puncte. În plus, specificăm tipurile de obiecte pe care le detectăm și, după cum puteți vedea, îi spunem să caute featurePoints, care sunt în esență suprafețe plate, ceea ce are sens pentru o aplicație de măsurare.

În cele din urmă, linia de cod are cel mai precis rezultat, care, în cazul hitTest este ultimul rezultat și verifică dacă nu este zero. Dacă este, ignoră restul liniilor din această metodă, dar dacă există într-adevăr un rezultat, va fi atribuită unei constante numite rezultat.

matrici

Dacă te gândești la clasa de algebră în liceu, ai putea să-ți amintești de matrice, care ar fi putut să nu părea la fel de importante ca atunci când sunt chiar acum. Acestea sunt utilizate în mod obișnuit în sarcinile legate de grafica computerizată, iar noi le vom vedea în această aplicație.

Adăugați următoarele linii la dvs. handleTap () și vom trece peste ele în detaliu:

// Convertește matrix_float4x4 la SCNMatrix4 pentru a fi utilizat cu SceneKit let transform = SCNMatrix4.init (result.worldTransform) // Creează un SCNVector3 cu anumiți indici în matrice let vector = SCNVector3Make (transform.m41, transform.m42, transform. m43) // face o nouă sferă cu metoda creată să lase sphere = newSphere (la: vector)

Înainte de a intra în prima linie de cod, este important să înțelegeți că testul de lovitură pe care l-am făcut mai devreme o returnează matrix_float4x4, care este, în esență, o matrice de valori flotante de patru până la patru. De când suntem noiSceneKit, totuși, va trebui să îl convertim în ceva pe care SceneKit îl poate înțelege - în acest caz, într-o SCNMatrix4.

Apoi, vom folosi această matrice pentru a crea o SCNVector3, care, după cum sugerează și numele său, este un vector cu trei componente. Așa cum probabil ați ghicit, acele componente sunt Xy, și z, să ne dea o poziție în spațiu. transform.m41transform.m42, și transform.m43 sunt valorile de coordonate relevante pentru vectorii cu trei componente.

În cele din urmă, hai să folosim newSphere () metodă pe care am creat-o mai devreme, împreună cu informațiile despre locație pe care le-am analizat din evenimentul touch, pentru a face o sferă și ao atribui unei constante numite sferă.

Rezolvarea bug-ului Double-Tap

Acum, ați realizat un ușor defect în codul nostru; dacă utilizatorul continuă să atingă, o nouă sferă ar continua să fie creată. Nu vrem acest lucru deoarece face dificilă determinarea sferelor care trebuie măsurate. De asemenea, este dificil pentru utilizator să țină evidența tuturor sferelor!

Rezolvarea cu matrice

Primul pas pentru a rezolva acest lucru este de a crea o matrice în partea de sus a clasei.

var sfere: [SCNNode] = []

Aceasta este o serie de SCNNodes pentru că asta e tipul pe care l-am întors de la noi newSphere () metodă pe care am creat-o spre începutul acestui tutorial. Mai târziu, vom pune sferele în această matrice și vom verifica câte sunt. Pe baza acestui fapt, vom putea să manipulăm numerele acestora, eliminându-le și adăugându-le.

Legarea opțională

Apoi, vom folosi o serie de declarații if-else și pentru bucle pentru a afla dacă există sfere în matrice sau nu. Pentru început, adăugați următoarea legare opțională la aplicația dvs.:

if let first = spheres.first // Codul merge aici altceva // Codul merge aici

În primul rând, verificăm dacă există orice articole în sfere array și, dacă nu, executați codul în altfel clauză.

Auditarea sferelor

După aceasta, adăugați următoarele în prima parte ( dacă ramură) de-al tăuafirmație:

// Adăugă o a doua sferă la sfera spheres.append (sferă) print (sphere.distance (la: first)) // Dacă mai mult de două sunt prezente ... if spheres.count> 2 // Iterați prin sfere matrice pentru sferă în sfere // Eliminați toate sferele sphere.removeFromParentNode () // Eliminați sferele sferice străine = [sfere [2]] 

Din moment ce suntem deja într-un eveniment de robinet, știm că noi creăm o altă sferă. Deci, dacă există deja o sferă, trebuie să luăm distanța și să o afișăm utilizatorului. Puteți apela distanţă() pe sferă, deoarece mai târziu, vom crea o extensie a SCNNode.

Apoi, trebuie să știm dacă există deja mai mult decât maximul a două sfere. Pentru a face acest lucru, folosim doar proprietatea de contorizare a noastră sfere array și an dacă afirmație. Repetăm ​​toate sferele din matrice și le eliminăm din scenă. (Nu vă faceți griji, o să revenim mai târziu.)

În cele din urmă, deoarece suntem deja în dacă afirmația care ne spune că există mai mult de două sfere, putem elimina a treia din matrice, pentru a ne asigura că doar două sunt lăsate în matrice în orice moment.

Adăugarea sferelor

În cele din urmă, în altfel clauza, știm că sfere matricea este goală, deci ceea ce trebuie să facem este să adăugăm doar sfera pe care am creat-o în momentul apelării metodei. În interiorul tău altfel clauza, adăugați aceasta:

// Adăugați sferele sfere.append (sferă)

Ura! Tocmai am adăugat sfera noastră sfere matrice, iar matricea noastră este gata pentru următorul robinet. Ne-am pregătit acum matricea noastră cu sferele care ar trebui să fie pe ecran, așa că acum, hai să le adăugăm în array.

Pentru a itera și a adăuga sferele, adăugați acest cod:

// Iterați prin sfere de sfere pentru sferă în sfere // Adăugați toate sferele în matricea self.sceneView.scene.rootNode.addChildNode (sfera)

Acesta este un simplu lucru pentru și adăugăm sferele (SCNNode) Ca un copil al nodului rădăcină al scenei. În SceneKit, acesta este modul preferat de a adăuga lucruri.

Metoda completă

Iată ce este ultimul handleTap () metoda ar trebui să arate ca:

@objc func handleTap (expeditor: UITapGestureRecognizer) permiteți locația = sender.location (în: sceneView) lăsați hitTest = sceneView.hitTest (locație, tipuri: [ARHitTestResult.ResultType.featurePoint]) guard leave result = hitTest.last else return  să transformăm = SCNMatrix4.init (result.worldTransform) let vector = SCNVector3Make (transform.m41, transform.m42, transform.m43) let sphere = newSphere (la: vector) if let first = spheres.first spheres.append sferă) sferă = sferă [2]] altfel spheres.append (sferă) sferă (sphere.distance (la: pentru sfera în sfere self.sceneView.scene.rootNode.addChildNode (sfera)

Calculul distanțelor

Acum, dacă vă veți aminti, am sunat a distanta (la :) metoda de pe noi SCNNode, sfera, și sunt sigur că Xcode strigă la tine pentru că folosește o metodă nedeclarată. Să terminăm acum, prin crearea unei extensii a SCNNode clasă.

Pentru a crea o extensie, faceți doar următoarele în afara dvs. ViewController clasă:

extensie SCNNode // Codul dvs. merge aici

Acest lucru vă permite pur și simplu să modificați clasa (este ca și cum ați fi editat clasa reală). Apoi, vom adăuga o metodă care va calcula distanța dintre două noduri.

Iată declarația funcției:

func distanță (la destinație: SCNNode) -> CGFloat // codul dvs. merge aici

Dacă veți vedea, există un parametru care este altul SCNNode, și se întoarce a CGFloat drept urmare. Pentru calculul real, adăugați acest lucru la dvs. distanţă() funcţie:

dx = destination.position.x - position.x permite dy = destinație.position.y - position.y permite dz = destinație.position.z - position.z permite inci: Float = 39.3701 let metri = sqrt (dx * dx + dy * dy + dz * dz) retur CGFloat (metri * inci)

Primele trei linii de coduri scad pozițiile x, y și z ale curentului SCNNode din coordonatele nodului trecut ca parametru. Mai târziu, vom conecta aceste valori în formula distanței pentru a le distanța. De asemenea, pentru că vreau rezultatul în centimetri, am creat o constantă pentru rata de conversie între metri și inci pentru o conversie ușoară mai târziu. 

Acum, pentru a obține distanța dintre cele două noduri, gândiți-vă la clasa de matematică a școlii de mijloc: puteți aminti formula de distanță pentru planul cartezian. Aici, îl aplicăm punctelor din spațiul tridimensional.

În cele din urmă, vom întoarce valoarea înmulțită cu rata de conversie inci pentru a obține unitatea de măsură adecvată. Dacă locuiți în afara Statelor Unite, puteți să o lăsați în metri sau să o convertiți în centimetri dacă doriți.

Concluzie

Ei bine, asta e o înfășurare! Iată cum ar trebui să arate proiectul dvs. final:

După cum puteți vedea, măsurătorile nu sunt perfecte, dar se crede că un computer de 15 inci este în jur de 14,998 țoli, deci nu este rău!

Acum știți cum să măsurați distanțele folosind noua bibliotecă a Apple, ARKit. Această aplicație poate fi utilizată pentru multe lucruri și vă provoc să vă gândiți la diferite moduri în care acest lucru poate fi folosit în lumea reală și asigurați-vă că vă lăsați gândurile în comentariile de mai jos.

De asemenea, asigurați-vă că verificați replica GitHub pentru această aplicație. Și în timp ce sunteți încă aici, verificați celelalte tutoriale de dezvoltare iOS de aici pe Envato Tuts+!

Cod