20

Is there a way to rotate a Node in SpriteKit around an arbitrary point? I now I can manipulate the anchorPoint of my Node, but that is not sufficient if the rotation point I want to use lies outside of the Node.

enter image description here

What is the best way to achieve this kind of rotation in SpriteKit?

Ben-G
  • 4,996
  • 27
  • 33
  • Is it not possible to set the anchor point to some value greater than (1, 1) to rotate around that outer point? – rizzes Dec 07 '14 at 23:51

4 Answers4

29

Since you're asking for the best way, here's one that works well (best is subjective):

Create an SKNode and set its position to the center of rotation. Add the node that should rotate around that center as child to the center node. Set the child node's position to the desired offset (ie radius, say x + 100). Change the rotation property of the center node to make the child node(s) rotate around the center point. The same works for cocos2d btw.

CodeSmile
  • 64,284
  • 20
  • 132
  • 217
  • Thanks! Did this the job. If I had overlooked an existing API capability for exact this use case I would have called using that the 'best way'. Everything that is outside of API usage for exact that use case, I agree, is subjective. However, I like the solution and it works ;) – Ben-G Oct 10 '13 at 15:53
0

I was also trying to solve this problem a few weeks back, and did not implement the anchor points solution because I did not want to have to worry about removing the anchor point when lets say the object collides with another node and should leave its orbit and bounce away.

Instead, I came up with two solutions, both of which work if tweaked. The first took a long time to perfect, and is still not perfect. It involves calculating a certain number of points around a center position offset by a set radius, and then if a certain object comes in a certain distance of the center point, it will continually use physics to send the object on a trajectory path along the "circumference" of the circle, points that it calculated (see above).

There are two ways of calculating points with a radius

The first uses the pythagorean theorem, and the second ultimately uses trigonometry proper. In the first, you increment a for loop by a certain amount, while it is less that 361 (degree), and for each iteration of the loop, calculate using sine and cosine a point with that angle at a certain radius from the center point.

The second uses the pythagorean theorem, and its code is below:

After you calculate points, you should create a scheduled selector [<object> scheduled selector...]; or a timer in your didMoveToView, or use a fixed update method, in addition to an instance variable called int which will hold the index of the next location to which your object will move. Every time the timer method is called, it will move the object to the next point in your calculate points array using your own or the below code labeled physicsMovement; You can play around with the physics values, and even the frequency of the ttimer for different movement effects. Just make sure that you are getting the index right. Also, for more realism, I used a method which calculates the closest point in the array of calculated point to the object, which is called only once the collision begins. It is also below labeled nearestPointGoTo. If you need any more help, just say so in the comments. Keep Hacking!

I used the second, and here is the source code for it:

Community
  • 1
  • 1
theideasmith
  • 2,835
  • 2
  • 13
  • 20
0

The code itself didn't go through

Second point calculation option

+(NSArray *)calculatePoints:(CGPoint)point withRadius:(CGFloat)radius numberOfPoints:    (int)numberOfPoints{ //or sprite kit equivalent thereof 
//                [drawNode clear];


NSMutableArray *points = [[NSMutableArray alloc]init];
for (int j = 1; j < 5; j++) {
    float currentDistance;
    float myRadius = radius;
    float xAdd;
    float yAdd;
    int xMultiplier;
    int yMultiplier;
    CCColor *color = [[CCColor alloc]init]; //Will be used later to draw the position of the node, for debugging only 
    for (int i = 0; i < numberOfPoints; i += 1){ 
        //You also have to change the if (indextogoto == <value>) in the moveGumliMethod;
    float opposite = sqrtf( powf(myRadius, 2) - powf(currentDistance, 2) );
    currentDistance = i;

    switch (j) {
        case 1:
            xMultiplier = 1;
            yMultiplier = 1;
            xAdd = currentDistance;
            yAdd = opposite;
            color = [CCColor blueColor];
            break;
        case 2:
           xMultiplier = 1;
            yMultiplier = -1;
            xAdd = opposite;
            yAdd = currentDistance;
            color = [CCColor orangeColor];
            break;
        case 3:
            xMultiplier = -1;
            yMultiplier = -1;
            xAdd = currentDistance;
            yAdd = opposite;
            color = [CCColor redColor];
            break;
        case 4:
            xMultiplier = -1;
            yMultiplier = 1;
            xAdd = opposite;
            yAdd = currentDistance;
            color = [CCColor purpleColor];
            break;
        default:
            break;
    }
   int x = (CGFloat)(point.x + xAdd * xMultiplier); //or sprite kit equivalent thereof 
   int y = (CGFloat)(point.y + yAdd * yMultiplier); //or sprite kit equivalent thereof 
   CGPoint newPoint = CGPointMake((CGFloat)x,(CGFloat)y); //or sprite kit equivalent thereof 


   NSValue *pointWrapper = [NSValue valueWithCGPoint:newPoint]; //or sprite kit equivalent thereof 
   NSLog(@"Point is %@",pointWrapper);
  [points addObject:pointWrapper];
    }
}
return points;
}

Calculating Nearest Point To Object

-(CGPoint)calculateNearestGumliPoint:(CGPoint)search point { // MY Character is named    Gumli
    float closestDist = 2000;
    CGPoint closestPt = ccp(0,0);
    for (NSValue *point in points) {
        CGPoint cgPoint = [point CGPointValue];
        float dist = sqrt(pow( (cgPoint.x - searchpoint.x), 2) + pow( (cgPoint.y - searchpoint.y), 2));
    if (dist < closestDist) {
        closestDist = dist;
        closestPt = cgPoint;

        }
    }

    return closestPt;
}
theideasmith
  • 2,835
  • 2
  • 13
  • 20
0

I think the best way to make this work is through two SKNode and joint them with SKPhysicsJointPin (Look at the pin example below)

enter image description here

I tried to hang a door sign (SKSpriteNode) on my door(`SkScene), and would like to rotate around on the hanging spot when someone touch it

What I did is making a 1x1 SKNode with a HUGH mass and disabled it's gravity effects.

var doorSignAnchor = SKSpriteNode(color: myUIColor, size: CGSize(width: 1, height: 1))
doorSignAnchor.physicsBody = SKPhysicsBody(rectangleOf: doorSignAnchor.frame.size)
doorSignAnchor.physicsBody!.affectedByGravity = false // MAGIC PART
doorSignAnchor.physicsBody!.mass = 9999999999 // MAGIC PART

var doorSignNode = SKSpriteNode(imageNamed:"doorSign")
doorSignNode.physicsBody = SKPhysicsBody(rectangleOf: doorSignNode.frame.size)

and created a SKPhysicsJointPin to connect them all

let joint = SKPhysicsJointPin.joint(
      withBodyA:  doorSignAnchor.physicsBody!,
      bodyB: doorSignNode.physicsBody!,
      anchor: doorSignAnchor.position)
mySkScene.physicsWorld.add(joint)

So it will move like actual door sign, rotate around an arbitrary point (doorSignAnchor)

Reference:

Ryan Wu
  • 5,963
  • 2
  • 36
  • 47