Funny, I actually have movement in my game almost the way you described, except that instead of always going clockwise when on the right side and counterclockwise when on the left, it will choose more close way.
So, I grabbed the code and modified it to fit your description. It moves to the left when the end point is to the left of the player, otherwise it will move to the right. You can also set the speed of the node, as well as the radius and position of the orbit.
However, my implementation does not use SKActions and paths to move. Everything happens dynamically in real time, which allows you to make collisions with moving objects and improve motion control. However, if you absolutely must use the paths with SKActions, let me know and I will try to come up with a solution. Essentially, it comes down to finding the arc at tangent points (which the code already does to some extent).
Physical calculations come from my two respondents here and here .
The implementation method is that it first determines the final destination point, as well as the distance angular to the best tangent point, using the secondary circle to find the tangent points. Then, using the centripetal motion, the node moves along the path to the tangent point and then switches to linear motion to end the motion to the final destination.
Below is the code for GameScene:
import SpriteKit enum MotionState { case None, Linear, Centripetal } class GameScene: SKScene { var node: SKShapeNode! var circle: SKShapeNode! var angularDistance: CGFloat = 0 var maxAngularDistance: CGFloat = 0 let dt: CGFloat = 1.0/60.0 //Delta Time var centripetalPoint = CGPoint() //Point to orbit. let centripetalRadius: CGFloat = 60 //Radius of orbit. var motionState: MotionState = .None var invert: CGFloat = 1 var travelPoint: CGPoint = CGPoint() //The point to travel to. let travelSpeed:CGFloat = 200 //The speed at which to travel. override func didMoveToView(view: SKView) { physicsWorld.gravity = CGVector(dx: 0, dy: 0) circle = SKShapeNode(circleOfRadius: centripetalRadius) circle.strokeColor = SKColor.redColor() circle.hidden = true self.addChild(circle) } func moveToPoint(point: CGPoint) { travelPoint = point motionState = .Centripetal //Assume clockwise when point is to the right. Else counter-clockwise if point.x > node.position.x { invert = -1 //Assume orbit point is always one x radius right from node position. centripetalPoint = CGPoint(x: node.position.x + centripetalRadius, y: node.position.y) angularDistance = CGFloat(M_PI) } else { invert = 1 //Assume orbit point is always one x radius left from node position. centripetalPoint = CGPoint(x: node.position.x - centripetalRadius, y: node.position.y) angularDistance = 0 } } final func calculateCentripetalVelocity() { let normal = CGVector(dx:centripetalPoint.x + CGFloat(cos(self.angularDistance))*centripetalRadius,dy:centripetalPoint.y + CGFloat(sin(self.angularDistance))*centripetalRadius); let period = (CGFloat(M_PI)*2.0)*centripetalRadius/(travelSpeed*invert) self.angularDistance += (CGFloat(M_PI)*2.0)/period*dt; if (self.angularDistance>CGFloat(M_PI)*2) { self.angularDistance = 0 } if (self.angularDistance < 0) { self.angularDistance = CGFloat(M_PI)*2 } node.physicsBody!.velocity = CGVector(dx:(normal.dx-node.position.x)/dt ,dy:(normal.dy-node.position.y)/dt) //Here we check if we are at the tangent angle. Assume 4 degree threshold for error. if abs(maxAngularDistance-angularDistance) < CGFloat(4*M_PI/180) { motionState = .Linear } } final func calculateLinearVelocity() { let disp = CGVector(dx: travelPoint.x-node.position.x, dy: travelPoint.y-node.position.y) let angle = atan2(disp.dy, disp.dx) node.physicsBody!.velocity = CGVector(dx: cos(angle)*travelSpeed, dy: sin(angle)*travelSpeed) //Here we check if we are at the travel point. Assume 15 point threshold for error. if sqrt(disp.dx*disp.dx+disp.dy*disp.dy) < 15 { //We made it to the final position! Code that happens after reaching the point should go here. motionState = .None println("Node finished moving to point!") } } override func update(currentTime: NSTimeInterval) { if motionState == .Centripetal { calculateCentripetalVelocity() } else if motionState == .Linear { calculateLinearVelocity() } } func calculateMaxAngularDistanceOfBestTangent() { let disp = CGVector(dx: centripetalPoint.x - travelPoint.x, dy: centripetalPoint.y - travelPoint.y) let specialCirclePos = CGPoint(x: (travelPoint.x+centripetalPoint.x)/2.0, y: (travelPoint.y+centripetalPoint.y)/2.0) let specialCircleRadius = sqrt(disp.dx*disp.dx+disp.dy*disp.dy)/2.0 let tangentPair = getPairPointsFromCircleOnCircle(centripetalPoint, radiusA: centripetalRadius, pointB: specialCirclePos, radiusB: specialCircleRadius) let tangentAngle1 = (atan2(tangentPair.0.y - centripetalPoint.y,tangentPair.0.x - centripetalPoint.x)+CGFloat(2*M_PI))%CGFloat(2*M_PI) let tangentAngle2 = (atan2(tangentPair.1.y - centripetalPoint.y,tangentPair.1.x - centripetalPoint.x)+CGFloat(2*M_PI))%CGFloat(2*M_PI) if invert == -1 { maxAngularDistance = tangentAngle2 } else { maxAngularDistance = tangentAngle1 } } //Not mine, modified algorithm from https://stackoverflow.com/q/3349125/2158465 func getPairPointsFromCircleOnCircle(pointA: CGPoint, radiusA: CGFloat, pointB: CGPoint, radiusB: CGFloat) -> (CGPoint,CGPoint) { let dX = (pointA.x - pointB.x)*(pointA.x - pointB.x) let dY = (pointA.y - pointB.y)*(pointA.y - pointB.y) let d = sqrt(dX+dY) let a = (radiusA*radiusA - radiusB*radiusB + d*d)/(2.0*d); let h = sqrt(radiusA*radiusA - a*a); let pointCSub = CGPoint(x:pointB.x-pointA.x,y:pointB.y-pointA.y) let pointCScale = CGPoint(x: pointCSub.x*(a/d), y: pointCSub.y*(a/d)) let pointC = CGPoint(x: pointCScale.x+pointA.x, y: pointCScale.y+pointA.y) let x3 = pointC.x + h*(pointB.y - pointA.y)/d; let y3 = pointC.y - h*(pointB.x - pointA.x)/d; let x4 = pointC.x - h*(pointB.y - pointA.y)/d; let y4 = pointC.y + h*(pointB.x - pointA.x)/d; return (CGPoint(x:x3, y:y3), CGPoint(x:x4, y:y4)); } override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) { let touchPos = (touches.first! as! UITouch).locationInNode(self) node = SKShapeNode(circleOfRadius: 10) node.position = CGPoint(x: self.size.width/2.0, y: self.size.height/2.0) node.physicsBody = SKPhysicsBody(circleOfRadius: 10) self.addChild(node) moveToPoint(touchPos) calculateMaxAngularDistanceOfBestTangent() //Expensive! circle.hidden = false circle.position = centripetalPoint } }

Note that the circle you see is another node that I added to the scene to make the movement more noticeable; You can easily remove it. When debugging, you may also find it useful to add nodes to tangent points. The tangentPair tuple inside the calculateMaxAngularDistanceOfBestTangent function contains two tangent points.
Also, note that find the tangent points / corners of the road, but this only happens every time you need to move to a new point. If, however, the game requires a constant transition to a new point, using this algorithm multiple times on many nodes can be expensive (always profile before taking this, though). Another way to check when to move from a centripetal motion to a linear motion is to check if the velocity vector is approaching its final position, as shown below. This is less accurate, but allows you to completely remove the calculateMaxAngularDistanceOfBestTangent function.
let velAngle = atan2(node.physicsBody!.velocity.dy,node.physicsBody!.velocity.dx) let disp = CGVector(dx: travelPoint.x-node.position.x, dy: travelPoint.y-node.position.y) let dispAngle = atan2(disp.dy,disp.dx) //Here we check if we are at the tangent angle. Assume 4 degree threshold for error. if velAngle != 0 && abs(velAngle - dispAngle) < CGFloat(4*M_PI/180) { motionState = .Linear }
Finally, let me know if you need to use paths with SKActions, regardless of whether I think I will update this last part, show how it is done (if someone does not defeat me! And, as I mentioned earlier, the code I posted does this to a certain extent.) I don’t have the time right now, but I hope I get a chance soon! Hope something mentioned in this answer helps you. Good luck with your game.
Update including SKActions
The code below shows the exact same effect, except for this time, using SKActions to animate CGPath to the tangential angle and then to the final destination. This is much simpler since there is no longer any manual calculation of centripetal and linear motion, however, since this is an animation, you lose the dynamic control of the motion in real time, which provides the above solution.
class GameScene: SKScene { var centripetalPoint = CGPoint() //Point to orbit. let centripetalRadius: CGFloat = 60 //Radius of orbit. var travelPoint: CGPoint = CGPoint() //The point to travel to. var travelDuration: NSTimeInterval = 1.0 //The duration of action. var node: SKShapeNode! var circle: SKShapeNode! override func didMoveToView(view: SKView) { physicsWorld.gravity = CGVector(dx: 0, dy: 0) circle = SKShapeNode(circleOfRadius: centripetalRadius) circle.strokeColor = SKColor.redColor() circle.hidden = true self.addChild(circle) } //Not mine, modified algorithm from https://stackoverflow.com/q/3349125/2158465 func getPairPointsFromCircleOnCircle(pointA: CGPoint, radiusA: CGFloat, pointB: CGPoint, radiusB: CGFloat) -> (CGPoint,CGPoint) { let dX = (pointA.x - pointB.x)*(pointA.x - pointB.x) let dY = (pointA.y - pointB.y)*(pointA.y - pointB.y) let d = sqrt(dX+dY) let a = (radiusA*radiusA - radiusB*radiusB + d*d)/(2.0*d); let h = sqrt(radiusA*radiusA - a*a); let pointCSub = CGPoint(x:pointB.x-pointA.x,y:pointB.y-pointA.y) let pointCScale = CGPoint(x: pointCSub.x*(a/d), y: pointCSub.y*(a/d)) let pointC = CGPoint(x: pointCScale.x+pointA.x, y: pointCScale.y+pointA.y) let x3 = pointC.x + h*(pointB.y - pointA.y)/d; let y3 = pointC.y - h*(pointB.x - pointA.x)/d; let x4 = pointC.x - h*(pointB.y - pointA.y)/d; let y4 = pointC.y + h*(pointB.x - pointA.x)/d; return (CGPoint(x:x3, y:y3), CGPoint(x:x4, y:y4)); } func moveToPoint(point: CGPoint) { travelPoint = point //Assume clockwise when point is to the right. Else counter-clockwise if point.x > node.position.x { centripetalPoint = CGPoint(x: node.position.x + centripetalRadius, y: node.position.y) } else { centripetalPoint = CGPoint(x: node.position.x - centripetalRadius, y: node.position.y) } let disp = CGVector(dx: centripetalPoint.x - travelPoint.x, dy: centripetalPoint.y - travelPoint.y) let specialCirclePos = CGPoint(x: (travelPoint.x+centripetalPoint.x)/2.0, y: (travelPoint.y+centripetalPoint.y)/2.0) let specialCircleRadius = sqrt(disp.dx*disp.dx+disp.dy*disp.dy)/2.0 let tangentPair = getPairPointsFromCircleOnCircle(centripetalPoint, radiusA: centripetalRadius, pointB: specialCirclePos, radiusB: specialCircleRadius) let tangentAngle1 = (atan2(tangentPair.0.y - centripetalPoint.y,tangentPair.0.x - centripetalPoint.x)+CGFloat(2*M_PI))%CGFloat(2*M_PI) let tangentAngle2 = (atan2(tangentPair.1.y - centripetalPoint.y,tangentPair.1.x - centripetalPoint.x)+CGFloat(2*M_PI))%CGFloat(2*M_PI) let path = CGPathCreateMutable() CGPathMoveToPoint(path, nil, node.position.x, node.position.y) if travelPoint.x > node.position.x { CGPathAddArc(path, nil, node.position.x+centripetalRadius, node.position.y, centripetalRadius, CGFloat(M_PI), tangentAngle2, true) } else { CGPathAddArc(path, nil, node.position.x-centripetalRadius, node.position.y, centripetalRadius, 0, tangentAngle1, false) } CGPathAddLineToPoint(path, nil, travelPoint.x, travelPoint.y) let action = SKAction.followPath(path, asOffset: false, orientToPath: false, duration: travelDuration) node.runAction(action) } override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) { let touchPos = (touches.first! as! UITouch).locationInNode(self) node = SKShapeNode(circleOfRadius: 10) node.position = CGPoint(x: self.size.width/2.0, y: self.size.height/2.0) self.addChild(node) moveToPoint(touchPos) circle.hidden = false circle.position = centripetalPoint } }