ARKit Animation SCNNode - PullRequest
       8

ARKit Animation SCNNode

0 голосов
/ 24 августа 2018

У меня есть SCNNode, где я отображаю поверхность - на этой поверхности я хочу отобразить путь. Этот путь является самим SCNNode, который добавляется к поверхностному SCNNode. Этот SCNNode (путь) состоит из нескольких SCNNode, которые являются небольшими частями всего пути, поэтому я добавляю их все в путь SCNNode.

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

  1. Рассчитать куски SCNNode пути
  2. Добавить фрагменты к полному пути. SCNNode
  3. Когда добавляется каждый блок SCNNode -> добавляется полный путь к поверхности SCNNode

Проблема: я не просто хочу добавить это, я хочу анимировать это от начала до конца (от первого до последнего), но как мне это сделать?

Спасибо за любую помощь!

1 Ответ

0 голосов
/ 25 августа 2018

Поскольку вы не предоставили никакого кода (пожалуйста, сделайте это в следующий раз), я собираюсь предоставить решение, которое должно указать вам правильное направление.

Давайте начнем с создания PathItem Class, котороемы будем использовать, чтобы составить полный путь, например, строку из них:

/// Path Item Node
class PathItem: SCNNode{


    /// Creates A PathItem
    ///
    /// - Parameters:
    ///   - size: CGFloat (Defaults To 20cm)
    ///   - texture: UIColour
    ///   - position: SCNVector3
    init(size: CGFloat = 0.2, texture: UIColor, position: SCNVector3){

        super.init()

        //1. Create Our Path Geometry
        let pathGeometry = SCNPlane(width: size, height: size)

        //2. Assign The Colour To The Geoemtry
        pathGeometry.firstMaterial?.diffuse.contents = texture

        //3. Assign The Geometry, Position The Node & Rotate The Node So It Is Horizontal
        self.geometry = pathGeometry
        self.position = position
        self.eulerAngles.x = GLKMathDegreesToRadians(-90)
    }

    required init?(coder aDecoder: NSCoder) { fatalError("Path Item Coder Has Not Been Implemented") }

}

Теперь, сделав это, давайте создадим func, который создаст линию PathItem (путь).

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

let pathItemSize: CGFloat = 0.2

Затем мы создаем нашу функцию, которая чередует цвет каждого PathItem, а также предоставляет имуникальный name или индекс, который мы будем использовать позже в нашей анимации:

/// Create A Path With A Number Of Elements
///
/// - Parameter numberOfElements: Int
/// - Returns: PATH (SCNNode)
func createdPathOfSize(_ numberOfElements: Int) {

    var pathColour: UIColor!

    //2. Loop Through The Number Of Path Elements We Want & Place Them In A Line
    for pathIndex in 0 ..< numberOfElements{

        //a. Position Each Peice Next To Each Other Based On The Index
        let pathPosition = SCNVector3(0, -0.2, -pathItemSize * CGFloat(pathIndex+1))

        //b. Alternate The Colour Of Our Path
        if pathIndex % 2 == 0 { pathColour = UIColor.white } else { pathColour = UIColor.black }

        //c. Create Our Path Item With A Unique Index We Can Use For Animating
        let pathItem = PathItem(texture: pathColour, position: pathPosition)
        pathItem.name = String(pathIndex)

        //d. Set It To Hidden Initially
        pathItem.isHidden = true

        //e. Add It To Our Scene
       self.augmentedRealityView.scene.rootNode.addChildNode(pathItem)
    }

}

Чтобы сгенерировать Path, мы можем теперь сделать что-то вроде этого:

override func viewDidLoad() {
    super.viewDidLoad()

    //1. Set Up Our ARSession
    augmentedRealityView.session = augmentedRealitySession
    sessionConfiguration.planeDetection = .horizontal
    augmentedRealityView.debugOptions = .showFeaturePoints
    augmentedRealitySession.run(sessionConfiguration, options: [.resetTracking, .removeExistingAnchors])

    //2. Create A Path Of 10 PathItems
    createdPathOfSize(10)
}

При этом яиметь следующее Global variables:

@IBOutlet var augmentedRealityView: ARSCNView!
let augmentedRealitySession = ARSession()
let sessionConfiguration = ARWorldTrackingConfiguration()

Теперь мы сгенерировали наш Путь, нам нужно его анимировать!

Чтобы обеспечить немного разнообразия, давайте создадим Enum, который мы можем использоватьсоздавать различные PathAnimations:

 /// Path Item Animation
 ///
 /// - UnHide: UnHides The Path Item
 /// - FadeIn: Fades The Path Item In
 /// - FlipIn: Flips The Path Item In
 enum AnimationType{

     case UnHide
     case FadeIn
     case FlipIn

 }

Нет, так как мы будемсоздание некоторых анимаций позволяет создать еще 2 глобальные переменные, которые будут использоваться для запуска нашей анимации на таймере и отслеживания того, где мы находимся:

var pathAnimationTimer: Timer?
var time: Int = 0

Теперь давайте создадим нашу функцию анимации:

/// Animates The Laying Of The Path
///
/// - Parameters:
///   - numberOfElements: Int
///   - animation: AnimationType
func animatePathElements(_ numberOfElements: Int, withAnimation animation: AnimationType ){

    //1. If We Are Flipping The PathItems In We Need To 1st Unhide Them All & Rotate Them To A Vertical Postions
    if animation == .FlipIn {

        let pathItems = self.augmentedRealityView.scene.rootNode.childNodes

        pathItems.forEach({ (pathItemToAnimate) in
            pathItemToAnimate.isHidden = false
            pathItemToAnimate.eulerAngles.x = GLKMathDegreesToRadians(0)
        })

    }

    //2. Create Our Time Which Will Run Every .25 Seconds
    pathAnimationTimer = Timer.scheduledTimer(withTimeInterval: 0.25, repeats: true) { timer in

        //3. Whilst Our Time Doesnt Equal The Number Of Path Items Then Continue Our Animation
        if self.time != numberOfElements{

            //a. Get The Current Node Remembering Each One Has A Unique Name (Index
            guard let pathItemToAnimate = self.augmentedRealityView.scene.rootNode.childNode(withName: "\(self.time)", recursively: false) else { return }

            //b. Run The Desired Animation Sequence
            switch animation{
            case .UnHide:
                 //Simply Unhide Each PathItem
                 pathItemToAnimate.isHidden = false
            case .FadeIn:

                //1. Unhide The Item & Sets It's Opacity To 0 Rendering It Invisible
                 pathItemToAnimate.isHidden = false
                 pathItemToAnimate.opacity = 0

                 //2. Create An SCNAction To Fade In Our PathItem
                 let fadeInAction = SCNAction.fadeOpacity(to: 1, duration: 0.25)
                 pathItemToAnimate.runAction(fadeInAction)

            case .FlipIn:
                 //Simply Rotate The Path Item Horizontally
                 pathItemToAnimate.eulerAngles.x = GLKMathDegreesToRadians(-90)
            }

            self.time += 1

        }else{
            //4. Our Animation Has Finished So Invalidate The Timer
            self.pathAnimationTimer?.invalidate()
            self.time = 0
        }
    }
}

Затем нам нужно добавить это в конец нашей функции createPathOfSize следующим образом:

/// Create A Path With A Number Of Elements Which Can Be Animated
///
/// - Parameter numberOfElements: Int
/// - Returns: PATH (SCNNode)
func createdPathOfSize(_ numberOfElements: Int) {

    var pathColour: UIColor!

    //1. Loop Through The Number Of Path Elements We Want & Place Them In A Line
    for pathIndex in 0 ..< numberOfElements{

        //a. Position Each Peice Next To Each Other Based On The Index
        let pathPosition = SCNVector3(0, -0.2, -pathItemSize * CGFloat(pathIndex+1))

        //b. Alternate The Colour Of Our Path

        if pathIndex % 2 == 0 { pathColour = UIColor.white } else { pathColour = UIColor.black }

        //b. Create Our Path Item With A Unique Index We Can Use For Animating
        let pathItem = PathItem(texture: pathColour, position: pathPosition)
        pathItem.name = String(pathIndex)

        //c. Set It To Hidden Initially
        pathItem.isHidden = true

        //d. Add It To Our Scene
       self.augmentedRealityView.scene.rootNode.addChildNode(pathItem)
    }

    //2. Animate The Path
    animatePathElements(10, withAnimation: .FlipIn)

}

А вот полный пример:

import UIKit
import ARKit

//----------------------
//MARK: - Path Animation
//----------------------

/// Path Item Animation
///
/// - Show: UnHides The Path Item
/// - FadeIn: Fades The Path Item In
enum AnimationType{

    case UnHide
    case FadeIn
    case FlipIn

}

//-----------------
//MARK: - Path Item
//-----------------

/// Path Item Node
class PathItem: SCNNode{


    /// Creates A PathItem
    ///
    /// - Parameters:
    ///   - size: CGFloat (Defaults To 20cm)
    ///   - texture: UIColour
    ///   - position: SCNVector3
    init(size: CGFloat = 0.2, texture: UIColor, position: SCNVector3){

        super.init()

        //1. Create Our Path Geometry
        let pathGeometry = SCNPlane(width: size, height: size)

        //2. Assign The Colour To The Geoemtry
        pathGeometry.firstMaterial?.diffuse.contents = texture

        //3. Assign The Geometry, Position The Node & Rotate The Node So It Is Horizontal
        self.geometry = pathGeometry
        self.position = position
        self.eulerAngles.x = GLKMathDegreesToRadians(-90)
    }

    required init?(coder aDecoder: NSCoder) { fatalError("Path Item Coder Has Not Been Implemented") }

}

class ViewController: UIViewController {

    typealias PATH = SCNNode
    @IBOutlet var augmentedRealityView: ARSCNView!
    let augmentedRealitySession = ARSession()
    let sessionConfiguration = ARWorldTrackingConfiguration()

    var pathPlaced = false
    let pathItemSize: CGFloat = 0.2
    var pathAnimationTimer: Timer?
    var time: Int = 0

    //----------------------
    //MARK: - View LifeCycle
    //----------------------

    override func viewDidLoad() {
        super.viewDidLoad()

        //1. Set Up Our ARSession
        augmentedRealityView.session = augmentedRealitySession
        sessionConfiguration.planeDetection = .horizontal
        augmentedRealityView.debugOptions = .showFeaturePoints
        augmentedRealitySession.run(sessionConfiguration, options: [.resetTracking, .removeExistingAnchors])

        //2. Create A Path Of 10 Path Items
        createdPathOfSize(10)
    }

    //---------------------------------
    //MARK: - Path Creation & Animation
    //---------------------------------

    /// Animates The Laying Of The Path
    ///
    /// - Parameters:
    ///   - numberOfElements: Int
    ///   - animation: AnimationType
    func animatePathElements(_ numberOfElements: Int, withAnimation animation: AnimationType ){

        if animation == .FlipIn {

            let pathItems = self.augmentedRealityView.scene.rootNode.childNodes

            pathItems.forEach({ (pathItemToAnimate) in
                pathItemToAnimate.isHidden = false
                pathItemToAnimate.eulerAngles.x = GLKMathDegreesToRadians(0)
            })

        }

        pathAnimationTimer = Timer.scheduledTimer(withTimeInterval: 0.25, repeats: true) { timer in

            //1. Whilst Our Time Doesnt Equal The Number Of Path Items Then Continue Our Animation
            if self.time != numberOfElements{

                guard let pathItemToAnimate = self.augmentedRealityView.scene.rootNode.childNode(withName: "\(self.time)", recursively: false) else { return }

                //2. Run The Desired Animation Sequence
                switch animation{
                case .UnHide:
                    pathItemToAnimate.isHidden = false
                case .FadeIn:
                    pathItemToAnimate.isHidden = false
                    pathItemToAnimate.opacity = 0
                    let fadeInAction = SCNAction.fadeOpacity(to: 1, duration: 0.3)
                    pathItemToAnimate.runAction(fadeInAction)
                case .FlipIn:

                    pathItemToAnimate.eulerAngles.x = GLKMathDegreesToRadians(-90)
                }

                self.time += 1

            }else{
                self.pathAnimationTimer?.invalidate()
                self.time = 0
            }
        }


    }


    /// Create A Path With A Number Of Elements Which Can Be Animated
    ///
    /// - Parameter numberOfElements: Int
    /// - Returns: PATH (SCNNode)
    func createdPathOfSize(_ numberOfElements: Int) {

        var pathColour: UIColor!

        //1. Loop Through The Number Of Path Elements We Want & Place Them In A Line
        for pathIndex in 0 ..< numberOfElements{

            //a. Position Each Peice Next To Each Other Based On The Index
            let pathPosition = SCNVector3(0, -0.2, -pathItemSize * CGFloat(pathIndex+1))

            //b. Alternate The Colour Of Our Path

            if pathIndex % 2 == 0 { pathColour = UIColor.white } else { pathColour = UIColor.black }

            //b. Create Our Path Item With A Unique Index We Can Use For Animating
            let pathItem = PathItem(texture: pathColour, position: pathPosition)
            pathItem.name = String(pathIndex)

            //c. Set It To Hidden Initially
            pathItem.isHidden = true

            //d. Add It To Our Scene
            self.augmentedRealityView.scene.rootNode.addChildNode(pathItem)
        }

        //2. Animate The Path
        animatePathElements(10, withAnimation: .FlipIn)

    }

}

Это должно быть большечем достаточно, чтобы указать вам правильное направление ^ __________ ^.

...