Reputation: 5026
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.
What is the best way to achieve this kind of rotation in SpriteKit?
Upvotes: 20
Views: 7824
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)
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
Reputation: 2925
The code itself didn't go through
+(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;
}
-(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
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).
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
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