SA1916 — Фокус-узлы и рекламные щиты
Содержание страницы
В предыдущей главе вы создали универсальную, многоразовую основу для всех ваших будущих AR-приложений на основе SceneKit. Приложение работает в нескольких основных состояниях и, в качестве дополнительного бонуса, оно также соответствует стандартному процессу адаптации благодаря представлению Apple AR Coaching Overlay. Это позволит вашим пользователям чувствовать себя как дома, когда они возьмут ваше приложение и будут играть с созданными вами AR-эффектами.
В этой главе вы продолжите добавлять дополнительные компоненты в многоразовую основу. Вы узнаете, как создать узел фокусировки и управлять им, чтобы пользователь знал, где будет размещаться контент. Вы также сможете создать весь опыт AR с некоторыми базовыми взаимодействиями.
Без лишних слов, вытяните эти пальцы, тресните их костяшками пальцев и давайте приступим к делу!
Примечание: Чтобы начать работу, вы можете либо продолжить свой собственный проект из предыдущей главы, либо загрузить проект starter из starter/ARPort.
Импорт 3D-ресурсов
В предыдущей главе вы узнали о каталоге активов SceneKit, который представляет собой папку, к которой может делиться вся ваша команда художников и разработчиков. Он полностью отделяет графический компонент вашего приложения от кода. Это позволяет вам и вашей команде объединять любые графические изменения и дополнения в приложение с минимальными нарушениями.
Ваш первый шаг к началу работы-добавить в проект готовый каталог активов.
Если ваш проект открыт в Xcode с одной стороны, а Finder-с другой, найдите art.scnassets внутри starter/resources.
Перетащите папку art.scnassets в Xcode, поместив ее прямо над Assets.xcassets.

Убедитесь, что в пункте назначения установлен флажок Копировать элементы, если это необходимо, а для Добавления в целевые объекты установлено значение ARPort. Выберите Готово, чтобы завершить процесс.

Отлично, вы успешно импортировали все 3D-активы, необходимые для завершения AR-опыта.
Узлы фокусировки
Приложение уже обнаруживает горизонтальные поверхности, поэтому теперь ваша цель-показать пользователю, куда именно он указывает. Вот тут-то и пригодится фокус-узел.
Что такое Фокус-узел?
Узел фокусировки-это цель, которая показывает положение в пространстве, на которое указывает пользователь в дополненной реальности.

Чтобы определить, где разместить узел фокусировки, используется ray casting. Луч выстреливает из точки фокусировки в центральном положении экрана в расширенное пространство. Приложение разместит узел фокусировки везде, где луч пересекается с ранее обнаруженной поверхностью.
Создание точки фокусировки
Прежде чем создать точку фокусировки, необходимо определить положение на экране, которое будет использоваться для приведения луча. Для этого конкретного приложения вы будете использовать центральную точку экрана.
Вам нужно будет создать свойство для удержания этой центральной позиции, поэтому начните с добавления следующего в раздел Свойств:
var focusPoint:CGPoint!
Затем добавьте следующую функцию в раздел Управление узлом фокуса:
func initFocusNode() { focusPoint = CGPoint(x: view.center.x, y: view.center.y + view.center.y * 0.1) }
Здесь вы инициализируете узел фокуса вместе со всем необходимым для управления им. Вы инициализируете точку фокусировки в центре экрана с небольшим 10%
смещением по оси y для более естественного ощущения. Луч будет стрелять из этого положения экрана.
Это достаточно просто, но что, если пользователь изменит ориентацию экрана? Вы будете решать эту проблему дальше.
Обработка изменений ориентации
Чтобы решить проблему, необходимо обновлять точку фокусировки каждый раз, когда пользователь меняет ориентацию экрана.
Добавьте следующую вспомогательную функцию в разделе Управление узлами фокуса:
@objc func orientationChanged() { focusPoint = CGPoint(x: view.center.x, y: view.center.y + view.center.y * 0.1) }
Это, по сути, просто корректирует точку фокусировки. Обратите внимание, что функция имеет @objc
атрибут. Это делает функцию доступной для NotificationCenter
, которая является частью среды выполнения Objective-C.
Теперь вам нужно вызвать вспомогательную функцию при изменении ориентации.
Добавьте следующий код в нижнюю частьinitFocusNode()
:
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.orientationChanged), name: UIDevice.orientationDidChangeNotification, object: nil)
Это уведомляет приложение каждый раз, когда ориентация меняется. Он также вызывает вспомогательную функцию, которая обновляет точку фокусировки до правильного положения на экране.
Создание узла фокусировки
Чтобы создать новый узел фокусировки, сначала необходимо создать новую сцену SceneKit.
Щелкните правой кнопкой мыши art.scnassets/Scenes и выберите Новый файл.

Это создает новую пустую сцену. Переименуйте его в FocusScene.scn.
Если файл FocusScene.scn все еще открыт, выберите и удалите узел камеры в графе сцены.

Выберите символ + в левом нижнем углу, чтобы добавить пустой узел, а затем назовите его Focus.

Добавьте новый объект из библиотеки объектов, затем выполните поиск Plane. Перетащите новую плоскость из библиотеки объектов в график сцены в качестве дочернего узла узла фокусировки.

Откройте art.scnassets/Textures/Focus, затем перетащите Footprint_DIFFUSE.png поверх плоскости следа в пределах сцены.

Выполните тот же процесс, что и раньше, и добавьте еще одну плоскость, называемую Тенью, также в качестве дочернего узла фокуса.
Выбрав узел «Тень», откройте инспектор атрибутов и задайте размер плоскости (ширина: 0,15, высота: 0,15).

Откройте инспектор узлов и установите положение преобразования равным (x:0, y:0.001, z:0) таким образом, тень находится немного выше Следаноги, предотвращая z-борьбу. Затем перетащите текстуру Shadow_DIFFUSE.png на нее.

Чтобы немного осветлить тень, откройте инспектор материалов и установите значение прозрачности настроек равным 0,75.

Снова выполните тот же процесс, что и раньше, и перетащите еще одну плоскость в качестве дочернего узла узла фокуса, на этот раз назвав ее значком.
Установите размер плоскости равным (ширина:0.1, высота: 0.1), а положение преобразования-равным (x:0, y:0.05, z:0).
Завершите его, назначив ему текстуру Focus_DIFFUSE.png. В разделе «Инспектор материалов» установите значение «Затенение свойств материала» Постоянным, что предотвращает его реакцию на свет.
Вот как будет выглядеть результат:

Добавление ограничений рекламного щита
Было бы очень круто, если бы узел фокуса всегда был обращен к пользователю. Для достижения этого эффекта можно использовать ограничение billboard. Рекламный щит — это плоский узел с текстурой на нем, который всегда будет обращен к камере.
Если файл focusNode.scn все еще открыт, выберите узел Focus и откройте инспектор узлов. Найдите раздел Ограничения и нажмите кнопку+, чтобы добавить ограничение. Выберите Billboard из списка.

Наконец, вы не хотите, чтобы ограничение billboard влияло на все оси, только на ось y.
В разделе Параметры ограничений Осиснимите флажки с осей x и z.

Отлично, теперь узел фокусировки всегда будет обращен в ту же сторону, что и пользователь.
Загрузка узла фокусировки
Теперь, когда фокус-сцена готова к работе, вам нужно загрузить ее и добавить в основную сцену.
Начните с создания переменной для хранения узла фокуса. Добавьте следующую переменную в раздел Свойства:
var focusNode: SCNNode!
Добавьте следующий код в началоinitFocusNode()
:
// 1 let focusScene = SCNScene( named: "art.scnassets/Scenes/FocusScene.scn")! // 2 focusNode = focusScene.rootNode.childNode( withName: "Focus", recursively: false)! // 3 focusNode.isHidden = true sceneView.scene.rootNode.addChildNode(focusNode)
Посмотрите, что здесь происходит:
- Это загружает весь FocusScene.scn в локальную переменную.
- Теперь, когда у вас есть только что загруженная сцена, вас интересует только узел фокусировки. Этот код выполняет поиск во всех дочерних узлах в фокусной сцене узла с именем Focus. После того, как он найден, он сохраняет узел
focusNode
в. - Наконец, вы устанавливаете состояние узла фокусировки по умолчанию скрытым перед добавлением его в основную сцену.
С помощью этой функции не забудьте инициализировать узел фокуса при запуске приложения. Для этого добавьте следующий вызов функции в нижнюю частьviewDidLoad()
:
self.initFocusNode()
Отлично, теперь вы инициализировали узел фокусировки.
Обновление узла фокусировки
Теперь, когда узел фокуса готов к работе, вам нужен некоторый код для управления видимостью узла.
Добавьте следующую функцию в раздел Управление узлом фокусировки:
func updateFocusNode() { // 1 guard appState != .Started else { focusNode.isHidden = true return } // 2 if let query = self.sceneView.raycastQuery( from: self.focusPoint, allowing: .estimatedPlane, alignment: .horizontal) { // 3 let results = self.sceneView.session.raycast(query) if results.count == 1 { if let match = results.first { // 4 let t = match.worldTransform // 5 self.focusNode.position = SCNVector3( x: t.columns.3.x, y: t.columns.3.y, z: t.columns.3.z) self.appState = .TapToStart focusNode.isHidden = false } } else { // 6 self.appState = .PointAtSurface focusNode.isHidden = true } } }
Совсем немного происходит в этой функции обновления:
- Во-первых, приложение должно обновлять узел фокуса только в начальном состоянии. Если нет, то состояние узла фокуса должно быть невидимым во все времена.
- Это выполняет тест ray-cast, который запускает виртуальный луч из точки фокусировки наружу в дополненное пространство. Также важно отметить, что тест ray-cast будет рассматривать только пересечения с оценочными плоскостями, которые были идентифицированы как горизонтальные поверхности.
- После завершения теста вас интересует только первый результат попадания.
- Вы используете результат попадания
worldTransform
-преобразование , содержащее информацию о положении, ориентации и масштабе. - Здесь вы обновляете положение узла фокуса на основе преобразования результата попадания. Позиционную информацию можно найти в третьем столбце матрицы преобразования. На этом этапе вы можете сделать узел фокуса видимым и изменить состояние приложения на TapToStart.
- В конечном счете, если тест ray-cast не дал результатов, приложение должно продолжать инструктировать пользователя указывать на допустимую поверхность, а узел фокусировки должен оставаться в скрытом состоянии.
Теперь, чтобы постоянно обновлять узел фокуса, добавьте следующий вызов функцииrenderer(:updateAtTime:)
:
self.updateFocusNode()
Это гарантирует, что узел фокусировки обновляется один раз в каждом кадре.
Чтобы протестировать узел фокусировки, выполните сборку и запуск.

После процесса адаптации при указании на горизонтальную поверхность появляется узел фокусировки, и приложение переключается на TapToStart. Когда вы укажете в сторону от поверхности, узел фокусировки скроется, и приложение переключится на PointToSurface. Отлично!
Создание сцены
Теперь, когда вы знаете, где вы хотите разместить свой виртуальный контент, пришло время создать интересный контент для размещения. :]
Построение сцены
Создайте новую пустую сцену с именем ARPortScene.scn, щелкнув правой кнопкой мыши папку art.scnassets/Scenes и выбрав Новый файл. Если сцена все еще выбрана, удалите узел камеры под графиком сцены и создайте новый пустой узел с именем ARPort.

Узел ARPort будет выступать в качестве корневого узла для всей сцены. Вы добавите все элементы в качестве дочерних элементов этого узла.
Перетащите art.scnassets/Models/Base.scn в пустое пространство графика сцены.

Это добавляет сцену Base.scn в качестве опорного узла к сцене ARPort в положение по умолчанию.
Наконец, перетащите базовый ссылочный узел поверх узла ARPort, сделав его дочерним узлом.

Выбрав базовый узел, откройте инспектор узлов и установите положение преобразования равным (x: 0, y: 0.49, z:0), чтобы разместить базовый узел поверх плоскости заземления.

Теперь выполните тот же процесс, что и для базового узла, и перетащите оставшиеся узлы из art.scnassets/Prefabs в ARPortScene.scn как дочерние узлы узла ARPort.
Начните с Buildings.scn, расположенного в (x: 0, y: 0.1, z:0).

Теперь вы видите главный терминал аэропорта и диспетчерскую вышку с большим радаром.
Далее добавьте плоскости.scn, расположенные в (x: 0, y: 0.1, z:0).

Это добавляет четыре самолета, по одному припаркованному на каждом из доступных терминалов.
Сделайте то же самое для SolarFarm.scn, FuelDepots.scn и Trees.scn, все они расположены в (x: 0, y: 0.1, z:0).

Фантастика, ваш аэропорт отлично подходит. Но подождите, разве чего-то важного все еще не хватает? О, конечно, взлетно-посадочная полоса, упс! :]
Как и раньше, добавьте Runway.scn и расположите его в (x: 0, y: 0.1, z:0).

Хорошо сделано, какая гладкая взлетно-посадочная полоса; есть даже самолет, готовый к взлету.
Но… вы можете заметить, что деревья выглядят немного плоскими. Это потому, что сцена все еще нуждается в освещении.
Добавление света и теней
Создайте новый пустой узел как дочерний элемент ARPort и назовите его Lights & Shadows. Из библиотеки объектов перетащите направленный свет в сцену и сделайте его дочерним элементом Lights & Shadows. Переименуйте его в DirectionalLightтоже.
Установите свет в положение (x: 0, y: 1, z:0) и установите вращение Эйлера в положение (x: -75, y: 0, z:-40).

Деревья больше не должны казаться плоскими. Вместо этого они хорошо освещены сверху, что делает их яркими вверху и темными внизу. Тем не менее, пока нет падения тени. Ты исправишь это в следующий раз.
Если DirectionalLight по—прежнему выбран, откройте инспектор атрибутов. Найдите раздел Тени и установите флажок Включить тени, чтобы установить направленный свет для отбрасывания теней. Кроме того, установите цвет тени на прозрачность 75%, чтобы тень не была жестким черным цветом.

Добавление ловца теней
Чтобы немного повысить фактор реализма вашего опыта AR, было бы удивительно, если бы высокая диспетчерская башня отбрасывала тень на поверхность земли под ней.
Вы можете достичь этого эффекта с помощью чего-то известного как ловец теней. Затем вы добавите его в сцену.
В библиотеке объектовназовите плоскость ShadowCatcher и перетащите ее в сцену в качестве дочернего элемента Lights & Shadows. В инспекторе атрибутовзадайте размер плоскости (ширина: 5, высота: 5).

Это создает красивую большую белую плоскость, которая улавливает все тени в сцене. Одна проблема: самолет белый, что испортит весь опыт.
Вас интересуют только тени — остальная часть плоскости должна быть прозрачной. SceneKit имеет специальный шейдер именно для такого случая.
Если ShadowCatcher все еще выбран, откройте инспектор материалов и измените свойства затенения на Только тень.

Конечный результат будет выглядеть так:

Большая белая плоскость теперь прозрачна, но она улавливает все важные тени.
Загрузка сцены
Теперь, когда сцена построена, вам нужно сделать две вещи: сначала загрузить сцену, а затем, когда пользователь нажимает, чтобы запустить AR-интерфейс, поместить ARPort в местоположение узла фокуса.
Добавьте следующую переменную в раздел Свойства:
var arPortNode: SCNNode!
Это создает переменную, которая будет содержать загруженный узел ARPort.
Добавьте следующий блок кода в нижнюю частьinitScene()
:
// 1 let arPortScene = SCNScene( named: "art.scnassets/Scenes/ARPortScene.scn")! // 2 arPortNode = arPortScene.rootNode.childNode( withName: "ARPort", recursively: false)! // 3 arPortNode.isHidden = true sceneView.scene.rootNode.addChildNode(arPortNode)
Ну, это было просто и должно выглядеть знакомо. Это почти тот же процесс, который вы использовали для загрузки узла фокуса.
Тем не менее, вот более пристальный взгляд:
- Начните с загрузки всего ARPortScene.
- Затем найдите дочерний узел с именем ARPort в сцене.
- Установите его состояние по умолчанию скрытым, затем добавьте узел в качестве дочернего элемента в основную сцену.
Представление сцены
Когда узел ARPort готов и ожидает отображения, остается одно: отобразить узел, когда пользователь коснется экрана.
Добавьте следующий код вtapGestureHandler(_:)
:
// 1 guard appState == .TapToStart else { return } // 2 self.arPortNode.isHidden = false self.focusNode.isHidden = true // 3 self.arPortNode.position = self.focusNode.position // 4 appState = .Started
Вот что это делает:
- Приложение должно быть в состоянии TapToStart.
- Затем узел фокуса устанавливается в скрытое состояние, а узел аэропорта — в видимое.
- Затем вы устанавливаете положение узла Аэропорта таким же, как и положение узла фокуса.
- Наконец, переведите приложение в начальное состояние.
Который час? Пришло время построить и запустить этот проект!

Найдите достаточно большое пространство, затем укажите на пол и позвольте узлу фокусировки направлять вас. Нажмите, чтобы начать опыт AR и отступить и быть пораженным. Сюрприз, кто-то забыл упомянуть, что сцена анимирована? :]
Только посмотрите на этот блестящий самолет, идущий на посадку. Не забудьте заметить его красивую тень, падающую на плитку пола. Потрясающе!
Добавление взаимодействия
Ваше приложение хорошо работает, и вы почти закончили. Но сначала вы сделаете его немного более полезным, предоставив пользователю некоторые элементы для взаимодействия.
Когда пользователь нажимает на определенные элементы, например, на взлетно-посадочную полосу, появляется рекламный щит, показывающий пользователю некоторую поддельную информацию об отправлении и прибытии.
Как вы узнали ранее в этой главе, рекламный щит-это плоский узел с текстурой на нем, который всегда будет обращен к камере. Эффект достигается простым добавлением ограничения billboard к узлу, аналогично тому, что вы сделали для узла focus ранее.
Добавление рекламных щитов
Чтобы ускорить процесс, есть готовая сцена для вас.
Когда ARPortScene.scn открыт, перетащите art.scnassets/Prfabs/Interaction.scn в график сцены, а затем сделайте узел дочерним по отношению к ARPort.

Вы можете задаться вопросом, почему вы ничего не видите. Это потому, что все невидимо. Чтобы увидеть, как выглядит узел и его элементы, откройте art.scnassets/Prfabs/Interaction.scn.
На графике сцены, под узлом взаимодействия, вы увидите целую кучу сенсорных узлов. Выберите первый и откройте инспектор материалов. Затем измените свойства Diffuse на 50% прозрачности.

Это покажет примитивные коробки и сферы, которые действуют как точки взаимодействия. Когда пользователь коснется любого из этих сенсорных узлов, вы раскроете дочерний узел рекламного щита.
Примечание: Не забудьте вернуться назад и установить свойства материала Diffuse обратно на 0% прозрачности, когда вы закончите тестирование.
Обработка сенсорного ввода
В ViewController.swiftдобавьте следующий код в раздел Управление сценой:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { DispatchQueue.main.async { // 1 if let touchLocation = touches.first?.location( in: self.sceneView) { if let hit = self.sceneView.hitTest(touchLocation, options: nil).first { // 2 if hit.node.name == "Touch" { // 3 let billboardNode = hit.node.childNode( withName: "Billboard", recursively: false) billboardNode?.isHidden = false } // 4 if hit.node.name == "Billboard" { hit.node.isHidden = true } } } } }
Вот что происходит:
- Это занимает первое место касания на экране, а затем выполняет тест на попадание, чтобы определить, был ли затронут какой-либо узел в пространстве AR. Вас интересует только первый узел.
- Вас интересуют только узлы с именем Touch.
- Это находит дочерний узел с именем Billboard и устанавливает его состояние видимым.
- Если узел не был сенсорным узлом, но это был узел рекламного щита, это означает, что пользователь коснулся видимого рекламного щита и хочет отклонить его. Затем вы просто возвращаете узел Billboard в скрытое состояние.
Включение статистики и отладки (необязательно)
При решении проблем чрезвычайно полезно включить статистику сцены и отладочную информацию.
Примечание: Этот шаг является дополнительным шагом, который можно использовать для отладки сцен ARKit и SceneKit. Не забудьте выключить его снова, когда закончите тестирование.
Добавьте следующее в нижнюю частьinitScene()
:
// 1 sceneView.showsStatistics = true // 2 sceneView.debugOptions = [ ARSCNDebugOptions.showFeaturePoints, ARSCNDebugOptions.showCreases, ARSCNDebugOptions.showWorldOrigin, ARSCNDebugOptions.showBoundingBoxes, ARSCNDebugOptions.showWireframe]
Вот что происходит:
- Чтобы включить статистику, просто установите
showStatistics
значениеtrue
. - Чтобы отладить определенную сцену, просто предоставьте список параметров отладки в виде массива.
Сделайте быструю сборку и запустите, чтобы проверить это. Вы должны заметить полосу в нижней части экрана с небольшим символом+. Нажмите ее, чтобы открыть панель статистики SceneKit.

Вот что вы видите:
- В левом верхнем углу на дисплее текущей технологии рендеринга отображается Mt, сокращение от Metal. Помните, что SceneKit был построен поверх металла, так что это идеально.
- Рядом с этим вы видите текущую частоту кадров. Частота кадров 60 кадров в секунду означает, что SceneKit в настоящее время рендерит сцену 60 раз за одну секунду. Если это число падает ниже 30 кадров в секунду, вам, вероятно, следует оптимизировать элементы в вашей сцене.
- ◆ Показывает общее количество вызовов draw на кадр.
- ▲ показывает общее количество полигонов на кадр.
- Большой круг в левом нижнем углу показывает текущее время кадра с цветовой легендой каждого компонента и их общее время.
Добавление последних штрихов
Вы в основном сделали, есть только несколько крошечных проблем с уборкой, которые необходимо сделать, чтобы убедиться, что вы правильно справляетесь с каждой ситуацией.
Добавьте следующие строки вstartApp()
:
self.arPortNode.isHidden = true self.focusNode.isHidden = true
Это гарантирует, что и узел focus, и узел airport запускаются в скрытом состоянии.
Добавьте следующие строки кода вresetApp()
:
self.arPortNode.isHidden = true
Это проверяет, что узел airport возвращается в скрытое состояние при перезапуске AR.
Есть одна последняя проблема, которую вам нужно решить: опыт AR просто слишком велик. Вам нужно немного уменьшить его, чтобы он вписался в прогнозируемый след, как указано узлом фокуса.
Откройте art.scnassets/Scenes/ARPortScene.scn и выберите ARPort на графике сцены. Откройте инспектор узлов и установите масштаб преобразований равным (x: 0.75, y: 0.75, z: 0.75).
Это масштабирует весь опыт AR до 75% от его предыдущего размера. Теперь он должен поместиться на вашем обеденном столе!

Бам! Просто так, вы все сделали. Сделайте последнюю сборку и бегите, чтобы пожинать плоды своей тяжелой работы.

Вы заметите, что опыт AR немного меньше, чем раньше. Теперь при нажатии на взлетно-посадочную полосу появится всплывающее окно с информацией о вылетах и прибытиях. Нажмите на всплывающее окно, чтобы закрыть его. Фантастика!
Ключевые моменты
Поздравляю, вы дошли до конца этой главы и раздела и создали супер крутой AR-опыт, используя SceneKit с ARKit.
Прежде чем подписать контракт, взгляните на некоторые заключительные ключевые моменты:
- Каталоги активов SceneKit: очень легко импортировать 3D-контент в ваши проекты на основе SceneKit с помощью каталога активов. Лучше всего, каталог активов — это просто папка, которая может быть общей, которая отделяет код от графики.
- Узел фокусировки: С помощью Basic ray casting вы можете легко добавить узел фокусировки в свой опыт AR, показывая пользователю, с чем именно он взаимодействует.
- Рекламные щиты: добавление ограничений рекламных щитов к узлам-это детская игра. Теперь эти узлы всегда обращены к камере.
- SceneKit Сцены: Сцены просты в создании и сборке. Вы можете перетаскивать примитивные фигуры из библиотеки объектов или ссылаться на другие сцены с пользовательскими объектами в них.
- Свет и тени: Добавление света к сцене оживляет эту сцену. Свет особенно важен, если вы хотите, чтобы объекты отбрасывали тени.
- Ловцы теней: вы можете поймать тень объекта с помощью базового плоского узла, который использует в качестве материала специальный шейдер «Только тени».
- Представление сцен: SceneKit позволяет легко загрузить сцену из каталога активов. Отобразить эту сцену так же просто, как добавить ее в основную сцену в качестве дочернего узла.
- Взаимодействие: Благодаря мощи тестирования попаданий вы можете быстро добавить взаимодействие сцены в любой опыт AR.
- Статистика и отладка: Когда что-то не имеет смысла, добавление статистики и отладочной информации имеет решающее значение для поиска странных ошибок.
Пойдите, покажите своим друзьям свой удивительный аэропорт AR, но не забудьте вернуться для следующего и окончательного проекта. На этот раз вы узнаете все о том, как создавать совместные AR — приложения. Увидимся там!