SceneKit animate node along path

后端 未结 1 1079
逝去的感伤
逝去的感伤 2021-01-05 00:11

I have a box node

_boxNode = [SCNNode node];
_boxNode.geometry = [SCNBox boxWithWidth:1 height:1 length:1 chamferRadius:0];
_boxNode.position = SCNVector3Mak         


        
1条回答
  •  星月不相逢
    2021-01-05 00:38

    I came across that question as well and I wrote a little playground. The animation works well. One thing needs to be done. The distance between every point has to be calculated so that time can be scaled to get a smooth animation. Just copy & paste the code into a playground. The code is in Swift 3.

    Here is my solution (the BezierPath extension is not from me, found it here):

    import UIKit
    import SceneKit
    import PlaygroundSupport
    
    let animationDuration = 0.1
    
    public extension UIBezierPath {
    
        var elements: [PathElement] {
            var pathElements = [PathElement]()
            withUnsafeMutablePointer(to: &pathElements) { elementsPointer in
                cgPath.apply(info: elementsPointer) { (userInfo, nextElementPointer) in
                    let nextElement = PathElement(element: nextElementPointer.pointee)
                    let elementsPointer = userInfo!.assumingMemoryBound(to: [PathElement].self)
                    elementsPointer.pointee.append(nextElement)
                }
            }
            return pathElements
        }
    }
    
    public enum PathElement {
    
        case moveToPoint(CGPoint)
        case addLineToPoint(CGPoint)
        case addQuadCurveToPoint(CGPoint, CGPoint)
        case addCurveToPoint(CGPoint, CGPoint, CGPoint)
        case closeSubpath
    
        init(element: CGPathElement) {
            switch element.type {
            case .moveToPoint: self = .moveToPoint(element.points[0])
            case .addLineToPoint: self = .addLineToPoint(element.points[0])
            case .addQuadCurveToPoint: self = .addQuadCurveToPoint(element.points[0], element.points[1])
            case .addCurveToPoint: self = .addCurveToPoint(element.points[0], element.points[1], element.points[2])
            case .closeSubpath: self = .closeSubpath
            }
        }
    }
    
    public extension SCNAction {
    
        class func moveAlong(path: UIBezierPath) -> SCNAction {
    
            let points = path.elements
            var actions = [SCNAction]()
    
            for point in points {
    
                switch point {
                case .moveToPoint(let a):
                    let moveAction = SCNAction.move(to: SCNVector3(a.x, a.y, 0), duration: animationDuration)
                    actions.append(moveAction)
                    break
    
                case .addCurveToPoint(let a, let b, let c):
                    let moveAction1 = SCNAction.move(to: SCNVector3(a.x, a.y, 0), duration: animationDuration)
                    let moveAction2 = SCNAction.move(to: SCNVector3(b.x, b.y, 0), duration: animationDuration)
                    let moveAction3 = SCNAction.move(to: SCNVector3(c.x, c.y, 0), duration: animationDuration)
                    actions.append(moveAction1)
                    actions.append(moveAction2)
                    actions.append(moveAction3)
                    break
    
                case .addLineToPoint(let a):
                    let moveAction = SCNAction.move(to: SCNVector3(a.x, a.y, 0), duration: animationDuration)
                    actions.append(moveAction)
                    break
    
                case .addQuadCurveToPoint(let a, let b):
                    let moveAction1 = SCNAction.move(to: SCNVector3(a.x, a.y, 0), duration: animationDuration)
                    let moveAction2 = SCNAction.move(to: SCNVector3(b.x, b.y, 0), duration: animationDuration)
                    actions.append(moveAction1)
                    actions.append(moveAction2)
                    break
    
                default:
                    let moveAction = SCNAction.move(to: SCNVector3(0, 0, 0), duration: animationDuration)
                    actions.append(moveAction)
                    break
                }   
            }
            return SCNAction.sequence(actions)
        }
    }
    
    
    
    let scnView = SCNView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
    scnView.autoenablesDefaultLighting = true
    
    let scene = SCNScene()
    scnView.scene = scene
    
    let light = SCNLight()
    light.type = .ambient
    let lightNode = SCNNode()
    lightNode.light = light
    scene.rootNode.addChildNode(lightNode)
    
    let camera = SCNCamera()
    let cameraNode = SCNNode()
    cameraNode.camera = camera
    cameraNode.position = SCNVector3(0,0,10)
    scene.rootNode.addChildNode(cameraNode)
    
    let box = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0)
    let boxNode = SCNNode(geometry: box)
    boxNode.geometry?.firstMaterial?.diffuse.contents = UIColor.red
    
    scene.rootNode.addChildNode(boxNode)
    
    let path1 = UIBezierPath(roundedRect: CGRect(x: 1, y: 1, width: 2, height: 2), cornerRadius: 1)
    
    let moveAction = SCNAction.moveAlong(path: path1)
    let repeatAction = SCNAction.repeatForever(moveAction)
    SCNTransaction.begin()
    SCNTransaction.animationDuration = Double(path1.elements.count) * animationDuration
    boxNode.runAction(repeatAction)
    SCNTransaction.commit()
    
    PlaygroundPage.current.liveView = scnView
    

    0 讨论(0)
提交回复
热议问题