SpriteKit - Moving object in a semi circle arch

animation, ios, objective-c, path, sprite-kit

Solution

OK so there's a cool little `SKAction` (I'm sure you're familiar with that class) which allows a `SKSpriteNode` to follow a path:

SKAction *move = [SKAction followPath:/*aPath*/ duration:1];
[myNode runAction:action];
// There's also a followPath:withSpeed: method which may be more suitable for you

All you need for this is the path itself and for this you can use `UIBezierPath`. `UIBezierPath` is the objective-c class that allows you to define paths with can then be drawn on a view in the `drawRect:` method. They also can be converted to `CGPathRef`s which can then be followed by a node.

All an arc is is a part of a circle, and there's a method of `UIBezierPath` designed to define arcs: `arcWithCenter:radius:startAngle:endAngle:clockwise:`.

So... lets assume that your node is at point 100,200 in a rect of 200,200 )(i.e. right in the centre of the x but at the top of the y). You want this node to follow an arc from where it is to point 0,100:

Essentially what you are doing here is moving the node a quarter of a circle that looks like this:

In this case the circle has a radius of 100 and 0 degrees is found at 3 o'clock. Also it is worth noting that for a `UIBezierPath` angles are measured in radians not degrees. To convert a degree to radians you can use this simple formula: `float radian = degree * M_PI / 180.0f;` where `M_PI` is pi.

Therefore to create a path from point 100,200 to 0,100 and apply it to an `SKAction` you can do this:

UIBezierPath *path = [UIBezierPath pathWithArcCenter:CGPointMake(100,100), radius:100 startAngle:90.0f * M_PI / 180.0f endAngle:180.0f * M_PI / 180.0f clockwise:NO];
SKAction *moveInArc = [SKAction followPath:path.CGPath duration:1];

In terms of moving to different positions mid arc, you can associate `SKAction` with keys like an `NSDictionary` and then remove them before they're complete which terminates the action.

For arcing when tapped you need to override `touchesBegan:withEvent:`.

Hope this gets you started on the right path. (Pun intended...)

EDIT:

OK so the above code defines a path that draws like a slice of the circle like a slice of a pie chart, however, you can use the `UIBezierPath` method

- (void)addCurveToPoint:(CGPoint)endPoint
          controlPoint1:(CGPoint)controlPoint1
          controlPoint2:(CGPoint)controlPoint2

or

- (void)addQuadCurveToPoint:(CGPoint)endPoint
               controlPoint:(CGPoint)controlPoint

the latter may be the most appropriate for you. Here is a link to the docs.

Your code would then look something like this:

UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:node.positon];
[path addQuadCurveToPoint:destination controlPoint:control];

SKAction *move = [SKAction followPath:path.CGPath duration:1];
[node runAction:move];

To calculate the control point, you could just experiment until you get the desired effect (perhaps a right angle triangle where the line from the node to the destination is the hypotenuse and the control point is the point with the right angle), or here is a brief explanation of quadratic curves.

Problem

I'm relatively new to sprite kit. What I would like to achieve; - I have an object that's placed in the middle of the screen - When tapping the left side of the screen I would like the object to curve to the left in an arch fashion. If you imagine the object sitting at 12 on a clock face, pressing left will make it animate over to 9 o'clock. - When tapping right, I would like the object to move to 3 o'clock. - However, if the object has been arched over to a position between 9 and 12 (10 or 11), and I press right, it'll need to move across the path. - In summary, I would like a path that spans between 9 and 3 o clock. When the user taps left or right, the object will move along the path until the user lets go of the button (left or right side of the screen) If there's any additional information I can provide to help make this clearer, please let me know. As always, super grateful for any responses :)

Original source