ARKit, SceneKit și Cum să controlăm lumea

În partea 1 a acestei serii, am trecut printr-un flux de lucru unde am procesat un model 3D, am creat un proiect AR în Xcode, am început o sesiune de AR și am pus modelul nostru în scena augmentată.

În această postare, vom începe să punem modelul în acțiune, folosind o varietate de metode SceneKit și vom începe să interacționăm cu obiectele din lumea noastră.

Proiectul pentru această postare poate fi găsit la https://github.com/AbovegroundDan/ARTutorial_Part2

Acțiuni SceneKit

SceneKit oferă un set de acțiuni care pot fi aplicate unui nod. Aceste acțiuni pot fi utilizate pentru a anima mișcarea, rotația, scalarea și alte proprietăți ale nodului. Acestea pot fi grupate pentru a rula în același timp, secvențiate pentru a rula unul după altul și repetate sau inversate. Lista completă poate fi găsită la https://developer.apple.com/documentation/scenekit/scnaction

Vom continua să modificăm proiectul curent și vom începe să adăugăm unele acțiuni obiectului nostru. Să începem prin adăugarea unei rotații la sfera noastră.

După metoda addSphere din HoverScene, adăugați următoarea metodă:

În acest cod, creăm o acțiune care descrie o rotație de 180 de grade în jurul axei Y. Această rotație trebuie să dureze 5 secunde. Apoi, luăm această acțiune și o adăugăm la nodul transmis. Acum, trebuie să apelăm la această metodă din metoda noastră addSphere. La sfârșitul metodei, după linia în care adăugăm nodul copil, adăugați următoarea linie:

Acum, dacă rulăm acest cod, vom vedea că sfera noastră se rotește spre fața invers și apoi se va opri. Ceea ce vrem să facem acum este să facem ca această animație să se repete, astfel încât să continue să se rotească.

Să schimbăm codul nostru de adăugare la următoarele:

Aici, am adăugat o acțiune repetată pentru totdeauna, care are acțiunea de rotație ca intrare. Apoi, în loc de acțiunea de rotație, adăugăm acțiunea repereForever la nodul nostru. Executați din nou codul și vom vedea că sfera noastră acum se rotește continuu.

Să adăugăm un pic de pizzazz în sferă. Să-l facem să plutească și în jos un pic. Vom adăuga o acțiune de încărcare, o acțiune de mers în jos, secvențează acele două acțiuni și apoi le vom grupa cu acțiunea noastră de rotire existentă. Așa ar trebui să arate metoda addAnimation:

Acum avem o sferă rotativă și plină de viteză pe care o putem așeza oriunde în lumea noastră.

HitTests și abstractizări

Să adăugăm o anumită interactivitate în scenă. Să stabilim un obiectiv pentru a începe scena prin faptul că utilizatorul plasează sferele din lume și apoi atingeți pentru a le activa. Întrucât devenim ceva mai complexe cu codul nostru, a venit timpul să începem să facem abstracție de modul în care ne ocupăm de obiectele noastre de scenă. Creați un grup nou în proiectul nostru numit Obiecte. În acest folder, vom crea un nou fișier Swift numit SceneObject.swift.

Vom crea o clasă de bază, SceneObject, care derivă din SCNNode.

Vrem să abstractizăm încărcarea obiectului din restul codului, deci permiteți crearea unei metode init () care să ia un nume de fișier. În acest inițializator, vom muta codul pe care îl avem pentru încărcare din fișier.

Acum putem crea o clasă Sphere care derivă din SceneObject:

Avem acum o clasă de obiecte Sferă care se poate încărca singur. Deoarece vom anima sferele când le atingem, eliminăm apelul addAnimation din metoda addSphere din HoverScene. De asemenea, deoarece acum am mutat tot codul de încărcare în clasa Sferă, putem doar inițializa Sphere și adăuga direct la nodul rădăcină al scenei. Metoda noastră simplificată mult arată acum:

Mult mai curat!

Acum, vom analiza cum putem face un test de succes. Avem deja un dispozitiv de recunoaștere a gesturilor de tap în controlerul nostru de vizualizare, astfel încât să ne putem conecta la asta, dar cum vom ști dacă robinetele noastre lovesc o Sferă, un alt obiect sau nimic deloc?

Din fericire, ARSCNView nostru ne poate ajuta în acest sens. Are următoarea metodă:

Îi putem alimenta o locație în vedere și ne va da înapoi o serie de noduri care se află sub acel punct, indiferent care este valoarea z sau adâncimea.

Întrucât dorim să apucăm doar obiecte Sferă, să creăm un filtru rapid care verifică dacă fiecare nod returnat în hitTest este o Sferă. Pentru a face acest lucru, trebuie să apucăm cel mai înalt nod părinte pentru fiecare nod pe care dorim să îl verificăm. Revenim la fișierul nostru Node + Extensions.swift și adăugăm următoarea metodă:

Deoarece toate obiectele din scena noastră sunt un copil al nodului rădăcină al scenei, vrem să ne oprim când ajungem la acel nod și să nu mai verificăm. Deoarece este o metodă recursivă, ne oprim și ne întoarcem când descoperim că nodul rădăcină este părintele nodului nostru.

Cu asta în spatele nostru, să ne întoarcem la ViewController și să modificăm delegatul nostru de recunoaștere a gesturilor. Vrem să adăugăm în continuare sfere noi dacă atingem un spațiu gol, dar dacă atingem o sferă existentă, vrem să începem animația.

Când obținem un accesoriu, dorim să obținem punctul în vizualizarea scenei, să trecem la metoda hitTest a vizualizării scenei și să vedem ce obținem înapoi. Întrucât nu dorim să ne ocupăm de un singur obiect simultan, îl apucăm pe primul (dacă există accesări), apoi folosim extensia noastră (topmost) pentru a apuca cel mai de sus părinte și verificăm dacă este o sferă. Dacă este, atunci adăugăm animația noastră. Dacă nu am obținut rezultate din testul nostru, atunci facem ca înainte, adăugând o nouă Sferă în fața camerei.

Continuați și rulați aplicația din nou. Putem atinge pentru a adăuga o sferă, apoi atingeți orice sferă pentru a începe să se miște. Cu toate acestea, avem o eroare pe care am introdus-o. Putem continua să atingem o sferă și animațiile vor continua să fie adăugate la obiect de fiecare dată. Îl poți testa și vezi sfera care se învârte mai repede și se mișcă în sus și în jos mai repede de fiecare dată când o atingi.

Întrucât animația este specifică sferei, să mutăm codul addAnimation în Sfera propriu-zisă și redenumim-o pentru a anima doar (). În loc de node.addAnimation, putem apela doar addAnimation. De asemenea, vom adăuga un steag la clasa Sferă, pe care o vom verifica înainte de a adăuga animația și o vom seta pe adevărată prima dată când a fost adăugată:

Nu ne mai rămâne decât să schimbăm codul din apelul nostru de gestiune pentru a rula acest nou apel pe sfera în sine.

Cu toții ne-am setat cu această parte acum. Avem o sferă pe care o putem plasa în lume, am adăugat o oarecare interacțiune, astfel încât să putem începe animația și să curățăm puțin codul.

Credit suplimentar

Modul în care plasăm o sferă în lume este destul de brusc. Atingem, și deodată este acolo. Să adăugăm un pic de pizza la această funcție și să animăm sfera când o plasăm.

În metoda noastră addSphere din HoverScene, adăugăm un efect de scară. Când adăugăm sfera, vom anima scala ei și, în loc să folosim o scară liniară standard, vom pune un efect de respingere sau pop.

Să ne schimbăm metoda addSphere la următoarele și să adăugăm funcția de cronometrare easyOutElastic, care ne va oferi acel rebot:

Acum, când atingem pentru a plasa o sferă, obținem un efect animat destul de fain.

Credit suplimentar, partea a doua

Am făcut multe lucruri cu SceneKit, dar am pășit doar suprafața unor lucruri pe care ARKit le poate face. Ca rapid teaser înainte de a ajunge la mai multe funcționalități ARKit, să adăugăm un pic de distracție la scenă, făcând sfera „să privească” camera. Avem deja cârliguri la metoda de actualizare a distribuitorului, la ora actuală, și avem o trimitere și la camera de acolo. Așadar, să începem prin adăugarea unei metode la clasa Sferă, pentru ca aceasta să fie orientată spre o anumită direcție. „Ochiul” sferei se confruntă deja cu Z negativ, care este direcția înainte a obiectului. Ceea ce vom face este să creăm o metodă care să ia un vector care marchează un punct în spațiu cu care „ochiul” nostru se va confrunta.

În acest cod, privim distanța dintre sferă și poziția țintă (cameră). Dacă este mai mică decât o anumită sumă, vom anima ochiul pentru a înfrunta ținta. Dacă ochiul a fost orientat spre cameră, iar utilizatorul se îndepărtează mai mult decât această distanță setată, atunci vom merge în animația „patrulă”. Un lucru de reținut în acest cod este că, deoarece nu există o SCNAction utilă pentru aplicarea unei animații „LookAt”, ne înfășoară aspectul (la :) apel într-o SCNTransaction, care ne permite să animăm mișcarea. Motoarele de joc 3D dedicate, precum Unity sau Unreal, au funcții de comoditate pentru aceste lucruri, dar SceneKit nu este încă la acest nivel.

Este posibil să observați că există un apel la distanță pe SCNVector3 targetPos care a fost trecut, dar această metodă nu există pentru SCNVector3. Ceea ce vom face este să adăugăm o nouă extensie pentru acest apel la distanță.

Am introdus codul într-un nou fișier UtilityExtensions.swift, dar nu ezitați să îl puneți oriunde doriți.

În continuare, va trebui să ne schimbăm metoda updateAtTime pentru a elimina verificarea steagului și a apela la fiecare cadru o metodă din controlorul scenei. Controlorul nostru de scenă va fi responsabil de expedierea mesajului către toate obiectele din Sfera noastră.

În HoverScene, vom crea metoda makeUpdateCameraPos, care va filtra doar obiectele Sphere și va numi metoda de patrulare.

Să schimbăm și metoda noastră de plasare pentru a pune sferele puțin mai departe. Să facem că metoda didTapScreen plasează sfera noastră la 5 metri distanță în loc de 1:

În clasa noastră Sferă, să facem pragul nostru pentru a declanșa privirea ochilor la 4,85 metri:

Să schimbăm, de asemenea, animația noastră Sferă, astfel încât să arate în jur și să nu se ridice.

Float.random este o altă extensie care este pur și simplu:

Dați o alergare și așezați câteva sfere. Ei ar trebui să se anime singuri acum, fără a fi nevoie să le atingeți. Apropiați-vă de fiecare și ar trebui să-i vedeți întorcându-și ochii spre voi. Mergeți mai departe și ar trebui să se întoarcă în patrularea zonei lor. Acum avem o scenă direct dintr-un viitor distopic, cu ochii înfiorători care ne urmăresc fiecare mișcare.

Rămâneți la curent pentru mai multe distracții ARKit!