SceneKit沿路径设置动画节点

我有一个盒子节点

_boxNode = [SCNNode node]; _boxNode.geometry = [SCNBox boxWithWidth:1 height:1 length:1 chamferRadius:0]; _boxNode.position = SCNVector3Make(0, 0, -2); [scene.rootNode addChildNode:_boxNode]; 

我有一条路

 CGPathRef path = CGPathCreateWithEllipseInRect(CGRectMake(-2, -2, 4, 4), nil); 

我希望我的箱子沿着我的路径前行一次。

我如何在SceneKit中执行此操作?

我想制作一个看起来像的方法

 [_boxNode runAction:[SCNAction moveAlongPath:path forDuration:duration]]; 

我也遇到了这个问题,我写了一个小操场。 动画效果很好。 有一件事需要做。 必须计算每个点之间的距离,以便可以缩放时间以获得平滑的动画。 只需将代码复制并粘贴到游乐场即可。 代码在Swift 3中。

这是我的解决方案(BezierPath扩展不是来自我,在这里找到):

 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(ax, ay, 0), duration: animationDuration) actions.append(moveAction) break case .addCurveToPoint(let a, let b, let c): let moveAction1 = SCNAction.move(to: SCNVector3(ax, ay, 0), duration: animationDuration) let moveAction2 = SCNAction.move(to: SCNVector3(bx, by, 0), duration: animationDuration) let moveAction3 = SCNAction.move(to: SCNVector3(cx, cy, 0), duration: animationDuration) actions.append(moveAction1) actions.append(moveAction2) actions.append(moveAction3) break case .addLineToPoint(let a): let moveAction = SCNAction.move(to: SCNVector3(ax, ay, 0), duration: animationDuration) actions.append(moveAction) break case .addQuadCurveToPoint(let a, let b): let moveAction1 = SCNAction.move(to: SCNVector3(ax, ay, 0), duration: animationDuration) let moveAction2 = SCNAction.move(to: SCNVector3(bx, by, 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