Simple pin joint in SpriteKit doesn't work as expected - ios

I'd like to make a pendulum. Starting with an SKScene and everything defaulted, I do the following...
- (void)createSceneContents {
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.view.bounds];
// object 1 is the fulcrum
SKSpriteNode *object1 = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(5, 5)];
[self addChild:object1];
object1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:object1.frame.size];
object1.physicsBody.dynamic = NO;
object1.position = self.view.center;
// object2 is like a broomstick, which I will pin to the fulcrum
SKSpriteNode *object2 = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake(90, 2)];
[self addChild:object2];
object2.anchorPoint = CGPointMake(0.0, 0.5);
object2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:object2.frame.size];
object2.position = self.view.center;
// pin the physics bodies
SKPhysicsJointPin *pinJoint = [SKPhysicsJointPin jointWithBodyA:object1.physicsBody bodyB:object2.physicsBody anchor:object1.position];
[self.physicsWorld addJoint:pinJoint];
}
If I don't add the pin logic, as expected the fulcrum stays in the middle of the scene and the broomstick falls to the floor, so I know the broomstick is subject to gravity. After adding the pin, I get this:
No motion. Why? This causes no motion either...
[object2.physicsBody applyForce:CGVectorMake(0, -5)];
I expect the broomstick to swing and oscillate because it's pinned to the fulcrum. I've seen articles about positioning the nodes first, before joints, but I've done that. Am I wrong to expect the broom to swing? How can I get it to do so?

Node 2 have bad defined anchorPoint, here an upload an example:
Full code of example
Image:
Swift 3 code:
let nodeSize = CGSize(width: 10, height: 10)
let node = SKSpriteNode(color: .red, size: nodeSize)
node.physicsBody = SKPhysicsBody(rectangleOf: nodeSize)
node.physicsBody?.isDynamic = false
self.addChild(node)
let node2Size = CGSize(width: 60, height: 8)
let node2 = SKSpriteNode(color: .green, size: node2Size)
node2.position = CGPoint(x: 30, y: 0)
node2.physicsBody = SKPhysicsBody(rectangleOf: node2Size)
node2.physicsBody?.mass = 1.0
self.addChild(node2)
let a = SKPhysicsJointPin.joint(withBodyA: node.physicsBody! , bodyB: node2.physicsBody!, anchor: CGPoint(x: 0.0, y: 0.0))
self.physicsWorld.add(a)
Objective-C:
SKSpriteNode *object1 = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(5, 5)];
[self addChild:object1];
object1.position = self.view.center;
object1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:object1.frame.size];
object1.physicsBody.dynamic = NO;
SKSpriteNode *object2 = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake(90, 2)];
[self addChild:object2];
object2.position = CGPointMake(self.view.center.x+45, self.view.center.y);
object2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:object2.frame.size];
SKPhysicsJointPin *pinJoint = [SKPhysicsJointPin jointWithBodyA:object1.physicsBody bodyB:object2.physicsBody anchor:self.view.center];
[self.physicsWorld addJoint:pinJoint];

Thanks to #Maetschl, I was able to make it work in Objective-c as follows (removing the anchor point and just positioning the swinging node to one edge)...
SKSpriteNode *object1 = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(5, 5)];
[self addChild:object1];
object1.position = self.view.center;
object1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:object1.frame.size];
object1.physicsBody.dynamic = NO;
SKSpriteNode *object2 = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake(90, 2)];
[self addChild:object2];
object2.position = CGPointMake(self.view.center.x+45, self.view.center.y);
object2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:object2.frame.size];
SKPhysicsJointPin *pinJoint = [SKPhysicsJointPin jointWithBodyA:object1.physicsBody bodyB:object2.physicsBody anchor:self.view.center];
[self.physicsWorld addJoint:pinJoint];

Related

Scaling a pendulum made with SKSpriteNodes

I've got a working clock with a pendulum, but the swinging arm does not scale correctly. Here's what it looks like before scaling...
And then, after scaling...
The clock face scales down, and so does the swing arm (the green line), but it looks like it scales around it's center point not the fulcrum at the center of the face. I can fix that scaling by setting the swing's anchorPoint so it scales toward one end rather than the center...
swing.anchorPoint = CGPointMake(0, 0.5);
but doing that causes the pendulum to not swing. (I think because physics forces are applied to swing's anchorPoint which, if I move it to one end, is at the pin anchor which is fixed).
How can I get the arm to scale towards the fulcrum and still allow it to swing?
Here's the code...
// in my SKScene
ClockFace *face = [ClockFace face]; // just an SKNode subclass
face.name = #"face";
face.position = CGPointMake(150, 150);
[self addChild:face];
// add the pendulum
SKSpriteNode *fulcrum = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(5, 5)];
[face addChild:fulcrum];
fulcrum.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:fulcrum.frame.size];
fulcrum.physicsBody.dynamic = NO;
SKSpriteNode *swing = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake(190, 2)];
[face addChild:swing];
swing.position = CGPointMake(95, 0);
swing.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:swing.frame.size];
SKPhysicsJointPin *pinJoint = [SKPhysicsJointPin jointWithBodyA:fulcrum.physicsBody bodyB:swing.physicsBody anchor:face.position];
[self.physicsWorld addJoint:pinJoint];
In Swift 3 example of this:
let nodeSize = CGSize(width: 10, height: 10)
let node = SKSpriteNode(color: .red, size: nodeSize)
node.physicsBody = SKPhysicsBody(rectangleOf: nodeSize)
node.physicsBody?.isDynamic = false
self.addChild(node)
let node2Size = CGSize(width: 60, height: 8)
let node2 = SKSpriteNode(color: .green, size: node2Size)
node2.position = CGPoint(x: 5, y: 0)
node2.anchorPoint = CGPoint(x: 0.0, y: 0.5) // <- New Line
node2.physicsBody = SKPhysicsBody(rectangleOf: node2Size)
node2.physicsBody?.mass = 1.0
self.addChild(node2)
// Scale Line
node2.run(SKAction.repeatForever(SKAction.sequence([
SKAction.scale(to: 0.2, duration: 1.0),
SKAction.scale(to: 1.5, duration: 0.5),
])))
// Anchor Point
let a = SKPhysicsJointPin.joint(withBodyA: node.physicsBody! , bodyB: node2.physicsBody!, anchor: CGPoint(x: 0.0, y: 0.0))
self.physicsWorld.add(a)
The reason why the scaling is weird is because of how you do your anchor point. Imagine a rubber band with a thumbtack in it. The anchor point is where the thumbtack is. Now to scale. Just grab the rubber band and pull. When the thumbtack is the middle, you will find it easy to stretch both sides evenly (this is what you are doing on the y axis). When you place it on the left of the rubber band, you will only be able to scale the right side (This is what you want to be doing)
Now with PhysicsBody, you need to adjust the bodies anchor point based on the sprites anchor point. To do this you need to do some math:
let centerPoint = CGPointMake(sprite.size.width / 2 - (sprite.size.width * sprite.anchorPoint.x), sprite.size.height / 2 - (sprite.size.height * sprite.anchorPoint.y))
sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size, center: centerPoint)
Using centerPoint allows you to move where the center of your body is.
// in my SKScene
ClockFace *face = [ClockFace face]; // just an SKNode subclass
face.name = #"face";
face.position = CGPointMake(150, 150);
[self addChild:face];
// add the pendulum
SKSpriteNode *fulcrum = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(5, 5)];
[face addChild:fulcrum];
fulcrum.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:fulcrum.frame.size];
fulcrum.physicsBody.dynamic = NO;
SKSpriteNode *swing = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake(190, 2)];
[face addChild:swing];
swing.anchorPoint = CGPointMake(0,0.5);
CGPoint *centerPoint = CGPointMake(swing.size.width / 2 - (swing.size.width * swing.anchorPoint.x), swing.size.height / 2 - (swing.size.height * swing.anchorPoint.y));
swing.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:swing.frame.size center:centerPoint];
SKPhysicsJointPin *pinJoint = [SKPhysicsJointPin jointWithBodyA:fulcrum.physicsBody bodyB:swing.physicsBody anchor:face.position];
[self.physicsWorld addJoint:pinJoint];

How can I apply physics to a SKShapeNode in Sprite Kit?

In the following example, there are three things on the screen:
ball (a SKShapeNode)
spriteContainer (a SKSpriteNode that contains ball2, a SKShapeNode)
box (a SKSpriteNode)
Why does ball fall out of view? Does a SKShapeNode need to be inside a SKSpriteNode to have physics properly applied to it?
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
SKColor * warmRed = [SKColor colorWithRed:0.99 green:0.41 blue:0.25 alpha:1.0];
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.backgroundColor = warmRed;
//falls out of view
SKShapeNode * ball = [[SKShapeNode alloc] init];
CGMutablePathRef ballPath = CGPathCreateMutable();
CGPathAddArc(ballPath, NULL, size.width-40, self.size.height/2, 20, 0, M_PI*2, YES);
ball.path = ballPath;
ball.lineWidth = 2;
ball.fillColor = warmRed;
ball.strokeColor = [SKColor whiteColor];
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:20];
[self addChild:ball];
//lands on bottom of screen
SKShapeNode * ball2 = [[SKShapeNode alloc] init];
CGMutablePathRef ball2Path = CGPathCreateMutable();
CGPathAddArc(ball2Path, NULL, 0, 0, 20, 0, M_PI*2, YES);
ball2.path = ball2Path;
ball2.lineWidth = 2;
ball2.fillColor = warmRed;
ball2.strokeColor = [SKColor whiteColor];
CGSize spriteContainerSize = CGSizeMake(40,40);
CGPoint spriteContainerPosition = CGPointMake(size.width/2, size.height/2);
SKSpriteNode * spriteContainer = [SKSpriteNode spriteNodeWithColor:[SKColor blackColor] size:spriteContainerSize];
spriteContainer.position = spriteContainerPosition;
spriteContainer.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:spriteContainerSize];
[spriteContainer addChild:ball2];
[self addChild:spriteContainer];
//lands on bottom of screen
CGSize boxSize = CGSizeMake(40,40);
CGPoint boxPosition = CGPointMake(boxSize.width, size.height/2);
SKSpriteNode * box = [SKSpriteNode spriteNodeWithColor:[SKColor blackColor] size:boxSize];
box.position = boxPosition;
box.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:boxSize];
[self addChild:box];
}
return self;
}
Screenshot:
https://dl.dropboxusercontent.com/u/164157126/example.jpg
Note that you don't set a position for the node which falls off the screen, but set a position for the other two nodes you create.
The default position of a node is 0,0. Your ball will appear at the bottom left of the scene, and since it is over the edge body you defined, will fall off immediately.
Set the ball's position appropriately so that it does not intersect the edge of the screen, and the ball will not fall off.

iOS circles and lines in Sprite Kit

I want to draw a circle and a line from from the circles center which is also twice as long as the circle's radius
My output is drawing a circle with the line next to it.
- (void)setupMainBall {
mainBall = [self makeMainBall];
mainBall.position = CGPointMake(self.size.width / 2, self.size.height / 2);
mainBall.zPosition = 3;
[self addChild:mainBall];
[mainBall addChild:[self makeCanon]];
}
-(SKShapeNode *)makeMainBall {
SKShapeNode *theMainBall = [[SKShapeNode alloc] init];
CGMutablePathRef myPath = CGPathCreateMutable();
CGPathAddArc(myPath, NULL, 0, 0, 10, 0, M_PI*2, YES);
theMainBall.fillColor = [SKColor blueColor];
theMainBall.path = myPath;
theMainBall.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:theMainBall.frame.size.width / 2];
return theMainBall;
}
-(SKSpriteNode *)makeCanon {
canon = [SKSpriteNode spriteNodeWithColor:[SKColor blackColor] size:CGSizeMake(3, 20)];
canon.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:canon.frame.size];
return canon;
}
Try:
SKSpriteNode *canon = [self makeCannon];
[mainBall addChild:canon];
canon.position = CGPointMake(mainBall.size.width / 2, mainBall.size.width /2);

I want to make a soccer ball by IOS SpriteKit

I want to make a soccer ball by IOS SpriteKit
I was able to create a ball in the code such as the following.
However, it does not bounce at all.
To make rebound rate, How can I do this?
(UITouch *touch in touches) {
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"Ball"];
sprite.position = location;
const CGFloat radius = 20.0f;
SKPhysicsBody *pbody = [SKPhysicsBody bodyWithCircleOfRadius:radius];
sprite.physicsBody = pbody;
sprite.size = (CGSize){radius * 2, radius * 2};
sprite.physicsBody.linearDamping=0.12f;
[self addChild:sprite];
}
I have declared like this.
  
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:(CGRect){CGPointZero, size}];
self.physicsWorld.gravity=CGVectorMake(0, -100);
}
return self;
}
This looks like a ball to me
- (void) soccerBallExample
{
// the world bounds
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(0, 0, self.size.width, self.size.height)];
// the ball
SKShapeNode *ball;
ball = [[SKShapeNode alloc] init];
CGMutablePathRef myPath = CGPathCreateMutable();
CGPathAddArc(myPath, NULL, 0,0, 30, 0, M_PI*2, YES);
ball.path = myPath;
CGPathRelease(myPath);
ball.fillColor = [SKColor blueColor];
ball.position = CGPointMake(200, 200);
[self addChild:ball];
// set the physics
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:30];
ball.physicsBody.restitution = 0.8;
}
In your example, you use a sprite so for radius use
// ball.size.width * 0.5;

Sprite Kit pin joints appear to have an incorrect anchor

I'm testing out pin joints with Sprite Kit, and I'm finding something unusual happening.
My desired setup is this: one wide, flat box, and two circles; the circles are connected via SKPhysicsPinJoints to the box, so they can act as wheels.
Here's my code. I've tried to make it as concise as possible:
- (SKNode*) createWheelWithRadius:(float)wheelRadius {
CGRect wheelRect = CGRectMake(-wheelRadius, -wheelRadius, wheelRadius*2, wheelRadius*2);
SKShapeNode* wheelNode = [[SKShapeNode alloc] init];
wheelNode.path = [UIBezierPath bezierPathWithOvalInRect:wheelRect].CGPath;
wheelNode.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:wheelRadius];
return wheelNode;
}
- (void) createCar {
// Create the car
SKSpriteNode* carNode = [SKSpriteNode spriteNodeWithColor:[SKColor yellowColor] size:CGSizeMake(150, 50)];
carNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:carNode.size];
carNode.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
[self addChild:carNode];
// Create the left wheel
SKNode* leftWheelNode = [self createWheelWithRadius:30];
leftWheelNode.position = CGPointMake(carNode.position.x-80, carNode.position.y);
[self addChild:leftWheelNode];
// Create the right wheel
SKNode* rightWheelNode = [self createWheelWithRadius:30];
rightWheelNode.position = CGPointMake(carNode.position.x+80, carNode.position.y);
[self addChild:rightWheelNode];
// Attach the wheels to the body
CGPoint leftWheelPosition = leftWheelNode.position;
CGPoint rightWheelPosition = rightWheelNode.position;
SKPhysicsJointPin* leftPinJoint = [SKPhysicsJointPin jointWithBodyA:carNode.physicsBody bodyB:leftWheelNode.physicsBody anchor:leftWheelPosition];
SKPhysicsJointPin* rightPinJoint = [SKPhysicsJointPin jointWithBodyA:carNode.physicsBody bodyB:rightWheelNode.physicsBody anchor:rightWheelPosition];
[self.physicsWorld addJoint:leftPinJoint];
[self.physicsWorld addJoint:rightPinJoint];
}
What I'm expecting is that the pin joints are anchored at their centre points; however, when I test this, the anchors for the joints appear to be far off.
Am I missing something really obvious?
I also had this issue and the cause is setting the physics body before setting the sprites position.
carNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:carNode.size];
carNode.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
Change the above to
carNode.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
carNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:carNode.size];
It should work. Thanks Smick.
SpriteKit: How to create Basic Physics Joints
Try this code, I used yours and got weird issues, so started from scratch.
- (SKShapeNode*) makeWheel
{
SKShapeNode *wheel = [[SKShapeNode alloc] init];
CGMutablePathRef myPath = CGPathCreateMutable();
CGPathAddArc(myPath, NULL, 0,0, 16, 0, M_PI*2, YES);
wheel.path = myPath;
return wheel;
}
- (void) createCar
{
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(0, 0, self.size.width, self.size.height)];
// 1. car body
SKSpriteNode *carBody = [SKSpriteNode spriteNodeWithColor:[UIColor whiteColor] size:CGSizeMake(120, 8)];
carBody.position = CGPointMake(200, 200);
carBody.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:carBody.size];
[self addChild:carBody];
// 2. wheels
SKShapeNode *leftWheel = [self makeWheel];
leftWheel.position = CGPointMake(carBody.position.x - carBody.size.width / 2, carBody.position.y);
leftWheel.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:16];
[self addChild:leftWheel];
SKShapeNode *rightWheel = [self makeWheel];
rightWheel.position = CGPointMake(carBody.position.x + carBody.size.width / 2, carBody.position.y);
rightWheel.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:16];
[self addChild:rightWheel];
// 3. Join wheels to car
[self.physicsWorld addJoint:[SKPhysicsJointPin jointWithBodyA:carBody.physicsBody bodyB:leftWheel.physicsBody anchor:leftWheel.position]];
[self.physicsWorld addJoint:[SKPhysicsJointPin jointWithBodyA:carBody.physicsBody bodyB:rightWheel.physicsBody anchor:rightWheel.position]];
// 4. drive car
[carBody.physicsBody applyForce:CGVectorMake(10, 0)];
}

Resources