ARKit动画SCNNode

3
我有一个SCNNode,在其上显示表面 - 我想在该表面上显示路径。这个路径本身是一个SCNNode,它被添加到表面SCNNode上。这个SCNNode(路径)由多个SCNNode组成,它们都是整个路径的小块 - 因此我将它们全部添加到路径SCNNode中。
所以工作流程如下:
1. 计算路径的SCNNode块 2. 将这些块添加到完整路径SCNNode中 3. 当每个SCNNode块都添加时 -> 添加完整路径到表面SCNNode 问题:我不只想添加,我想从开始到结束(从第一个块到最后一个块)进行动画处理,但我应该怎么做?
感谢任何帮助!
1个回答

2

由于您没有提供任何代码(下次请务必提供),我将提供一个解决方案,应该能指引您正确的方向。

让我们从创建一个PathItem类开始,我们将使用它来创建完整的路径,例如一行路径:

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

我有以下的全局变量:

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

现在我们已经生成了路径,需要对其进行动画处理!
为了增加一些多样性,让我们创建一个枚举类型,以便创建不同的路径动画:
 /// 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)

    }

}

这应该足以为您指明正确的方向^_____^。

非常感谢,我会尝试这个!但是我不能在这里发布代码,否则我已经做了 (; - ValW
如果这解决了你的问题,请标记为正确答案 :) - BlackMirrorz
嗨 - 我只需要带有计时器的代码片段 - 太棒了,谢谢。 我尝试使用 "DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(0.2), execute: {}" 并仅将不透明度设置为1,当我将其添加到 "FullPath" 时设置为0 - 没有起作用。 你的代码像需要的一样工作,非常感谢! - ValW

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接