ARKit动画SCNNode

时间:2018-08-24 12:52:38

标签: swift scenekit arkit

我有一个SCNNode,在其中显示一个曲面-在此曲面上我想显示一条路径。此路径是一个SCNNode本身,已添加到表面SCNNode。该SCNNode(路径)由多个SCNNode组成,它们是整个路径的一小部分-因此我将它们全部添加到路径SCNNode中。

工作流程如下:

  1. 计算路径的SCNNode块
  2. 将块添加到完整路径SCNNode
  3. 添加每个SCNNode块后->将完整路径添加到表面SCNNode

问题:我不仅要添加它,还想从头到尾(从第一个块到最后一个块)对其进行动画处理,但是我该怎么做?

感谢您的帮助!

1 个答案:

答案 0 :(得分:2)

由于您尚未提供任何代码(请下次提供),我将提供一种解决方案,该解决方案应为您指明正确的方向。

让我们首先创建一个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个全局变量,这些变量将用于在Timer上运行动画并跟踪我们的工作位置:

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)

    }

}

这足以将您指向正确的方向^ __________ ^。