Progress so far:
So what I have at the moment is this:
(the green point represents the parent "BlankNode, adding children then rotating them around that node,
Im a bit stick how to get it work properly, for some reason they dont sit next to eachother but opposite (as showen in http://i.stack.imgur.com/w7QvS.png)
inGameLevel
myArc = [[Arcs alloc]initWithArcCount:myAmmountOfSprites];
[self addChild:myArc];
My wish is for the sprite.rotation to be slightly offset from the next loaded...here they are split...
(The diagram belows showing the arc shape I would like to load the sprites in)
**With one stick loaded, maybe its easier to spot the mistake
(if I load a second sprite it loads directly opposite to the previous and not at the expected angle incremented
In this version I have just loaded the stick and blanknode, positioned it using anchor points, Im confused how the rotation works... **
SKSpriteNode *blank = [[SKSpriteNode alloc]
///like the otherone
blank.zRotation=0;
blank.anchorPoint = CGPointMake(0.5, 0.5);
[self addChild:blank];
//set to 0 value so I can see what its natural state is (it is vertical and above the parent node)
//but this value will be incremented each time a new sprite is added
int rotationAmount = 0;
Rotation = Rotation-rotationAmount; //will increment
objectPic = [SKSpriteNode spriteNode....as normal
//use blank nodes anchorpoint
objectPic.anchorPoint = blank.anchorPoint;
//Rotation
objectPic.zRotation = Rotation;
float moveUp_donut = 0.3;
//"moveUp_donut" moving this value up moves the stick up
//and outward from the center
objectPic.anchorPoint =
CGPointMake(0.0,-moveUp_donut); //(0.0,-moveOutward);
[blank addChild:objectPic];
}
}
I have made an xcode project available for anyone interested to have a look at the problem, hopefully you can explain how to get the rotation working correctly.
at the moment it is just loading one sprite, so you might need to play with the setting,
myArc = [[Arcs alloc]initWithArcCount:addLotsOfSticks];
//and play with the rotation ammount
int rotationAmount = 3;
http://www.filedropper.com/rotationtest
Solution Found! see below:
🌸
A huge thanks to WangYudong for giving such a great answer!
I made a sample project and hope it can help. The algorithm is not base on your project, so make some change to fit your need.
Firstly, add a blank node to the middle of the scene:
self.blank = [[SKSpriteNode alloc] initWithColor:[SKColor greenColor]size:CGSizeMake(20, 20)];
self.blank.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
[self addChild:self.blank];
Then, create the stick:
- (SKSpriteNode *)newStick
{
SKSpriteNode *stick = [[SKSpriteNode alloc] initWithColor:[SKColor redColor]size:CGSizeMake(5, 100)];
return stick;
}
And given the amount of sticks, the radius (of the inner circle), the starting radian and ending radian, add a method:
- (void)loadStickArcWithStickAmount:(NSUInteger)amount radius:(CGFloat)radius startRadians:(CGFloat)startRad endRadians:(CGFloat)endRad
{
for (NSUInteger index = 0; index < amount; index++) {
SKSpriteNode *stick = [self newStick];
CGFloat halfStickLength = stick.size.height / 2;
CGFloat rotateRad = startRad + (endRad - startRad) / (amount - 1) * index;
stick.zRotation = M_PI_2 + rotateRad;
stick.position = CGPointMake((radius + halfStickLength) * cos(rotateRad),
(radius + halfStickLength) * sin(rotateRad));
[self.blank addChild:stick];
}
}
Some hints:
rotateRad divides radians of endRad - startRad.
M_PI_2 is an offset of zRotation.
Trigonometric maths calculates the position of sticks.
Both anchor points of blank node and stick remain default (0.5, 0.5).
Use the method:
[self loadStickArcWithStickAmount:27 radius:50.0 startRadians:M_PI endRadians:2*M_PI];
to achieve the following result:
Related
So for the App I'm working on, I have two cubes colliding. I check for this in the standard way. The app tells me when they collide in my "didBeginContact" method.
-(void)didBeginContact:(SKPhysicsContact *)contact {
if (contact.bodyA.categoryBitMask == WALL_CATEGORY && contact.bodyB.categoryBitMask == CHARACTER_CATEGORY) {
CGPoint point = contact.contactPoint;
}
}
So i know where the collision takes place, but because it is two squares it can be at any point along the side including the corners. So how would I check if the collision on the left/right/top/bottom exclusively?
Edit: Correct Answer: Might not be the cleanest way to do it but it works. Hopefully it'll help someone in the future.
m_lNode = [SKNode node];
m_lNode.position = CGPointMake(-(CHARACTER_SIZE / 2), 0);
m_lNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(1, m_character.size.height)];
m_lNode.physicsBody.allowsRotation = NO;
m_lNode.physicsBody.usesPreciseCollisionDetection = YES;
m_lNode.physicsBody.categoryBitMask = CHARACTER_L_CATEGORY;
m_rNode = [SKNode node];
m_rNode.position = CGPointMake((CHARACTER_SIZE / 2), 0);
m_rNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(1, m_character.size.height)];
m_rNode.physicsBody.allowsRotation = NO;
m_rNode.physicsBody.usesPreciseCollisionDetection = YES;
m_rNode.physicsBody.categoryBitMask = CHARACTER_R_CATEGORY;
m_tNode = [SKNode node];
m_tNode.position = CGPointMake(0, (CHARACTER_SIZE / 2));
m_tNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(m_character.size.width , 1)];
m_tNode.physicsBody.allowsRotation = NO;
m_tNode.physicsBody.usesPreciseCollisionDetection = YES;
m_tNode.physicsBody.categoryBitMask = CHARACTER_T_CATEGORY;
m_bNode = [SKNode node];
m_bNode.position = CGPointMake(0, -(CHARACTER_SIZE / 2));
m_bNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(m_character.size.width, 1)];
m_bNode.physicsBody.allowsRotation = NO;
m_bNode.physicsBody.usesPreciseCollisionDetection = YES;
m_bNode.physicsBody.categoryBitMask = CHARACTER_B_CATEGORY;
[m_character addChild:m_tNode];
[m_character addChild:m_bNode];
[m_character addChild:m_lNode];
[m_character addChild:m_rNode];
-(void)didBeginContact:(SKPhysicsContact *)contact {
if (contact.bodyA.categoryBitMask == WALL_CATEGORY) {
switch (contact.bodyB.categoryBitMask) {
case CHARACTER_T_CATEGORY:
NSLog(#"Top");
m_isHitTop = true;
break;
case CHARACTER_B_CATEGORY:
NSLog(#"Bottom");
m_isHitBottom = true;
break;
case CHARACTER_L_CATEGORY:
NSLog(#"Left");
m_isHitLeft = true;
break;
case CHARACTER_R_CATEGORY:
NSLog(#"Right");
m_isHitRight = true;
break;
}
}
}
Added some relevant code. It's my code so there is variables amongst other things but you should be able to figure it out.
I think the best way to determine what side is involved in your contact (top,left,bottom,right side) is to:
calculate the points leaved by a centering cross for up, down, left and right sides (for example if you have a squared sprite..)
let halfWidth = self.frame.width/2
let halfHeight = self.frame.height/2
let down = CGPointMake(self.frame.origin.x+halfWidth,self.frame.origin.y)
let up = CGPointMake(self.frame.origin.x+halfWidth,self.frame.origin.y+self.frame.size.height)
let left = CGPointMake(self.frame.origin.x,self.frame.origin.y+halfHeight)
let right = CGPointMake(self.frame.origin.x+self.frame.size.width,self.frame.origin.y+halfHeight)
Calculate the distance between the contactPoint and each point sides (up, left , right and down)
This step can be possible with this little function:
func getDistance(p1:CGPoint,p2:CGPoint)->CGFloat {
let xDist = (p2.x - p1.x)
let yDist = (p2.y - p1.y)
return CGFloat(sqrt((xDist * xDist) + (yDist * yDist)))
}
After, the point involved with the lowest distance calculated is the side point nearest the contactPoint.
What are the advantages of this method:
You don't paint zombies or ghost nodes and relative physic bodies
This method work also inside a dynamic mutable CGPath, not only with a
knowed rectangular boundaries
Is fast, few lines of code and if you add other few lines you can be
able to determine also diagonals and make more precise your
algorithm.
the easiest way is to add child sprites the top, left, right, bottom of your squares. Add physics bodies to those, and then you can tell where things are colliding. I'd recommend trying this method first unless you have many many squares on the screen.
If you have a ton of squares on the screen and youre worried about performance. then maybe use contact.contactPoint and convert that point to square coordinates. given the center point of the square, the angle of the squares rotation and that point, you should be able to tell where the square collided. That would require some math.. and I was afraid writing up that kind of solution when the first one may be all that you really need.
Two likely related things here:
1) I can draw a box and add to child from my SKScene impelmentation file with self, self.scene, and myWorld, but not with an SKSprite node's scene property.
SKSpriteNode *bla = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(100, 100)];
[self.scene addChild:bla]; // If I use bla.scene, it doesn't work. self, self.scene, myworld all do work though.
bla.position = CGPointMake(0, 0);
bla.zPosition = 999;
2) I've seen the related questions here and here, but I'm trying to add a joint during gameplay (grabbing a rope). This method gets called after doing some sorting in `didBeginContact:
-(void)monkey:(SKPhysicsBody *)monkeyPhysicsBody didCollideWithRope:(SKPhysicsBody *)ropePhysicsBody atPoint:(CGPoint)contactPoint
{
if (monkeyPhysicsBody.joints.count == 0) {
// Create a new joint between the player and the rope segment
CGPoint convertedRopePosition = [self.scene convertPoint:ropePhysicsBody.node.position fromNode:ropePhysicsBody.node.parent];
SKPhysicsJointPin *jointPin = [SKPhysicsJointPin jointWithBodyA:playerPhysicsBody bodyB:ropePhysicsBody anchor:convertedRopePosition];
jointPin.upperAngleLimit = M_PI/4;
jointPin.shouldEnableLimits = YES;
[self.scene.physicsWorld addJoint:jointPin];
}
}
I've got showPhyiscs enabled on the scene, so I can see that the joint is ending up in a totally wacky place. Unfortunately, I don't know how to apply the linked solutions since I'm not adding the SKSpriteNodes in this method, they already exist, so I can't just flip the order of position and physicsBody.
I've tried every permutation I could for both of the convertPoint methods. Nothing worked. My best guess is that physicsWorld is using some wacky coordinate system.
Method members of SKPhysicsWorld that relate to position (CGPoint) or frame (CGRect) are to be in scene coordinates. Scene coordinates reference the point {0,0} as the bottom left corner and is consistent throughout SpriteKit.
The scene property of your object named bla will be nil when bla is first created and is set by the scene when added to it.
[bla.scene addChild:bla]; // this won't do anything as scene will be nil when first created
It looks as though convertedRopePosition is being assigned an incorrect value because the second member you're passing into, - (CGPoint)convertPoint:(CGPoint)point fromNode:(SKNode *)node , is the scene when it should be another node in the same node tree as this node. where this node is the caller (in this case the SKScene subclass).
Try replacing the line-
CGPoint convertedRopePosition = [self.scene convertPoint:ropePhysicsBody.node.position fromNode:ropePhysicsBody.node.parent];
with-
CGPoint convertedRopePosition = [self convertPoint:ropePhysicsBody.node.position fromNode:ropePhysicsBody.node];
I came up with a janky work around for this problem. It turns out that the coordinate system offset for physicsWorld was likely due to an anchor difference. Changing the anchors of every related thing made no difference, and you can't change the anchor of the physicsWorld directly, so I ended up adding half of the scene width and half of the scene height to the anchor position of my joint. That got it to show in the right place and behave normally.
This problem persisted once side scrolling was factored in. I've posted other questions here with this same problem but I'll include th
I added the following convenience method to my GameScene.m. It essentially takes the place of the seemingly useless convertTo built in method.
-(CGPoint)convertSceneToFrameCoordinates:(CGPoint)scenePoint
{
CGFloat xDiff = myWorld.position.x - self.position.x;
CGFloat yDiff = myWorld.position.y - self.position.y;
return CGPointMake(scenePoint.x + self.frame.size.width/2 + xDiff, scenePoint.y + self.frame.size.height/2 + yDiff);
}
I use this method to add joints. It handles all of the coordinate system transformations that need to be dealt with that lead to the issue raised in this question. For example, the way I add joints
CGPoint convertedRopePosition = [self convertSceneToFrameCoordinates:ropePhysicsBody.node.position];
SKPhysicsJointPin *jointPin = [SKPhysicsJointPin jointWithBodyA:monkeyPhysicsBody bodyB:ropePhysicsBody anchor:convertedRopePosition];
jointPin.upperAngleLimit = M_PI/4;
jointPin.shouldEnableLimits = YES;
[self.scene.physicsWorld addJoint:jointPin];
I got 5 objects with their images. I add them to array and shuffle them.
// Adding Things to Things Strip
[thingStrip removeAllObjects];
for (Thing *i in myThings) {
[self addThing:[i icon]];
}
// Randomizing Things' order in the Things Strip
for (int x = 0; x<thingStrip.count; x++) {
int randInt = (arc4random() % (thingStrip.count - x)) + x;
[thingStrip exchangeObjectAtIndex:x withObjectAtIndex:randInt];
}
// Adding Things' Images
for (int i=0; i<thingStrip.count; i++) {
UIImage *thingImage = thingStrip[i];
Then I add them to UIImage and create CALayers with them.
All I need is:
Make them move from right to left continuously without any gaps.
Scale them smoothly from 80x53 at right position to 120x80 at left position
Make opacity from 0.5 at right position to 1.0 at left position.
Each iteration lasts 0.5 seconds.
Whole animation lasts 5 seconds or stop on screen touch.
After animation is stopped I need left object proceed to the left end of the screen.
After animation is stopped I need to know which object image stopped at left end of the screen to use its' object data later.
For now I can do moving animation, but my solution is not so good.
// Adding Things' Images
for (int i=0; i<thingStrip.count; i++) {
UIImage *thingImage = thingStrip[i];
//for (int j = 0; j < 10; j++)
//{
CALayer *thingFrame = [CALayer layer];
thingFrame.bounds = CGRectMake(0.0, 0.0, 80.0, 53.0);
thingFrame.position = CGPointMake(i*thingFrame.frame.size.width, 515.0);
//thingFrame.position = CGPointMake(i*thingFrame.frame.size.width + j * thingStrip.count * thingFrame.frame.size.width, 515.0);
thingFrame.contents = (id)thingImage.CGImage;
[thingFrame setAnchorPoint:CGPointMake(0.0, 0.0)];
[self.view.layer addSublayer:thingFrame];
CGFloat currentPosition = [[thingFrame valueForKeyPath:#"position.x"] floatValue];
CGFloat newPosition = currentPosition - thingStrip.count * thingFrame.frame.size.width;
CABasicAnimation *thingMoveAnimation = [CABasicAnimation animationWithKeyPath:#"position.x"];
thingMoveAnimation.duration = 0.5f;
thingMoveAnimation.repeatDuration = 5.0f;
thingMoveAnimation.delegate = self;
thingMoveAnimation.fromValue = #(currentPosition);
thingMoveAnimation.toValue = #(newPosition);
thingMoveAnimation.fillMode = kCAFillModeForwards;
[thingFrame addAnimation:thingMoveAnimation forKey:#"position.x"];
//}
}
This code works only if I use for loop with 'j' (commented one) which adds 5 of each image. Only then I get smooth moving animation without any gaps. I guess it's not right solution. When I use only one for loop, I get animation of 5 images moving left again and again, but not continuously one after another.
I don't have working solution for scaling and opacity animation and I don't know how to end it by touching the screen and get needed image data.
I was trying to find similar solution for a while but found nothing.
I'm looking for help with working code.
Thanks in advance.
I started working with Sprite kit and I Was wondering how can I create an infinite side scrolling game? I read the Sprite kit documentation and I read through the pre-processing of the scene. It's stated that we can readjust the scene's position in case the content is larger then the scene. I tried it and it works however, when I scroll through out the entire background image, I start seeing the default background of the scene. How can I create the infinite background? Can anybody point me to the right documentation or articles that talk about his issue? Thank you.
Something like this should get you started:
Add the background images...
for (int i = 0; i < 2; i++) {
SKSpriteNode * bg = [SKSpriteNode spriteNodeWithImageNamed:#"background"];
bg.anchorPoint = CGPointZero;
bg.position = CGPointMake(i * bg.size.width, 0);
bg.name = #"background";
[self addChild:bg];
}
In your update method.
[self enumerateChildNodesWithName:#"background" usingBlock: ^(SKNode *node, BOOL *stop) {
SKSpriteNode *bg = (SKSpriteNode *) node;
bg.position = CGPointMake(bg.position.x - 5, bg.position.y);
if (bg.position.x <= -bg.size.width) {
bg.position = CGPointMake(bg.position.x + bg.size.width * 2, bg.position.y);
}
}];
This is from an example in RW his example used background image size of 1136 px.
I did a generic composant called SKScrollingNode for this purpose as I usually add multiple scrolling backgrounds to achieve a parallax effect. Using it is pretty straightforward :
Declare it in your scene class
#property(strong,nonatomic) SKScrollingNode * clouds;
Create it and add it to the scene instance
self.clouds = [SKScrollingNode spriteNodeWithImageNamed:#"clouds"];
self.clouds.scrollingSpeed = .5; // Speed at which the clouds will scroll
[self addChild:clouds];
And in you scene 'update' method, just call
[self.clouds update:currentTime]
Done !
You can find the full code for the 'SKScrollinNode' composant here :
https://github.com/kirualex/SprityBird/blob/master/spritybird/Classes/Scenes/
My code example for this exact reference using SpriteKit and Swift is seen below.
func background1() {
let backgroundTexture = SKTexture(imageNamed: "bg")
//move background right to left; replace
let shiftBackground = SKAction.moveByX(-backgroundTexture.size().width, y: 0, duration: 15)
let replaceBackground = SKAction.moveByX(backgroundTexture.size().width, y:0, duration: 0)
let movingAndReplacingBackground = SKAction.repeatActionForever(SKAction.sequence([shiftBackground,replaceBackground]))
for var i:CGFloat = 0; i<3; i++ {
//defining background; giving it height and moving width
let background = SKSpriteNode(texture:backgroundTexture)
background.position = CGPoint(x: backgroundTexture.size().width/2 + (backgroundTexture.size().width * i), y: CGRectGetMidY(self.frame))
background.size.height = self.frame.height
background.zPosition = -20
background.runAction(movingAndReplacingBackground)
self.addChild(background)
create constant backgroundTexture created as an SKTexture with your image.
create constants for moving your background to the left (shiftBackground) and for adding another instance of your background to the right side of your currently displayed one (replaceBackground)
create constant (movingAndReplacingBackground) as an SKAction that repeats forever, setting the parameter of function repeatActionForever to a SKAction.sequence with your shiftBackground and replaceBackground constants referenced.
create a loop that defines the definitions for your background. (for variable i, if amount of i is less than 3, increment i by one.
in the loop, defend the texture, position, size, and zPosition (if sibling order is turned off) and then call your movingAndReplacingBackground as your action, which then iterates your sequence.
finally, the last part of the loop is adding your background. It will perform this action any time there is less than 3 iterations of it. and as soon as your first background reaches far left of screen, it will remove the nodes to 2, therefore running the action in the loop over again.
I wrapped this in a function because for a parallax background, I was able to copy this function 4 times, rename the variables, change the "duration" for the shiftBackground constant, and get different speeds based on their distance.
I hope this helps! When Swift goes to Swift 3 this "C-Style" for loop will be deprecated, so I'm not sure how to fix that yet for the long term.
I've been working on a library for an infinite tile scroller, hopefully could be the starting point for a side scroller:
RPTileScroller
It's using a tableView-like delegate, so I hope is really easy to provide tiles, or any kind of node, to the scroller. I don't have any collision logic implemented yet, but I am planning on adding that also.
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?
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.
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:
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;
}
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:
Official document about Sumulating Physics
How to Make Hanging Chains With SpriteKit Physis Joints