При создании прототипа в Reality Composer очевидно, что мы не можем активировать одновременное обнаружение вертикальной и горизонтальной плоскости, потому что в меню, а не набор опций. Но многие разработчики не могут догадаться, как это сделать даже в RealityKit. Моя история прольет свет на эту тему.

Однако в ARKit есть простой подход - мы должны использовать Свойство экземпляра planeDetection, соответствующее протоколу OptionSet. У этого свойства есть два варианта: по вертикали и по горизонтали:

let config = ARWorldTrackingConfiguration()
config.planeDetection = [.vertical, .horizontal]
sceneView.session.run(config)

Если вы хотите узнать, к какому типу выравнивания принадлежит ваш ARPlaneAnchor, вы можете проверить его с помощью простого if-else-if.

func renderer(_ renderer: SCNSceneRenderer,
             didAdd node: SCNNode,
              for anchor: ARAnchor) {
    guard let planeAnchor = anchor as? ARPlaneAnchor 
    else { return }
    if planeAnchor.alignment == .horizontal {
        print("Horizontal")
    } else if planeAnchor.alignment == .vertical {
        print("Vertical")
    }
}

RealityKit

RealityKit имеет класс AnchorEntity с удобной функцией init, нацеленной на самолеты - init (plane: classification: minimumBounds :). Для случая обнаружения плоскости доступны три варианта выравнивания: вертикальное, горизонтальное или любое. .

let planeAnchor = AnchorEntity(.plane([.vertical, .horizontal], 
                      classification: [.wall, .floor, .ceiling], 
                       minimumBounds: [1.0, 1.0]))

Учтите: этот инициализатор не работает с приложением Simulator, поэтому убедитесь, что в активной схеме Xcode выбрано физическое устройство.

Давайте программно создадим примитив блока в файле ViewController.swift и закрепим его с нашим объектом planeAnchor:

import UIKit
import RealityKit
class ViewController: UIViewController {
    @IBOutlet weak var arView: ARView!
    override func viewDidLoad() {
       super.viewDidLoad()
        let boxMesh: MeshResource = .generateBox(size: 0.25)
        let modelEntity = ModelEntity(mesh: boxMesh)
        let planeAnchor = AnchorEntity(.plane([.any], 
                              classification: [.any], 
                               minimumBounds: [0.5, 0.5]))
        planeAnchor.addChild(modelEntity)
        arView.scene.anchors.append(planeAnchor)
    }
}

Схематическое изображение сцены и коллекции привязанных к ней якорей, моделей, источников света, отслеживаемых тел, звуков окружающей среды и камер выглядит так просто:

Тем не менее, в ARKit коллекция якорей может быть доступна через объект ARSession, а не через объект Scene.

Reality Composer + RealityKit

Предположим, что мы создали простую сцену, содержащую модель часов в Reality Composer. Для этого проекта мы выбрали конфигурацию World Tracking с обнаружением вертикальной плоскости.

Поскольку у нас есть только выравнивание по вертикали, мы также можем добавить выравнивание по горизонтали. Сначала нам нужно прочитать эту сцену в Xcode. Для этого мы должны использовать оператор Swift try!, потому что мы загружаем сцену с помощью функции бросания.

let sceneAnchor = try! Experience.loadInteriorItems()

Теперь все, что нам нужно сделать, это перейти к компоненту привязки в иерархии сцены, чтобы назначить как вертикальное, так и Параметры горизонтального выравнивания.

Давайте посмотрим на иерархию сцены, чтобы узнать, где расположен объект привязка с соответствующим ему компонентом привязки. Но перед этим мы должны дать описательные имена нашей сцене и нашему якору. Это довольно просто:

sceneAnchor.name = "myScene"
sceneAnchor.children[0].name = "myAnchor"
sceneAnchor.children[0].children[0].name = "myEntity"

Затем просто распечатайте свойство.

print(sceneAnchor)

В соответствии с иерархией путь к компоненту привязки теперь очевиден:

sceneAnchor.children[0].anchor?.anchoring

Вставьте это в наш код:

class ViewController: UIViewController {
    @IBOutlet weak var arView: ARView!
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        let sceneAnchor = try! Experience.loadInteriorItems()
        sceneAnchor.children[0].anchor?.anchoring = 
               AnchoringComponent(.plane([.horizontal, .vertical], 
                           classification: .any, 
                            minimumBounds: [0.2, 0.2]))
        arView.scene.anchors.append(sceneAnchor)
    }
}

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

let sceneAnchor = try! Experience.loadInteriorItems()
let clockEntity = sceneAnchor.children[0].children[0].children[0]

И если у модели Reality Composer есть имя (и, да, у нее есть имя, помните?), Тогда также была переменная с аналогичным именем, автоматически сгенерированная Reality Composer. Так что второй вариант намного удобнее, не правда ли?

let clockEntity = sceneAnchor.clock!

Полная версия нашего кода может выглядеть так:

class ViewController: UIViewController {
    @IBOutlet weak var arView: ARView!
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        let sceneAnchor = try! Experience.loadInteriorItems()
        let clockEntity = sceneAnchor.clock!
        let anchor = AnchorEntity(plane:[.horizontal, .vertical], 
                         classification: .any, 
                          minimumBounds: [0.2, 0.2])
        anchor.addChild(clockEntity)
        arView.scene.anchors.append(anchor)
    }
}

Вуаля!

Пожертвовать автору

addr1q9w70n62nu8p7f9ukfn66gzumm9d9uxwppkx7gk7vd7gy0ehfavj97gkncwm8t8l8l8x9e4adzmw2djh4y5gd9rmtewqr99zr3

На этом пока все.

Если эта публикация вам полезна, нажмите кнопку Хлопать и удерживайте ее. На Medium вы можете аплодировать до 50 за каждое сообщение.

Если у вас есть вопросы, вы можете связаться со мной через StackOverflow.

До встречи!