首页 文章

ARKit 动画 SCNNode

提问于
浏览
3

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

工作流程如下:

  • 计算路径的 SCNNode 块

  • 将块添加到完整路径 SCNNode

  • 添加每个 SCNNode 块时 - >添加表面 SCNNode 的完整路径

问题:我不只是想添加这个我想要从开始到结束的动画(第一个块到最后一个块)但是我该怎么做?

谢谢你的帮助!

1 回答

  • 2

    由于您没有提供任何代码(请在下次提供),我将提供一个解决方案,指出您正确的方向。

    让我们首先创建一个PathItem Class,我们将使用它来创建一个完整的路径 e.g. 一行:

    /// 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
    
     }
    

    不,因为我们将做一些动画让我们创建另外两个全局变量,这些变量将用于在 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)
    
        }
    
    }
    

    这应该足以让你指向正确的方向^ ^。

相关问题