SceneKit aimate node along the path

I have a node field

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

I have a way

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

I want my boxing to travel my path once.

How to do it in SceneKit?

I would like to make a method that looks like

 [_boxNode runAction:[SCNAction moveAlongPath:path forDuration:duration]]; 
+6
source share
1 answer

I came across this question and I wrote a small playground. Animation works well. One thing to do. The distance between each point must be calculated so that the time can be scaled to get a smooth animation. Just copy and paste the code into the playground. The code is in Swift 3.

Here is my solution (the BezierPath extension is not from me, found 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(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 
+5
source

Source: https://habr.com/ru/post/986611/


All Articles