我有一个盒子节点
_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]];
答案 0 :(得分:7)
我也遇到了这个问题,我写了一个小游乐场。动画效果很好。有一件事需要做。必须计算每个点之间的距离,以便可以缩放时间以获得平滑的动画。只需复制&将代码粘贴到游乐场中。代码在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(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