SKPhysicsContactDelegate Not Working - ios

I have the following code:
in my scene:
static const uint32_t enermyCategory = 0x1 << 0;
static const uint32_t fatherCategory = 0x1 << 1;
self.physicsWorld.contactDelegate = self;
//init ship
Ship *ship = [Ship getFather];
ship.position = CGPointMake(CGRectGetMaxX(self.frame) - ship.frame.size.width , ship.frame.size.height);
[self addChild: ship];
//init enermy
Enermy *ene = [[Enermy alloc] initWithImageNamed:enermyName gameScene:self];
ene.position = ship.position;
[self addChild:ene];
#pragma mark - Physics Delegate Methods
- (void)didBeginContact:(SKPhysicsContact *)contact{
NSLog(#"contact detected");
}
As you can see, I set both ship and energy at the same location to start with, so they will always collide.
For both classes, I have the following code in their init method:
//setup physics body
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.size];
self.physicsBody.dynamic = NO;
self.physicsBody.categoryBitMask = enermyCategory; #shipCategory for ship
self.physicsBody.collisionBitMask = 0;
self.physicsBody.contactTestBitMask = shipCategory; #enermyCategory for ship
What I found is that the NSLog is never get called, so that the physical collision detection never works, I've read a lot from apple developer tutorials and it seems all the same of what they had, not sure why is not working. I can see the ship and energy images on screen collide each other.

self.physicsBody.dynamic = NO;
Static (non-dynamic) bodies do not generate contact events. Make them dynamic.
Also, I see you're passing gameScene to an instance of Enemy. If Enemy has a strong reference to game scene (an ivar) then you may be creating a retain cycle here (enemy retains scene, scene retains enemy).
In Sprite Kit you can simply use self.scene to access the scene and if you need the scene during init, move that code into a setup method and call [ene setup] right after adding it as child to perform setup steps involving the scene.

Having the physicsBody set to dynamic = NO doesn't affect contacts. The more likely case is that you are setting the same contactBitMask and categoryBitMask for both nodes. You should be setting the ship to have the following:
self.physicsBody.categoryBitMask = shipCategory;
self.physicsBody.collisionBitMask = 0;
self.physicsBody.contactTestBitMask = enermyCategory;
The enemy should have the following:
self.physicsBody.categoryBitMask = enermyCategory;
self.physicsBody.collisionBitMask = 0;
self.physicsBody.contactTestBitMask = shipCategory;
Bottom line: they should be swapped in the ship.

Related

Organizing zPosition values in Sprite Kit with enums

We have been using enums to organize the zPositions of our sprites. As we started to add SKNodes with several subsprites to our game, the structure quickly began to break down. Some child sprites that were placed on the screen had to have negative values in order to be beneath other nodes with children. These negative values are hard to keep track of in relation to other sprites in separate enums.
Is there a better way to organize the zPosition of sprites (especially those with sub-sprites) than using enums?
Upon reading more into your issue, it looks like you are using multiple enums to organize your z order. I suggest using a single enum for the z - ordering of your game.
Use layers to organize your scene:
- (instancetype)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.view.ignoresSiblingOrder = YES;
[self addLayers];
}
return self;
}
- (void)addLayers {
self.backgroundLayer = [SKNode node];
self.backgroundLayer.zPosition = 100;
[self addChild:self.backgroundLayer];
self.playerLayer = [SKNode node];
self.playerLayer.zPosition = 300;
[self addChild:self.playerLayer];
self.enemyLayer = [SKNode node];
self.enemyLayer.zPosition = 400;
[self addChild:self.enemyLayer];
// UI elements must be on top of all nodes on the scene
self.uiLayer = [SKNode node];
self.uiLayer.zPosition = 1000;
}
- (void)addBackgrounds {
SKSpriteNode *backgroundNode1 = [SKSpriteNode spriteNodeWithTexture:[self backgroundTexture1]];
backgroundNode1.zPosition = 10;
[self.backgroundLayer addChild:backgroundNode1];
SKSpriteNode *backgroundNode2 = [SKSpriteNode spriteNodeWithTexture:[self backgroundTexture2]];
backgroundNode2.zPosition = 20;
[self.backgroundLayer addChild:backgroundNode2];
}
.... etc

How to test if 2 SKSpriteNodes collide

Hi I am a newbie in IOS Dev.
I would like to text if my hull node and my fallingObject node collide.
Once it collided, I want to remove that fallingObject node.
In my code I created a basket in my Scene. then on a Random point on the screen it creates a fallingObject node.
My goal is once the fallingObject collides (or catched) by the hull (imaginary box inside the basket node). the fallingObject would disappear or removed from the Scene.
Please see my code below:
static const uint32_t boxCategory = 0x1 << 0;
static const uint32_t fallingObjectCategory = 0x1 << 1;
#import "MyScene.h"
- (SKSpriteNode *)newBasket
{
SKSpriteNode *basketImage = [[SKSpriteNode alloc] initWithImageNamed:#"basket-01.png"];
basketImage.size = CGSizeMake(150,120);
SKSpriteNode *hull = [[SKSpriteNode alloc] initWithColor:[SKColor grayColor] size:CGSizeMake(64,32)];
[basketImage addChild:hull];
hull.alpha = 0.0;
hull.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:hull.size];
hull.physicsBody.dynamic = NO;
hull.physicsBody.categoryBitMask = boxCategory;
hull.physicsBody.contactTestBitMask = fallingObjectCategory;
hull.physicsBody.collisionBitMask = boxCategory | fallingObjectCategory;
_basket = basketImage;
_box =hull;
return basketImage;
}
- (void)addFallingObject
{
SKSpriteNode *fallingObject = [[SKSpriteNode alloc] initWithImageNamed:[self getRandomFallingObject]];
//Configure the Size
switch (randomNumber) {
case 0:
fallingObject.size = CGSizeMake(70,45);
break;
case 1:
fallingObject.size = CGSizeMake(30,45);
break;
case 2:
fallingObject.size = CGSizeMake(40,90);
break;
default:
fallingObject.size = CGSizeMake(50,50);
break;
}
fallingObject.position = CGPointMake(skRand(0, self.size.width), self.size.height-50);
fallingObject.name = #"fallingObject";
fallingObject.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:fallingObject.size];
self.physicsWorld.speed = 0.30;
fallingObject.physicsBody.categoryBitMask = fallingObjectCategory;
fallingObject.physicsBody.contactTestBitMask = boxCategory;
fallingObject.physicsBody.collisionBitMask = fallingObjectCategory | boxCategory;
[self addChild:fallingObject];
}
-(void)didBeginContact:(SKPhysicsContact *)contact
{
NSLog(#"contact detected");
SKPhysicsBody *boxBody;
SKPhysicsBody *fallingObjectBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
boxBody = contact.bodyA;
fallingObjectBody = contact.bodyB;
}
else
{
boxBody = contact.bodyB;
fallingObjectBody = contact.bodyA;
}
if ((boxBody.categoryBitMask & boxCategory) !=0 && (fallingObjectBody.categoryBitMask & fallingObjectCategory) !=0)
{
//do whatever you want like remove a node
}
}
thank you for all your help. :D
I have a sprite kit game with two things that collide. One is the player and one is the charge. I set up a category for both like this before all my code. This is before all my imports and stuff.
static const uint32_t playerCategory = 0x1 << 4;
static const uint32_t chargeCategory = 0x1 << 5;
Then when I make each node I use the following.
self.player.physicsBody.categoryBitMask = playerCategory;
self.player.physicsBody.contactTestBitMask = chargeCategory;
self.player.physicsBody.collisionBitMask = chargeCategory | selfCategory;
and for the charge.
_chargedBall.physicsBody.categoryBitMask = chargeCategory;
_chargedBall.physicsBody.contactTestBitMask = playerCategory;
_chargedBall.physicsBody.collisionBitMask = playerCategory | selfCategory | chargeCategory;
This tells the player it can collide with a charge and something I called self, but that is not necessary right now.
Then in didBeginContact I use.
SKPhysicsBody *playerBody, *chargeBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
playerBody = contact.bodyA;
chargeBody = contact.bodyB;
}
else
{
playerBody = contact.bodyB;
chargeBody = contact.bodyA;
}
if ((playerBody.categoryBitMask & playerCategory) !=0 && (chargeBody.categoryBitMask & chargeCategory) !=0)
{
//do whatever you want like remove a node
}
EDIT #1
I actually make my objects in initWithSize.
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size])
{ // a bunch of code
self.player = [[Player alloc] initWithImageNamed:#"character"];
self.player.name = #"MyPlayer";
self.player.position = CGPointMake(self.frame.size.width / 2.0f, 120);
self.player.zPosition = 15;
self.player.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.player.frame.size];
self.player.physicsBody.dynamic = YES;
self.player.physicsBody.affectedByGravity = NO;
self.player.physicsBody.mass = 0.25;
self.player.physicsBody.allowsRotation = NO;
self.player.physicsBody.friction = 0.5;
self.player.physicsBody.restitution = 0.3;
self.player.physicsBody.categoryBitMask = playerCategory;
self.player.physicsBody.contactTestBitMask = chargeCategory;
self.player.physicsBody.collisionBitMask = chargeCategory | selfCategory;
[self.map addChild:self.player];
}
Then I make the other obj in a method.
- (void)spawnChargesWithNumber:(int)number
{ // some code
_chargedBall.position = (CGPointMake(_randomX, random));
_chargedBall.name = [NSString stringWithFormat:#"chargeNumber%i", number];
_chargedBall.zPosition = 15;
_chargedBall.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:13.0f];
_chargedBall.physicsBody.dynamic = YES;
_chargedBall.physicsBody.affectedByGravity = NO;
_chargedBall.physicsBody.mass = 0.1;
_chargedBall.velocity = CGPointMake(0.0, 0.0); //changed actualVelocityX to 0
_chargedBall.physicsBody.categoryBitMask = chargeCategory;
_chargedBall.physicsBody.contactTestBitMask = playerCategory;
_chargedBall.physicsBody.collisionBitMask = playerCategory | selfCategory | chargeCategory;
[self.map addChild:_chargedBall];
}
EDIT #2
This might clear some things up. It is from the Apple Docs.
***categoryBitMask
A mask that defines which categories this physics body belongs to.
#property(assign, nonatomic) uint32_t categoryBitMask
Discussion
Every physics body in a scene can be assigned to up to 32 different categories, each corresponding to a bit in the bit mask. You define the mask values used in your game. In conjunction with the collisionBitMask and contactTestBitMask properties, you define which physics bodies interact with each other and when your game is notified of these interactions.
The default value is 0xFFFFFFFF (all bits set).
Availability
Available in iOS 7.0 and later.
See Also
#property collisionBitMask
#property contactTestBitMask
Declared In
SKPhysicsBody.h
***collisionBitMask
A mask that defines which categories of physics bodies can collide with this physics body.
#property(assign, nonatomic) uint32_t collisionBitMask
Discussion
When two physics bodies contact each other, a collision may occur. This body’s collision mask is compared to the other body’s category mask by performing a logical AND operation. If the result is a non-zero value, then this body is affected by the collision. Each body independently chooses whether it wants to be affected by the other body. For example, you might use this to avoid collision calculations that would make negligible changes to a body’s velocity.
The default value is 0xFFFFFFFF (all bits set).
Availability
Available in iOS 7.0 and later.
See Also
#property categoryBitMask
Declared In
SKPhysicsBody.h
***contactTestBitMask
A mask that defines which categories of bodies cause intersection notifications with this physics body.
#property(assign, nonatomic) uint32_t contactTestBitMask
Discussion
When two bodies share the same space, each body’s category mask is tested against the other body’s contact mask by performing a logical AND operation. If either comparison results in a non-zero value, an SKPhysicsContact object is created and passed to the physics world’s delegate. For best performance, only set bits in the contacts mask for interactions you are interested in.
The default value is 0x00000000 (all bits cleared).
Availability
Available in iOS 7.0 and later.
See Also
#property categoryBitMask
Declared In
SKPhysicsBody.h

SKSpriteNode ignoring physics deactivation

I have a node which is placed under my screen and works as a platform. As soon as another node touches the node a boolean is set to NO.
When I define my node with the SKPhysicsBody properties for the collision the node ignores the affectedByGravity property.
My code:
+ (void)addNewNodeTo:(SKNode *)parentNode
{
//Correct image size
SKSpriteNode *desertBottom = [SKSpriteNode node];
desertBottom = [[SKSpriteNode alloc] initWithImageNamed:#"Giraffe.png"];
desertBottom.position = CGPointMake(0, -200);
desertBottom.zPosition = 2;
desertBottom.physicsBody.collisionBitMask = lionType;
desertBottom.physicsBody.categoryBitMask = terrainType;
desertBottom.physicsBody.contactTestBitMask = lionType;
desertBottom.zPosition = 2;
desertBottom.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(desertBottom.size.width, desertBottom.size.height)];
desertBottom.physicsBody.dynamic = YES;
desertBottom.physicsBody.affectedByGravity = NO;
[parentNode addChild:desertBottom];
}
My collision methods:
- (void)didBeginContact:(SKPhysicsContact *)contact
{
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (lionType | terrainType)) {
self.lionNode.lionIsJumping = NO;
NSLog(#"%i", self.lionNode.lionIsJumping);
}
}
- (void)didEndContact:(SKPhysicsContact *)contact
{
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (lionType | terrainType)) {
self.lionNode.lionIsJumping = YES;
NSLog(#"%i", self.lionNode.lionIsJumping);
}
}
A physics body has a velocity. Gravity changes velocity over time. If the body is already traveling at a certain velocity due to gravity, and you disable gravity, it will continue to move according to its current velocity but no longer gain additional speed from gravity.
My guess is that you expect the body to stop when disabling gravity. If that's what you want you can do this manually by setting the y component of velocity to zero:
SKPhysicsBody* body = desertBottom.physicsBody;
body.velocity = CGVectorMake(body.velocity.x, 0.0);
If the platform should not move after a collision you have to set the dynamic property to false.
Maybe you have an issue with you bit masks?
Have you tried to log outside your if statement of the didBeginContact method?
I have created a new class with subclass of SKSpriteNode for the bottom and added it to my GameScene.

Sprite Kit Collision Detection

Hi I am trying to setup collision detection in my game and I want to add collision detection so when the balloons hit the spikes they pop. I have looked at Ray Wenderliches tutorial but I couldn't figure it out because it didn't apply to my case. Any ideas how to set it up for my case?
The spikes are at the top of the screen and the balloons spawn at the bottom.
The basics for setting upp collision between 2 objects is to first setting upp constant that represent the different objects that can collide. I usually make a constants.h file where i keep all the variables that will be used thru out the game/application.
Declare following in either a constants.h file or just declare them as global variables in the class:
static const int balloonHitCategory = 1;
static const int spikeHitCategory = 2;
What you want to do now is set up the physics for both your balloon and spikes
SKSpriteNode *ballooon = [SKSpriteNode spriteNodeWithImageNamed:#"yourimagefilename"];
ballooon.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:yourSize];
ballooon.physicsBody.categoryBitMask = balloonHitCategory;
ballooon.physicsBody.contactTestBitMask = spikeHitCategory;
ballooon.physicsBody.collisionBitMask = spikeHitCategory;
you should set your size and set your images for both of the spritenodes
SKSpriteNode *spikes = [SKSpriteNode spriteNodeWithImageNamed:#"yourimagefilename"];
spikes.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(yourSizeX, yourSizeY)];
spikes.physicsBody.categoryBitMask = spikeHitCategory;
spikes.physicsBody.contactTestBitMask = balloonHitCategory;
spikes.physicsBody.collisionBitMask = balloonHitCategory;
for collision set up the following method:
-(void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
firstBody = contact.bodyA;
secondBody = contact.bodyB;
if(firstBody.categoryBitMask == spikeHitCategory || secondBody.categoryBitMask == spikeHitCategory)
{
NSLog(#"balloon hit the spikes");
//setup your methods and other things here
}
}
Before the collision will work you should also add the . In your scenes .h file add .
#interface myScene : SKScene <SKPhysicsContactDelegate>
#end
and in the .m file in the init function add:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.physicsWorld.contactDelegate = self;
}
return self;
}
For more about collision handling check out apple documentation and adventure game example:
https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/CodeExplainedAdventure/HandlingCollisions/HandlingCollisions.html#//apple_ref/doc/uid/TP40013140-CH5-SW1
I have just released a plug-in 2D collision engine for Sprite Kit. It's still in its early stages but could be of some help to you: https://github.com/henryeverett/ToastCollisions2D

Collision detection in Sprite Kit

I'm using collision detection in Sprite Kit. It is working and preventing my sprites from crossing paths. However, I'm not getting notifications in didBeginContact: and I don't seem to have any control over how the physics engine responds when a collision occurs.
I have various cars (SKSpriteNodes) moving around following paths using the SKAction followPath:asOffset:orientToPath:duration:
Previously, if two cars crossed paths they would both just continue as normal with one driving over the other. To implement collision detection I have made the following changes...
Added this to my #interface:
<SKPhysicsContactDelegate>
Added this to my #implementation:
static const uint32_t carCategory = 0x1 << 0;
Added this in my init method:
self.physicsWorld.contactDelegate = self;
self.physicsWorld.gravity = CGVectorMake(0.0, 0.0);
I create my blue car:
- (void)addBlueCar
{
_blueCar = [SKSpriteNode spriteNodeWithImageNamed:#"Blue Car.png"];
_blueCar.position = CGPointMake(818.0, -50.0);
_blueCar.name = #"car";
[self addChild:_blueCar];
CGSize contactSize = CGSizeMake(_blueCar.size.width - 5.0, _blueCar.size.height - 5.0);
_blueCar.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:contactSize];
_blueCar.physicsBody.categoryBitMask = carCategory;
_blueCar.physicsBody.collisionBitMask = carCategory;
_blueCar.physicsBody.contactTestBitMask = carCategory;
}
And I also create a red car:
- (void)addRedCar
{
_redCar = [SKSpriteNode spriteNodeWithImageNamed:#"Red Car.png"];
_redCar = CGPointMake(818.0, -50.0);
_redCar = #"car";
[self addChild: _redCar];
CGSize contactSize = CGSizeMake(_blueCar.size.width - 5.0, _blueCar.size.height - 5.0);
_redCar.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:contactSize];
_redCar.physicsBody.categoryBitMask = carCategory;
_redCar.physicsBody.collisionBitMask = carCategory;
_redCar.physicsBody.contactTestBitMask = carCategory;
}
There are other cars as well, but I'm just using these two until I figure out what the problem is. I want to be notified of any collision between any two cars. That's why I'm just using a single category, carCategory.
The first problem is that I get no notifications. I have this yet it never logs anything to the console:
- (void)didBeginContact:(SKPhysicsContact *)contact
{
NSLog(#"Contact");
}
The second problem is that when two cars cross paths they start nudging each other off course. This is ok, but I don't seem to able to manage any aspects of how the collision is handled. The bizarre thing is that nothing changes when I do this to both of my car creation methods:
- (void)addBlueCar
{
_blueCar = [SKSpriteNode spriteNodeWithImageNamed:#"Blue Car.png"];
_blueCar.position = CGPointMake(818.0, -50.0);
_blueCar.name = #"car";
[self addChild:_blueCar];
CGSize contactSize = CGSizeMake(_blueCar.size.width - 5.0, _blueCar.size.height - 5.0);
_blueCar.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:contactSize];
// _blueCar.physicsBody.categoryBitMask = carCategory;
// _blueCar.physicsBody.collisionBitMask = carCategory;
// _blueCar.physicsBody.contactTestBitMask = carCategory;
}
Collision detection still works the same with those three lines commented out for both cars. This just doesn't seem right to me. Collision detection only ceases when I also comment out this line:
_blueCar.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:contactSize];
So my main question is: why is the contactTest not reporting anything? My second question is: why is collision detection happening when I don't assign a categoryBitMask, collisionBitMask or contactTestBitMask to any car?
collisionBitMask's default value is 0xFFFFFFFF (all bits set). Therefore the node will collide with each physicBody on the scene.
Just a small observation, perhaps you should setup your nodes and set the contact delegate in the scene's didMoveToView method.
If that doesn't solve it, any chance you can post some working code to github for debugging?
Something that helped me with this: Collisions and contacts are different things.
Collision is handled by the physics engine and setting collisionBitMask will determine which bodies collide and interact with each other in the physics world. By default, all your bodies will collide with each other, which kinda makes sense.
Contacts are events- when bodies make contact, will they create a notification that you can use in the delegate method to do more interesting things. If you have a ball you want to hit with a paddle, you want them to collide at a minimum. But if you have a ball moving through a cloud, you probably don't want them to collide in the physics world, but you might want to register the contact.

Resources