I am using SpriteKit to build a game and have run into some trouble with collision detection. I have 2 subclasses of SKSpriteNode, Player and Enemy. They should both detect collisions with each other. Here's how I am initializing the Player object's physicsBody:
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.size];
self.physicsBody.affectedByGravity = NO;
self.physicsBody.dynamic = NO;
self.physicsBody.usesPreciseCollisionDetection = YES;
self.physicsBody.categoryBitMask = playerCategory;
self.physicsBody.collisionBitMask = enemyCategory;
self.physicsBody.contactTestBitMask = enemyCategory;
And here's how I am initializing the Enemy objects's physicsBody:
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.size];
self.physicsBody.affectedByGravity = NO;
self.physicsBody.dynamic = NO;
self.physicsBody.usesPreciseCollisionDetection = NO;
self.physicsBody.categoryBitMask = enemyCategory;
self.physicsBody.collisionBitMask = playerCategory;
self.physicsBody.contactTestBitMask = playerCategory;
I have my GameScene implementing the SKPhysicsContactDelegate protocol and I have this in its init:
self.physicsWorld.gravity = CGVectorMake(0.0, 0.0);
self.physicsWorld.contactDelegate = self;
And yet, the didBeginContact method isn't being called as it should be. I tried initializing the physicsBodies after the objects are created in the scene and still nothing. What am I doing wrong?
UPDATE 1
Here's the code that makes the bitMasks, it's in a Common.h file that is imported in Prefix.pch file.
static const uint32_t enemyCategory = 0x1 <<0;
static const uint32_t playerCategory = 0x1 <<1;
You need to set physicsBody.dynamic to YES for your nodes.
From the SKPhysicsBody class reference:
dynamic
A Boolean value that indicates whether the physics body is
moved by the physics simulation.
The default value is YES. If the value is NO, then the physics body
ignores all forces and impulses applied to it.
Related
I have two body
SKShapeNode *hero = [SKShapeNode shapeNodeWithCircleOfRadius:HERO_SIZE];
hero.lineWidth = 1;
hero.fillColor = [SKColor orangeColor];
hero.name = #"Hero";
hero.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:HERO_SIZE];
hero.physicsBody.categoryBitMask = hero;
hero.physicsBody.collisionBitMask = friendlyCategory | enemyCategory;
hero.physicsBody.contactTestBitMask = friendlyCategory | enemyCategory;
hero.position = [self getStartPosition];
SKShapeNode *circle = [SKShapeNode shapeNodeWithCircleOfRadius:BALL_SIZE];
circle.position = [self randomPossition];
circle.lineWidth = 2;
circle.strokeColor = [SKColor blueColor];
circle.fillColor = color;
circle.name = #"Circle";
circle.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:BALL_SIZE];
circle.physicsBody.categoryBitMask = friendlyCategory;
circle.physicsBody.collisionBitMask = hero;
circle.physicsBody.contactTestBitMask = hero;
then,
- (void)didEndContact:(SKPhysicsContact *)contact
{
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (friendlyCategory | hero ))
{
SKShapeNode* node = (SKShapeNode*)contact.bodyA.node;
if ([node.name isEqualToString:#"Hero"])
{
self.activeFriendlyBall = (SKShapeNode*)contact.bodyB.node;
}
else
{
self.activeFriendlyBall = (SKShapeNode*)contact.bodyA.node;
}
self.hero.position = self.activeFriendlyBall.position;
}
}
Hero must be added above activeFriendlyBall, for him center position. But it added near him. I think it because physic body added. But I need to use physic body for other logic.
http://cl.ly/image/0g1S0d3h1h0w
must be like, how in top screen.
I find solution.
self.heroBall.physicsBody.velocity = CGVectorMake(0, 0);
[self.heroBall runAction: [SKAction moveTo:self.activeFriendlyBall.position duration:0.0]];
I believe it is because you have collision detection enabled for both, which means they can't be put on top of one another. If you are just trying to determine if one sprite comes into contact with another all you need is the contact detection. Removing either hero or friendlyball from the collision detection bit mask will allow them to be placed on top of each other/go through each other etc.
If you don't care about collisions at all for one of them (the sprite doesn't 'bounce' off anything) I'd recommend just setting collision detection to 0 for that sprite.
Best
I am trying to get collision working in my SpriteKit game using the didBeginContact function.
My problem is that the function is just not getting called at all when the ball bounces off of the bricks. This is how I have set them both up:
static const uint32_t blockCollisionCheck = 0x1 << 0;
static const uint32_t ballCollisionCheck = 0x1 << 1;
Ball:
SKShapeNode *ball = [[SKShapeNode alloc] init];
CGMutablePathRef drawPath = CGPathCreateMutable();
CGPathAddArc(drawPath, NULL, 0, 0, _ballRadius, 0, M_PI * 2, YES);
ball.path = drawPath;
CGPathRelease(drawPath);
ball.fillColor = [SKColor greenColor];
ball.position = CGPointMake(CGRectGetMidX(self.frame), 150);
ball.name = #"ball";
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:_ballRadius];
ball.physicsBody.friction = 0.0;
ball.physicsBody.restitution = 1.0;
ball.physicsBody.linearDamping = 0.0f;
ball.physicsBody.allowsRotation = NO;
ball.physicsBody.dynamic = YES;
ball.physicsBody.categoryBitMask = ballCollisionCheck;
ball.physicsBody.contactTestBitMask = blockCollisionCheck;
[self addChild:ball];
Bricks:
SKSpriteNode *block = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(_blockWidth, _blockHeight)];
block.name = #"block";
block.position = CGPointMake(x, y);
block.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:block.size];
block.physicsBody.allowsRotation = NO;
block.physicsBody.friction = 0.0;
block.physicsBody.dynamic = YES;
block.physicsBody.categoryBitMask = blockCollisionCheck;
block.physicsBody.contactTestBitMask = ballCollisionCheck;
[self addChild:block];
I can't for the life of me see what is wrong with this, as I have the category bit masks correct I think? Also both of the sprites are Dynamic, which is another problem I read it could have been.
It's not that the contents of my didBeginContact function is not working, it's just never getting there as evidenced from a lack of NSLog message and breakpoints not being reached.
Any help would be greatly appreciated.
Thank you.
If, as you say, the didBeginContact method is not being called at all, I suspect you did not add the self.physicsWorld.contactDelegate = self; into your GameScene init method.
Im using Sprite Kit to detect collision between two objects. Here is how I define their bitmasks.
static const uint32_t puffinCategory = 0x1 << 0;
static const uint32_t planeCategory = 0x1 << 1;
Here is my code as to how setup the puffins and planes physics body.
For puffin
SKSpriteNode *PuffinNode = [[SKSpriteNode alloc]initWithImageNamed:#"puffin"];
PuffinNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:PuffinNode.size];
PuffinNode.physicsBody.usesPreciseCollisionDetection = YES;
PuffinNode.physicsBody.categoryBitMask = puffinCategory;
PuffinNode.physicsBody.dynamic = NO;
PuffinNode.physicsBody.collisionBitMask = puffinCategory;
PuffinNode.physicsBody.contactTestBitMask = planeCategory;
[PuffinNode setZPosition:1.5];
For Plane
SKSpriteNode *planeSpriteNode = [[SKSpriteNode alloc]initWithImageNamed:planeStringFileName];
planeSpriteNode.position = CGPointMake(0, self.view.frame.size.height*-1);
planeSpriteNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:planeSpriteNode.size];
planeSpriteNode.physicsBody.usesPreciseCollisionDetection = YES;
planeSpriteNode.physicsBody.categoryBitMask = planeCategory;
planeSpriteNode.physicsBody.dynamic = NO;
planeSpriteNode.physicsBody.collisionBitMask = puffinCategory;
planeSpriteNode.physicsBody.contactTestBitMask = puffinCategory;
Here is my implementation of the delegate method didBeginContact:
-(void)didBeginContact:(SKPhysicsContact *)contact{
NSLog(#"collission method run");
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask){
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}else{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
if ((firstBody.categoryBitMask & puffinCategory) != 0 && (secondBody.categoryBitMask & planeCategory) != 0 ){
NSLog(#"collission occured");
}
}
Im not seeing the method logging if it is called, nor do I see a log when the two sprites collide.
You set
PuffinNode.physicsBody.dynamic = NO;
and
planeSpriteNode.physicsBody.dynamic = NO;
two static bodies cannot collide, at least one should be dynamic
Some problems :
1) Did you set the SKScene's physicsWorld.contactDelegate ?
2) Both of your nodes have no dynamic. If you want them to interact in the physics world you should make them under physics laws. One of them at least must be dynamic.
3) Your collisionBitMask are not well set.
I'm kind of new with SpriteKit and I'm having some troubles with the physics, I've tried many things to make a SKSpriteNode move with a force or an impulse but it never moves, only gravity affects it when i set it.
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.anchorPoint = CGPointMake(0.5, 0.5);
myWorld = [SKNode node];
[self addChild:myWorld];
SKSpriteNode* map = [SKSpriteNode spriteNodeWithImageNamed:#"grass"];
[myWorld addChild:map];
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:map.frame];
self.physicsBody.friction = 0.0f;
self.physicsBody.categoryBitMask = 2;
self.physicsWorld.gravity = CGVectorMake(0.0, 0.0);
self.physicsWorld.contactDelegate = self;
pointOfView = [SKSpriteNode spriteNodeWithColor:[UIColor colorWithWhite:1 alpha:0.2] size:CGSizeMake(300, 548)];
[myWorld addChild:pointOfView];
pointOfView.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:pointOfView.frame.size];
pointOfView.physicsBody.friction = 0.0f;
pointOfView.physicsBody.restitution = 1.0f;
pointOfView.physicsBody.linearDamping = 0.0f;
pointOfView.physicsBody.allowsRotation = NO;
pointOfView.physicsBody.collisionBitMask = 2;
pointOfView.physicsBody.categoryBitMask = 3;
[pointOfView.physicsBody applyImpulse:CGVectorMake(10.0f, -10.0f)];
}
return self;
}
Here is most of my code in my main scene, there's nothing else except some empty methods like update or touchesBegan.. Am I missing something ? I've tried so many things, i'm starting to lose it haha
Thanks very much for any help !
It's a bit late, but the answer is here: Not able to apply impulse to SKSpriteNode physics body
The problem is that you've created a physics body with bodyWithEdgeLoopFromRect; this creates a static physics body which won't respond to applyImpulse. You need to add the physics body using bodyWithRectangleOfSize: instead.
pointOfView.physicsBody.dynamic = YES;
The dynamic property decides if a sprite should be affected by physics forces.
you have written wrong code your pointOfView not affected by gravity you are just applying a small impusle on it.
change this line self.physicsWorld.gravity = CGVectorMake(0.0, 0.0) to self.physicsWorld.gravity = CGVectorMake(0.0, -9.8); if u looking for negative gravity towards up direction and CGVectorMake(0.0, 9.8) if you looking for positive gravity towards downwards. and try to apply impulse after a interval [pointOfView.physicsBody applyImpulse:CGVectorMake(10.0f, -10.0f)];
in my code I have physics bodies set up for ball and welding sprites.
here is the physicsBody setup for ball sprites:
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:10];
ball.physicsBody.affectedByGravity = NO;
ball.physicsBody.velocity = velocity;
ball.physicsBody.restitution = 1;
ball.physicsBody.friction = 0;
ball.physicsBody.categoryBitMask = ballCategory;
ball.physicsBody.collisionBitMask = ballCategory | barrierCategory | weldCategory | weldPointCategory;
ball.physicsBody.contactTestBitMask = weldingCategory;
and this is the physicsBody setup for welding sprites:
weldRU = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(2, 2)];
weldRU.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:weldRU.size];
//weldRU.physicsBody.dynamic = NO;
weldRU.physicsBody.affectedByGravity = NO;
weldRU.physicsBody.categoryBitMask = weldingCategory;
weldRU.physicsBody.collisionBitMask = 0;
weldRU.physicsBody.contactTestBitMask = ballCategory;
weldRU.physicsBody.usesPreciseCollisionDetection = YES;
in the setup I give each other categoryBitMask's and contactTestBitMask's but the contact override method never gets called when these bodies come in contact. the welding sprites do have actions running at any possible contact time but the physicsBody is always present. So am I missing something that is causing them to not detect contact?