Тестируем возможности ARKit. Создаем игру с дополненной реальностью

в 7:17, , рубрики: AR, arkit, iOS, swift, wwdc, wwdc 2017, xcode, Блог компании Touch Instinct, Разработка под AR и VR, разработка под iOS

image

На WWDC 2017 Apple анонсировала ARKit — SDK для работы с дополненной реальностью. Благодаря ему порог вхождения в эту технологию стал значительно ниже. Можно ожидать появления большого количества качественных игр и приложений.

Если вы смотрели Keynote, то, вероятно, вы уже в восторге от увиденного. Игровой мир, который инженеры Apple смогли развернуть на обычном столе при помощи ARKit, не может оставить равнодушными даже самых искушенных геймеров. Это был не просто прототип, а хорошо работающая технология, над которой действительно потрудились. В этом легко убедиться, запустив несколько демо или попробовав самим привнести что-либо виртуальное в наш мир.

Вынужден расстроить счастливых обладателей iPhone 6 и ниже. На данных девайсах все эти прелести жизни будут недоступны. Для использования всех ключевых функций ARKit необходим процессор А9 и выше. Apple, конечно, даст урезанный доступ к функциональности, но это уже совсем не то.

Дополненная реальность

Дополненная реальность (augmented reality, AR) — это виртуальная среда, которая накладывается на реальный мир для придания ему большей выразительности, информативности или просто ради развлечения. Термин, предположительно, был предложен исследователем компании Boeing Томасом Коделлом еще в 1990 году. Уже тогда начали появляться первые примеры устройств с применением данной технологии. Впервые дополненная реальность была реализована на электронных шлемах летчиков для вывода информации о полете и радаре.

Хочется спросить, чем же все занимались почти 20 лет и почему масштабное развитие эта технология получила лишь сейчас. Все предельно просто. Появление хороших камер в телефонах, сенсоров и развитие технологий компьютерного зрения сделали это возможным.

Что же можно сделать полезного и чего ждать в ближайшее время на полках AppStore? На самом деле все ограничивается лишь фантазией разработчиков. Можно с уверенностью назвать несколько отраслей, где AR произведет революцию с выходом нового фреймворка от Apple:

  • Игровая индустрия;
  • Архитектура;
  • Киноиндустрия.

Возможности ARKit

ARKit — не волшебная палочка Гарри Поттера, а инструмент, который умеет грамотно обрабатывать большое количество данных, полученных от устройства. Благодаря камере и датчикам движения фреймворк отслеживает движение, находит поверхности и определяет освещенность. После анализа данных мы получаем конкретное представление об окружающем мире в виде точек пересечения, координат поверхностей и положении камеры в пространстве.

Основой задачей ARKit является слежение за окружающим миром (World Tracking) для создания виртуальной модели реального мира. Фреймворк распознает особенности видеокадров, отслеживает изменения их положения и сравнивает эту информацию с данными от датчиков движения. Результатом является виртуальная модель реального мира. Отдельная возможность — распознавание плоских горизонтальных поверхностей. ARKit находит плоскости и сообщает об их расположении и размерах.

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

Основой ARKit являются ARSCNView и ARSKView. Они служат для отображения live видео и рендеринга 3D и 2D изображений. Как все уже догадались, это наследники от SCNView и SKView. Следовательно, ARKit не привносит каких-то невероятных особенностей в отображении данных. Это все те же движки для работы с 2D и 3D графикой, с которыми уже все знакомы. Поэтому порог вхождения в данную технологию будет достаточно низким. Apple знаменита любовью к своим технологиям и продуктам, но несмотря на это разработчики ARKit сделали поддержку Unity и Unreal Engine. Это положительно скажется на количестве качественных приложений, которые появятся в ближайшее время.

ARSCNView и ARSKView содержат в себе сердце ARKit — ARSession. Именно этот класс содержит в себе все необходимое для работы с дополненной реальностью. Для запуска ARSession необходимо передать конфигурацию работы сессии.

Тип конфигурации определяет стиль и качество работы AR, которое может быть достигнуто:

  • На девайсах с процессором A9 и новее можно использовать ARWorldTrackingSessionConfiguration. Именно эта конфигурация дает возможность воспользоваться всей мощью нового фреймворка. Для вас будет создана модель окружающего мира в виртуальной реальности и предоставлена информация о плоскостях в поле видимости камеры. Это поможет расположить виртуальные объекты с максимальной точностью.
  • На остальных девайсах, поддерживающих ARKit, будет доступна лишь ARSessionConfiguration. Базовый класс предоставляет только информацию о движении устройства в пространстве, но не строит виртуальных моделей. Это не даст необходимого эффекта и не позволит насладиться всем качеством новой технологии. Вам будет недоступна возможность фиксации виртуальных объектов относительно объектов реального мира.

После выбора типа конфигурации необходимо создать ее экземпляр, произвести настройку и запустить сессию:

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        // Create a session configuration
        let configuration = ARWorldTrackingSessionConfiguration()

        // Run the view's session
        sceneView.session.run(configuration)
}

Важно помнить, что ARKit потребляет довольно много энергии для расчетов. Если View с контентом не отображается в данный момент на экране, то имеет смысл приостановить сессию на это время, используя session.pause().

После запуска сессии можно начинать работать с виртуальным контентом. Если хотите, чтобы ARKit распознавал плоскости, не забудьте установить значение planeDetection у конфигурации в значение horizontal. Изначально распознавание горизонтальных поверхностей выключено. Будем надеяться, что в будущем появится возможность находить и вертикальные поверхности, но пока только горизонтальные.

Способ получения информации об окружающей среде зависит от того, какой вид отображения данных вы будете использовать ARSCNView, ARSKView или Metal. Единицей информации, которую предоставляет ARKit, является ARAnchor. Если у вас включено распознавание поверхностей, то вы столкнетесь с сабклассом ARPlaneAnchor. Он содержит в себе информацию о найденных плоскостях. Благодаря данным якорям есть возможность ориентироваться в пространстве. В случае использования Metal вам придется вручную заниматься рендерингом. Тогда можете подписаться на обновления, используя делегат ARSessionDelegate у класса ARSession, и получать якоря от сессии. Если используете один из Apple движков для рендеринга объектов, тогда есть возможность воспользоваться более удобными делегатами ARSCNViewDelegate или ARSKViewDelegate.

На первый взгляд все довольно просто. Почти всю сложную работу делает ARSession. Давайте попробуем сделать тестовое приложение.

Тестируем возможности ARKit

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

Для создания тестового приложения мы воспользуемся ARSCNView для создания и рендеринга 3D моделей. Наша игра будет состоять из 2 этапов. Сначала мы будем расставлять мишени по комнате, а после пытаться как можно быстрее попасть по ним всем. Игра довольна примитивна, но продемонстрирует простоту создания игр с дополненной реальностью.

Начнем с того, что растянем на весь ViewController ARSCNView и создадим IBOutlet. Далее будем работать с ней, как с обычной SCNView. Произведем первоначальную настройку. Сделаем контроллер делегатом контактов физического мира и выведем статистику. Настроим запуск и паузу сессии при появлении и скрытии контроллера.

override func viewDidLoad() {
        super.viewDidLoad()

        sceneView.scene.physicsWorld.contactDelegate = self

        // Show statistics such as fps and timing information
        sceneView.showsStatistics = true
}

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        // Create a session configuration
        let configuration = ARSessionConfiguration.isSupported ? 
ARWorldTrackingSessionConfiguration() : ARSessionConfiguration()

        // Run the view's session
        sceneView.session.run(configuration)
}

override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        // Pause the view's session
        sceneView.session.pause()
}

Выбор конфигурации сессии зависит от модели устройства, на котором запустили приложение. Крайне важно сделать эту проверку. Иначе, в случае неверной конфигурации, сессия пришлет ошибку и игра не запустится вообще.

ARKit настолько прост, что мы больше не будем использовать никакие его настройки. Единственное, что еще понадобится — это расположение камеры в пространстве виртуального мира. Остальное — дело техники и немного SceneKit.

Мы не будем здесь описывать обработку нажатий или подсчет очков. Это не так важно, и вы можете это увидеть сами в ДЕМО, представленном в конце статьи.

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

Что понадобится, чтобы создать физический объект:

  • задать фигуру определенного размера;
  • создать фигуру с физическими свойствами для контролирования контактов с другими объектам;
  • создать физическое тело для описания поведения объекта при соприкосновении;
  • задать текстуры.

Пример реализации классов патрона в виде шара и логотипа в виде куба с нужными текстурами.

class ARBullet: SCNNode {

    override init() {
        super.init()

        let arKitBox = SCNSphere(radius: 0.025)
        self.geometry = arKitBox
        let shape = SCNPhysicsShape(geometry: arKitBox, options: nil)
        self.physicsBody = SCNPhysicsBody(type: .dynamic, shape: shape)
        self.physicsBody?.isAffectedByGravity = false

        self.physicsBody?.categoryBitMask = CollisionCategory.arBullets.rawValue
        self.physicsBody?.contactTestBitMask = CollisionCategory.logos.rawValue

        // add texture
        let material = SCNMaterial()
        material.diffuse.contents = UIImage(named: "art.scnassets/ARKit_logo.png")
        self.geometry?.materials  = [material]
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

class Logo: SCNNode {

    override init() {
        super.init()

        let logo = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0)
        self.geometry = logo
        let shape = SCNPhysicsShape(geometry: logo, options: nil)

        self.physicsBody = SCNPhysicsBody(type: .dynamic, shape: shape)
        self.physicsBody?.isAffectedByGravity = false

        self.physicsBody?.categoryBitMask = CollisionCategory.logos.rawValue
        self.physicsBody?.contactTestBitMask = CollisionCategory.arBullets.rawValue

        // add texture
        let material = SCNMaterial()
        material.diffuse.contents = UIImage(named: "art.scnassets/logo-mobile.png")
        self.geometry?.materials  = Array(repeating: material, count: 6)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

Хочется обратить внимание на CollisionCategory. Это структура используется для определения типа объекта при контакте.

struct CollisionCategory: OptionSet {
    let rawValue: Int

    static let arBullets  = CollisionCategory(rawValue: 1 << 0)
    static let logos = CollisionCategory(rawValue: 1 << 1)
}

Это стандартная тактика для определения контакта. Свойство categoryBitMask задает маску конкретного объекта, а contactTestBitMask настраивает все контакты, которые нам будут интересны и о которых мы хотим получать уведомления.

Раз мы заговорили про обработку контактов, давайте посмотрим, как это выглядит в контроллере. Во viewDidLoad мы уже подписались на события контактов физического мира. Осталось реализовать одну функцию.

extension ViewController: SCNPhysicsContactDelegate {

    func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
        guard let nodeABitMask = contact.nodeA.physicsBody?.categoryBitMask,
            let nodeBBitMask = contact.nodeB.physicsBody?.categoryBitMask,
            nodeABitMask & nodeBBitMask == CollisionCategory.logos.rawValue & CollisionCategory.arBullets.rawValue else {
                    return
        }

        contact.nodeB.removeFromParentNode()
        logoCount -= 1

        if logoCount == 0 {
            DispatchQueue.main.async {
                self.stopGame()
            }
        }

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
            contact.nodeA.removeFromParentNode()
        })
    }

}

Самое интересное — это первая проверка того, что произошло именно соударение патрона и мишени в виде куба. Она выполняется на основе битовой маски. Это очень удобно и избавляет от большого количества других проверок.

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

Две основные игровые функции — это добавление мишени и выстрел. Добавление происходит на небольшом расстоянии от экрана в той стороне, куда направлена камера. Мы просто создаем уже сконфигурированный объект куба, добавляем его на сцену и настраиваем его расположение относительно камеры в пространстве.

private func addLogo() {
        guard let currentFrame = sceneView.session.currentFrame else {
            return
        }

        let logo = Logo()
        sceneView.scene.rootNode.addChildNode(logo)

        var translation = matrix_identity_float4x4
        translation.columns.3.z = -1
        logo.simdTransform = matrix_multiply(currentFrame.camera.transform, translation)

        logoCount += 1
        if logoCount == ViewController.logoMaxCount {
            startGame()
        }
}

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

private func shoot() {
        let arBullet = ARBullet()

        let (direction, position) = cameraVector
        arBullet.position = position
        arBullet.physicsBody?.applyForce(direction, asImpulse: true)
        sceneView.scene.rootNode.addChildNode(arBullet)
}

Вот так всего за пару десятков строк мы создали простую игру.

Будущее наступит в сентябре

Как видите, Apple потрудились на славу. Благодаря новому фреймворку ARKit создание приложений с дополненной реальностью — так же просто, как сделать приложение с несколькими контроллерами. При этом вам не нужно беспокоиться о красивых декорациях. Эта технология точно изменит наше представление о мобильных приложениях.

Скачивайте новый Xcode 9, и создавайте приложения, которые добавят в наш мир виртуальной магии. Будущее уже здесь. Ну или будет здесь ближе к сентябрю, после очередной презентации Apple.

Демо проект

Скачивайте в репозитории Touch Instinct

Автор: Touch Instinct

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js