ARKit, SceneKit и как да контролираме света

В част 1 от тази серия преминахме през работен процес, където обработихме 3D модел, създадохме AR проект в Xcode, стартирахме AR сесия и поставихме модела си в нашата разширена сцена.

В тази публикация ще започнем да прилагаме модела си в действие, използвайки различни методи на SceneKit, и ще започнем да взаимодействаме с обектите в нашия свят.

Проектът за тази публикация можете да намерите на https://github.com/AbovegroundDan/ARTutorial_Part2

Действия на SceneKit

SceneKit предоставя набор от действия, които могат да бъдат приложени към възел. Тези действия могат да се използват за анимиране на движение, въртене, мащабиране и други свойства на възлите. Те могат да бъдат групирани, за да работят едновременно, да бъдат последователни, за да се изпълняват едно след друго, и да се повтарят или да се обърнат. Пълният списък можете да намерите на https://developer.apple.com/documentation/scenekit/scnaction

Ще продължим да модифицираме текущия проект и да започнем да добавяме някои действия към нашия обект. Нека започнем с добавяне на ротация в нашата сфера.

След метода addSphere в нашия HoverScene, добавете следния метод:

В този код създаваме действие, което описва завъртане на 180 градуса около оста Y. Това завъртане трябва да отнеме 5 секунди. След това предприемаме това действие и го добавяме към възела, предаван. Сега, трябва да извикаме този метод от нашия метод addSphere. В края на метода след реда, в който добавяме дочерния възел, добавете следния ред:

Сега, ако стартираме този код, ще видим сферата ни да се върти към другия път и след това да спре. Това, което искаме да направим сега, е да направим тази анимация да се повтори, така че тя продължава да се върти.

Нека променим нашия код на AddAnimation на следното:

Тук добавихме действие за повтаряне завинаги, което приема входното действие като вход. След това вместо ротационното действие добавяме действието repeForever към нашия възел. Пуснете кода отново и ще видим, че нашата сфера сега непрекъснато се върти.

Нека добавим малко пица в сферата. Нека също така го накараме да завие малко нагоре и надолу. Ще добавим действие за задържане, действие за задържане надолу, последователност на тези две действия и след това ги групираме със съществуващото действие на въртене. Ето как трябва да изглежда методът на addAnimation:

Сега имаме въртяща се, висяща сфера, която можем да поставим навсякъде в нашия свят.

HitTests и абстракции

Нека добавим малко интерактивност към сцената. Да си поставим цел да стартираме сцената, като накараме потребителя да постави сферите в света и след това докоснете, за да ги активирате. Тъй като ставаме малко по-сложни с нашия код, е време да започнем да абстрахираме начина, по който се справяме с обектите на нашата сцена. Създайте нова група в нашия проект, наречена Обекти. В тази папка ще създадем нов файл Swift, наречен SceneObject.swift.

Ще създадем базов клас, SceneObject, който произлиза от SCNNode.

Искаме да абстрахираме зареждането на обекта от останалата част от кода, затова даваме възможност да създадем метод init (), който приема име на файл. В този инициализатор ще преместим кода, който имаме за зареждане от файл.

Сега можем да създадем клас Sphere, който произлиза от SceneObject:

Сега имаме клас обект Sphere, който може да се зарежда. Тъй като ще анимираме сферите, когато докоснем тях, нека премахнем призива addAnimation от метода addSphere в нашия HoverScene. Също така, тъй като сега сме преместили целия код за зареждане в класа Sphere, можем просто да инициализираме Sphere и да го добавим директно към коренния възел на сцената. Нашият значително опростен метод сега изглежда така:

Много по-чисто!

Сега ще разгледаме как можем да направим хитов тест. Вече имаме разпознаващ жест за докосване в контролера ни за изглед, така че можем да се закачим за това, но как ще разберем дали нашите кранове удрят сфера, друг обект или изобщо нищо?

За щастие, ARSCNView може да ни помогне за това. Той има следния метод:

Можем да му подадем място в изгледа и той ще ни върне масив от възли, които са под тази точка, без значение каква е z-стойността или дълбочината.

Тъй като ние искаме да вземем само Sphere обекти, нека да създадем бърз филтър, който проверява дали всеки възел, върнат в hitTest, е сфера. За целта трябва да вземем най-горния родителски възел за всеки възел, който искаме да проверим. Да се ​​върнем към нашия Node + Extensions.swift файл и да добавим следния метод:

Тъй като всички обекти в нашата сцена са дете на кореновия възел на сцената, ние искаме да спрем, когато достигнем този възел, и да не проверяваме повече. Тъй като неговият рекурсивен метод спираме и се връщаме, когато открием, че коренният възел е родител на нашия възел.

С това зад нас, да се върнем към нашия ViewController и да модифицираме делегата на разпознаващия жест на натискане. Искаме да продължим да добавяме нови сфери, ако докоснем празно пространство, но ако докоснем съществуваща сфера, искаме да стартираме нейната анимация.

Когато получим крана, искаме да намерим точката в нашия изглед на сцената, да я предадем на метода на HitTest на изгледа на сцената и да видим какво ще се върнем. Тъй като ние искаме да се справим само с един обект наведнъж, ние хващаме първия (ако има попадения), след това използваме нашето разширение topmost (), за да вземем най-горния родител и проверим дали неговата сфера. Ако е, тогава добавяме нашата анимация към нея. Ако не получихме хитове от нашия тест, тогава правим както преди, добавяйки нова сфера пред камерата.

Продължете и стартирайте приложението отново. Можем да докоснем, за да добавим сфера, а след това докоснете всяка сфера, за да започнете да се движи. Имаме обаче грешка, която въведохме. Можем да продължим да докосваме сфера и анимациите ще продължават да се добавят към обекта всеки път. Можете да го тествате и да видите сферата да се върти по-бързо и да се движи нагоре и надолу по-бързо всеки път, когато я докоснете.

Тъй като анимацията е специфична за сферата, нека да преместим кода на addAnimation в самата сфера и да го преименуваме на просто анимиране (). Вместо node.addAnimation можем просто да наречем addAnimation. Ще добавим също флаг към класа Sphere, който ще проверим, преди да добавим анимацията, и да я настроим на истината при първото й добавяне:

Остава само да променим кода в обратния ни разговор с жест, за да стартираме това ново обаждане в самата сфера.

Всички сме готови с тази част сега. Имаме сфера, която можем да поставим в света, добавихме малко взаимодействие, за да можем да стартираме анимацията и малко да изчистим кода.

Допълнителен кредит

Начинът, по който поставяме сфера в света, е доста рязък. Докосваме се и изведнъж е там. Нека добавим малко пица към тази функция и да анимираме сферата, когато я поставим.

В нашия метод addSphere в HoverScene, нека добавим ефект на мащаб. Когато добавим сферата, ние ще анимираме нейния мащаб и вместо да използваме стандартен линеен мащаб, ще поставим ефект отскачане или ще се появи.

Нека променим нашия метод addSphere на следния и добавете във функцията easyOutElastic за определяне на времето, която ще ни осигури този отскок:

Сега, когато докосваме да поставим сфера, получаваме доста готин анимиран ефект.

Допълнителен кредит, част deux

Правихме много неща със SceneKit, но само пасирахме повърхността на някои от нещата, които ARKit може да направи. Като бърз закачка, преди да стигнем до повече функционалност на ARKit, нека добавим малко забавление към сцената, като направим сферата да "гледа" камерата. Вече имаме куки в метода updateAtTime на рендера и имаме препратка към камерата също. Така че нека започнем с добавяне на метод към класа Sphere, за да го насочим към определена посока. „Окото“ на сферата вече е изправено пред отрицателно Z, което е посоката на обекта напред. Това, което ще направим, е да създадем метод, който взема вектор, маркиращ точка в пространството, с което нашето „око“ ще бъде изправено.

В този код разглеждаме разстоянието между сферата и целевата позиция (камера). Ако е по-малко от някаква сума, ще анимираме окото да се изправи срещу целта. Ако окото беше обърнато към камерата и потребителят се отдалечи по-далеч от това зададено разстояние, тогава ще преминем в нашата „патрулна“ анимация. Едно нещо, което трябва да се отбележи в този код е, че тъй като няма удобна SCNAction за прилагане на анимация „LookAt“, ние обгръщаме погледа си (при :) обаждане в SCNTransaction, което ни позволява да анимираме движението. Специализирани 3D игри за двигатели като Unity или Unreal имат удобни функции за тези неща, но SceneKit все още не е на това ниво.

Може да забележите, че има призовано разстояние от целевияPOS SCNVector3, който е предаден, но този метод не съществува за SCNVector3. Това, което ще направим, е да добавим ново разширение за този разговор от разстояние.

Поставям този код в нов файл UtilityExtensions.swift, но не се колебайте да го поставите, където искате.

След това ще трябва да променим нашия метод updateAtTime, за да премахнем проверката на флага и да извикаме метод в нашия контролер на сцени всеки кадър. Нашият контролер на сцена ще отговаря за изпращането на съобщението до всички обекти на сферата в нашата сцена.

В нашия HoverScene ще създадем метода makeUpdateCameraPos, който ще филтрира само в Sphere обекти и ще извикаме метода patrol.

Нека да променим и нашия метод на поставяне, за да поставим сферите малко по-далеч. Нека накараме метода didTapScreen да постави нашата сфера на 5 метра вместо 1:

В нашия клас Sphere, нека направим нашия праг, за да задейства погледа на очите до 4,85 метра:

Нека също променим нашата анимация Sphere, така че да се оглежда малко и да не се задържи.

Float.random е друго разширение, което е просто:

Пуснете това и пуснете някои сфери. Те трябва да анимират самостоятелно сега, без да е необходимо да ги докосвате. Вървете се по-близо до всеки един от тях и трябва да ги видите да насочат погледа си към вас. Вървете по-далеч и те трябва да се върнат към патрулирането на района си. Сега имаме сцена направо от дистопично бъдеще, с подвижни очи наблюдават всяко наше движение.

Следете за повече забавление от ARKit!