Ben-G
Ben-G

Reputation: 5026

Is it possible to rotate a Node around an arbitrary point in SpriteKit?

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?

Upvotes: 20

Views: 7824

Answers (4)

Ryan W
Ryan W

Reputation: 6173

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:

Upvotes: 0

theideasmith
theideasmith

Reputation: 2925

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;
}

Upvotes: 0

theideasmith
theideasmith

Reputation: 2925

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:

Upvotes: 0

CodeSmile
CodeSmile

Reputation: 64477

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.

Upvotes: 29

Related Questions