I have two spriteNodes that intersect but at different positions. both sprites are rectangular. I am getting some strange behavior where it only detects an intersection between the nodes if they intersect on the left half of one of the nodes (this node is always the same one and is always a stationary node). So what could cause such a behavior?
I do have some code I can show but it seems pretty straight forward so if anyone thinks it might help just let me know?
Update: it works with a different sprite here is the code for each of them:
if (!weldRUHit && [weldRU intersectsNode:[barriers objectAtIndex:i]]) {
SKSpriteNode *nodeA = (SKSpriteNode*)[barriers objectAtIndex:i];
SKPhysicsBody *firstBody = nodeA.physicsBody;
SKSpriteNode *nodeB = weldRU;
SKPhysicsBody *secondBody = nodeB.physicsBody;
weldRUHit = YES;
[self beginIntersectionWithBodyA:firstBody bodyB:secondBody];
}
if (!weldLDHit && [weldLD intersectsNode:[barriers objectAtIndex:i]]) {
SKSpriteNode *nodeA = (SKSpriteNode*)[barriers objectAtIndex:i];
SKPhysicsBody *firstBody = nodeA.physicsBody;
SKSpriteNode *nodeB = weldLD;
SKPhysicsBody *secondBody = nodeB.physicsBody;
weldLDHit = YES;
[self beginIntersectionWithBodyA:firstBody bodyB:secondBody];
}
it works for weldLD but not for weldRU.
I am experiencing the same problem with SKNode's intersectsNode: method. I think it has to do with a combination of anchor points and of two nodes not being children of the same parent node. The frame property is used to detect node intersection, and Apple defines the frame property of SKNode as "A rectangle in the parent’s coordinate system..." Thus with different parents you have different coordinate systems. Either way, my solution is to use this function instead.
bool CGRectIntersectsRect (
CGRect rect1,
CGRect rect2
);
And just make sure you have the correct coordinate system. Hint: use SKNode's convertPoint:FromNode: method.
Related
I have a Class with a SpriteNode that rotates very wide, when rotated within the main Scene(as if the anchor point is in the middle of the screen and the Sprite is rotating around it). I want it to rotate around the anchor point of itself in the main Scene(anchor point on the Sprite).
So in the Class i have something like the following
- (void)createChainWithPos:(CGPoint)pos {
SKTexture *myTex...
SKTexture *myTex2...
SKSpriteNode *chainFront = [SKSpriteNode spriteWithTexture:myTex];
chainFront.position = pos;
chainFront.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:mytex.size];
[self addChild:chainFront];
[_chainParts addObject:chainFront];
SKSpriteNode *chainSide = [SKSpriteNode spriteWithTexture:myTex2];
chainSide.position = CGPointMake(chainFront.position.x, chainFront.position.y - chainSide.size.height + 6);
chainSide.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:myTex2.size;
[self addChild:chainSide];
[_chainParts addObject:chainSide];
}
I have an loop creating the chain parts in the main file but couldn't get it rotate so stripped it down in an new project. There is actually 4 chain parts but i only did two. The other two are just mirrors of the ones above with their positions mirroring the chainSide.(to position them in a chain like fashion)
and in the Scene
self.chain1 = [chain node];
[self.chain1 createChainWithPos:CGPointMake(self.size.width/2, self.size.height/2);
self.chain1.zRotation = 3.14/4;
[self addChild:self.chain1];
I have a NSMutableArray in the chain class header that i use to hold the chains.
the physics joints
for (int i = 1; i < self.chain1.chainParts.count; i++ {
SKSpriteNode *nodeA = [[self.chain1 chainParts]objectAtindex:i-1];
SKSpriteNode *nodeB = [[self.chain1 chainParts]objectAtindex:i];
SKPhysicsJointPin *pin = [SKPhysicsJointPin jointWithBodyA:nodeA.physicsBody
bodyB:nodeB.physicsBody
anchor:CGPointMake(CGRectGetMidX(nodeA.frame), CGRectGetMinY(nodeA.Frame))];
}
I found that if i set the position of the chain in the middle in the Class, it rotates correctly in the Scene. However, the physics joints just start moving randomly across the screen and isn't correct to the anchor points set. (the physics joints are set in the Scene)
I don't know if i have to convert the coordinates or play with random anchor point positions , but if someone could shed some light if would be greatly appreciated.
You do not want to use the nodes frame, you want to use the nodes size, the frame mid is the screen coordinate + the fitted node size / 2, you want the middle of your sprite only, also for pi / 4 use M_PI_4
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.
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.
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 am creating a simple Sprite Kit game however when i am adding the PhysicsBody to one of my sprites it seems to be going in the wrong position. i know that it is in the wrong position as i have have set
skView.showsPhysics = YES;
and it is showing up in the wrong position.
The Square in the bottom corner is the physics body for the first semicircle. I am using a square at the moment just for testing purposes.
My app includes view following and follows my main sprite when it moves. I implemented this by following apples documentation and creating a 'myworld' node and creating all other nodes from that node.
myWorld = [SKNode node];
[self addChild:myWorld];
semicircle = [SKSpriteNode spriteNodeWithImageNamed:#"SEMICRICLE.png"];
semicircle.size = CGSizeMake(semicircle.frame.size.width/10, semicircle.frame.size.height/10);
semicircle.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:semicircle.frame.size];
semicircle.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
semicircle.physicsBody.dynamic = YES;
semicircle.physicsBody.collisionBitMask = 0;
semicircle.name = #"semicircle";
[myWorld addChild:semicircle];
To centre on the node I call these methods
- (void)didSimulatePhysics
{
[self centerOnNode: [self childNodeWithName: #"//mainball"]];
}
- (void) centerOnNode: (SKNode *) node
{
CGPoint cameraPositionInScene = [node.scene convertPoint:node.position fromNode:node.parent];
node.parent.position = CGPointMake(node.parent.position.x - cameraPositionInScene.x, node.parent.position.y - cameraPositionInScene.y);
}
I don't know if the my world thing makes any difference to the SkPhysics body...
SKPhysicsBody starts at coordinates 0,0 which is at the bottom left hand corner. If you make the area smaller, as you did by width/10 and height/10, you decrease the size but from the bottom left.
I think what you are looking for is bodyWithRectangleOfSize:center: which allows you to manually set the center from which you base your physics body area on.
Update:
Based on what I understand, your smallest semi circle pic size is the same as the screen size. I would suggest you modify the image size to something like the example I have. You can then set the sprite's position as required and set the physics body to the half of the image containing your semi circle.
Your centerOnNode call should be put in the didEvaluateActions function instead of the didSimulatePhysics function. This is because you need to move the world before the physics are drawn so that they stay in sync. Similar question found here: https://stackoverflow.com/a/24804793/5062806