cocos2d v3: physicsBody of a CCSprite is positioned incorrectly - ios

I'm very new to cocos2d development.
What I am trying to implement is some sprite nodes with physics bodies. As far as I understand, you're supposed to add them to the scene like this ("physics" here is the name of CCPhysicsNode declared earlier):
[physics addChild:node];
instead of
[self addChild:node];
The second one is pretty self-explanatory, I never had any trouble with it. But with the first the position of collision shapes does not match the position of the actual sprite (as seen in debug drawing, and by offset I mean greatly offset, like 2x the actual position). This is how I declare and add the node:
CCSprite *sprite = [CCSprite spriteWithImageNamed:#"sprite.png"];
sprite.position = position;
sprite.physicsBody = [CCPhysicsBody bodyWithRect:[sprite boundingBox] cornerRadius:0];
sprite.physicsBody.collisionType = #"SomeCollisionType";
sprite.name = #"Name";
[physics addChild:sprite];
What am I doing wrong? Please explain me how to make the positions match. TIA.

Related

Sprite Kit - Make SKPhysicsBody with restitution only on sides

I am trying to create a complex SKPhysicsBody that does not bounce on the top, but does allow bouncing on the sides and bottom. Currently I am creating two nodes. One that has the image of the Sprite and a no restitution PhysicsBody on the top.
My second node matches the first node, but is clear with the same size as the first node It just has a PhysicsBody on the front and bottom and has a restitution. to allow my main character to bounce off the bottom and front.
Here is the code for my current setup:
//This is the top part.
- (void)nodePhysicsBodySetup:(SKSpriteNode *)node
{
CGPoint topStart = CGPointMake(0, node.size.height);
CGPoint topEnd = CGPointMake(node.size.width, node.size.height);
SKPhysicsBody *topEdge = [SKPhysicsBody bodyWithEdgeFromPoint:topStart toPoint:topEnd];
node.physicsBody = topEdge;
//[SKPhysicsBody bodyWithBodies:#[topEdge, frontEdge, bottomEdge]]
//node.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:node.frame.size];
node.physicsBody.dynamic = NO;
node.physicsBody.restitution = 0.0;
node.physicsBody.affectedByGravity = NO;
node.physicsBody.categoryBitMask = groundCategory;
node.physicsBody.collisionBitMask = mainHeroCategory;
node.physicsBody.contactTestBitMask = mainHeroCategory;
}
//This is the bottom physicsbody that matches up with the image of the main node.
- (void)bottomNodePhysicsBodySetup:(SKSpriteNode *)node
{
CGPoint topStart = CGPointMake(0, node.size.height);
CGPoint frontEnd = CGPointMake(0,0);
CGPoint bottomEnd = CGPointMake(node.size.width,0);
SKPhysicsBody *frontEdge = [SKPhysicsBody bodyWithEdgeFromPoint:topStart toPoint:frontEnd];
SKPhysicsBody *bottomEdge = [SKPhysicsBody bodyWithEdgeFromPoint:frontEnd toPoint:bottomEnd];
bottomNode.physicsBody = [SKPhysicsBody bodyWithBodies:#[frontEdge, bottomEdge]];
bottomNode.physicsBody.dynamic = NO;
bottomNode.physicsBody.affectedByGravity = NO;
bottomNode.physicsBody.restitution = 0.5;
bottomNode.physicsBody.categoryBitMask = otherCategory;
bottomNode.physicsBody.collisionBitMask = mainHeroCategory;
bottomNode.physicsBody.contactTestBitMask = mainHeroCategory;
}
Picture as screenshots are under NDA:
Currently, the Green PhysicsBody is attached to the node, and the red PhysicsBody is attached to a transparent second node with the same position as the first node.
This does not really work. My main character (mainHeroCategory) gets stuck sometimes on the node corner where the PhysicsBody with restitution and the one without meet. What is a better way to do this?
I would try to do this using only one physics body and look at the contact delegate methods to determine where the contact occurred and what the normal vector is (that information is in the SKPhysicsContact object passed in the delegate method).
Once I got that information I would apply an impulse to the mainHeroCategory object, or not, depending on where the contact occurred.
I suggest you join the physics bodies with an SKPhysicsJointFixed or by constraining the bodies with an SKConstraint or two. SpriteKit only uses the shape when merging two or more bodies with bodyWithBodies. From the docs...
The properties on the children, such as mass or friction, are ignored.
Only the shapes of the child bodies are used.
The restitution property is ignored.

Sprite Kit: One node with two physics body

It's possible for one node to have two physics body paths? I want to create a node that has two (circle) physics bodies on the sides of the node.
If it's not possible, is there are any workaround to achieve that? thank you
You want to use [SKPhysicsBody bodyWithBodies:...]. From the docs :
The shapes of the physics bodies passed into this method are used to
create a new physics body whose covered area is the union of the areas
of its children. These areas do not need to be contiguous. If there is
space between two parts, other bodies may be able to pass between
these parts. However, the physics body is treated as a single
connected body, meaning that a force or impulse applied to the body
affects all of the pieces as if they were held together with an
indestructible frame.
It would look something like this :
SKPhysicsBody *leftCircle = [SKPhysicsBody bodyWithCircleOfRadius:leftCircleRadius center:leftCircleCenter];
SKPhysicsBody *rightCircle = [SKPhysicsBody bodyWithCircleOfRadius:rightCircleRadius center:rightCircleCenter];
node.physicsBody = [SKPhysicsBody bodyWithBodies:#[leftCircle, rightCircle]];
Here's an example of how to connect to sprite nodes using SKPhysicsJointFixed. First, create two sprites:
SKSpriteNode *sprite1 = [SKSpriteNode spriteNodeWithColor:[SKColor blueColor] size:CGSizeMake(64, 64)];
// position must be set before creating physics body to avoid bug in iOS 7.0.x
sprite1.position = CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame));
sprite1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite1.size];
sprite1.physicsBody.restitution = 1.0;
[self addChild:sprite1];
SKSpriteNode *sprite2 = [SKSpriteNode spriteNodeWithColor:[SKColor blueColor] size:CGSizeMake(64, 64)];
sprite2.position = CGPointMake(CGRectGetMidX(self.frame)-sprite2.size.width*2,
CGRectGetMidY(self.frame));
sprite2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite2.size];
sprite2.physicsBody.restitution = 1.0;
[self addChild:sprite2];
then connect the nodes by calling this method:
[self connectNode1:sprite1 toNode2:sprite2];
This method joins two nodes at their midpoint. Note that both physic bodies must be in the scene prior to calling this method.
- (void) connectNode1:(SKSpriteNode *)node1 toNode2:(SKSpriteNode *)node2
{
CGPoint midPoint = CGPointMake((node1.position.x + node2.position.x)/2,
(node1.position.y + node2.position.y)/2);
SKPhysicsJointFixed *joint = [SKPhysicsJointFixed jointWithBodyA:node1.physicsBody
bodyB:node2.physicsBody
anchor:midPoint];
[self.physicsWorld addJoint:joint];
}
Here is an easy way to achieve the behavior you are looking for:
How to detect contact on different areas of a physicsbody
//Create SpriteNode1 & physicsBody
//Create SpriteNode2 & physicsBody
[SpriteNode1 addChild: SpriteNode2]
You can position SpriteNode2 relative to SpriteNode1. Any movement, etc. performed on SpriteNode1 will also move SpriteNode2.
Set: SpriteNode2.PhysicsBody.Dynamic=NO;
You could also create a SpriteNode that acts as the main object and add both SN1 and SN2 as children if you find that easier.

iOS SpriteKit Bounciness

I have two nodes in my scene. The first node is a ball at the top of my view. The second node is a rectangle at the bottom of my view to stop the ball from dropping out of view. I am wanting this ball to have NO bounciness at all. I am wanting it to just completely stop when it hits the rectangle. I don't know what I am doing wrong. Any help would be greatly appreciated!
SKSpriteNode *rectangle = [SKSpriteNode spriteNodeWithColor:[UIColor whiteColor] size:CGSizeMake(self.frame.size.width, 50)];
bottom.position = CGPointMake(self.frame.size.width / 2, 0);
bottom.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(rectangle, bottom.size.height)];
bottom.physicsBody.dynamic = NO;
[self addChild:bottom];
SKSpriteNode *ball = [SKSpriteNode spriteNodeWithImageNamed:#"ball"];
ball.position = CGPointMake(node.position.x, node.position.y);
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius: 5];
ball.physicsBody.restitution = 0;
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 0, 0);
CGPathAddLineToPoint(path, NULL, [self randomX], [self randomY]);
SKAction *move = [SKAction followPath:path asOffset:YES orientToPath:NO duration:3.0];
[ball runAction:move];
[self addChild: ball];
Why is is still bouncing off the rectangle?
In my experience, you should in more cases than not be working with frameworks like they expects you to, hacks just end up with weird quirks like the one you encountered. In this case, as #LearnCocos2D said, you should probably be using [ball.physicsBody applyImpulse:(CGVEctor)] if you need the ball to move along a specific path but still maintain a dynamic physicsBody. If you then set the restitution to 0, it will not bounce. I promise.
Read up on vectors and vector maths if you need the ball to follow a specific path (it's knowledge that is good to have anyways when working with physics). I've found this to be a good resource for that. It's for openFrameworks, hence C++, but the concept remains the same.
And if you still really need to use a CGPath, then I think you should tell us a bit more specific about what you need to accomplish. There might be a better solution, or maybe we should file a bug to Apple. Sprite-Kit is still fairly young, I have myself encounter problems I believe to be bugs.
The documentation indicates that the value of the "restitution" property of SKPHysicsBody is "how much energy the physics body loses when it bounces off another object." (here). By setting the restitution to zero, you make the ball as bouncy as possible - it loses 0 energy. If you set the restitution to 1 - loses all energy - it should stop.

SKAction scaleXTo:-1

I am trying to mirror my SKSpriteNode using SKAction *mirrorDirection = [SKAction scaleXTo:-1 y:1 duration:0.0]; but every time I do, the sprite's physics body seems to get messed up and it drops off the screen. I have several objects (floors) beneath the sprite and it falls through all of them as if they are not there.
This only happens when I mirror the sprite. Anyone know how to fix this?
Update:
Reseting the physics body as one answer suggested unfortunately did not fix the problem. It appears that only the contact part of the physics body malfunctions. Gravity still has an effect as the little guy drops like a rock.
I also tried to reset by again self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(self.size.width, self.size.height)];
right after the self.xScale = -1; but this also did not fix the issue.
Add your mirror-able node as a child of some regular SKNode and set the physics body on this parent node instead of the negatively scaled node.
I don't know how to fix this but I would like to suggest one way you could create a mirrored sprite.
To achieve this set the x or y scale of your sprite node to -1. Then use the SKView method textureFromNode: to create a texture from this mirrored node.
You can then use this texture to create a new sprite node that is mirrored but doesn't require any negative scaling.
Don't use an SKAction, just set it directly to the SKSpriteNode's xScale property.
self.yourSprite.xScale = -1;
Roecrew is right about setting the xScale property directly. I would suggest you try this:
node.xScale = -1;
node.physicsBody = node.physicsBody;
You will need to 'reset' the physicsBody each time you change the xScale property.
The xScale issue with physicsBody is a bug in SpriteKit, but I was able to 'retain' the physicsBody using the second line.
I'm meeting the problem exactly like yours. I spent about 2 hours to figure it out.
Just init your physicsBody after you scaleX, i dont know why but i had correct this issue by doing this way.
walkRight = [SKAction sequence:#[resetDirection,[SKAction runBlock:^{
[self changePhysicsDirectionRight];
}],[SKAction repeatActionForever: walk]]];
walkLeft = [SKAction sequence:#[mirrorDirection,[SKAction runBlock:^{
[self changePhysicsDirectionLeft];
}],[SKAction repeatActionForever: walk]]];
walkRight and walkLeft is my action when changing direction, and resetDirection and mirrorDirection is exactly the action i used to scaleXTo:1 and scaleXTo:1
So after i scaleXTo i use a method call changePhysicsDirectionRight to re-init my physicBody like
- (void)changePhysicsDirectionRight{
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(self.size.width,self.size.height)];
self.physicsBody.categoryBitMask = guyCategory;
self.physicsBody.contactTestBitMask = 0;
self.physicsBody.collisionBitMask = 0;
}
Remember to re-assign all your category and everything like you init before.
I hope someone pro at spritekit can tell me the reason why ....

iOS Sprite Kit- SKSpriteNode seems to participate in simulation without physics body

I am working on an iOS game using Sprite Kit.
I recently added an SKSpriteNode into my scene and did NOT create a physics body for it. However, when I build and run, when the player's character(which does have a physics body) moves into the SKSpriteNode, it spins and moves away, like it has a physics body -but I didn't create one for the SKSpriteNode.
Even if I type
sprite.physicsBody = nil;
it still behaves like it's part of the simulation.
I wondered if a physics body is created automatically when you make an SKSpriteNode, but I looked at the documentation and searched it on Google and couldn't find anything to suggest that is the case.
Any help or suggestions would be much appreciated.
This is the code I used to create the sprite (the one that should not be affected by the simulation)
-(void)addSprite
{
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"image"];
sprite.position = CGPointMake(40, 30);
sprite.zPosition = 30;
sprite.name = #"spriteName";
[self addChild:sprite];
}
In another part of the project I have
[self addSprite];
to call the method.
Yes every SKSpriteNode comes with a physics body. Its best to change the physics body's properties instead of getting rid of it altogether. If you want your sprite to be on screen and not interact with any other sprite nodes, do the following:
sprite.physicsBody.dynamic = NO;
sprite.physicsBody.collisionBitMask = 0x0;
sprite.physicsBody.contactTestBitMask = 0x0;

Resources