SK Game - didBeginContact/didEndContact isn't getting called - ios

I'm making a drag and drop game, and attempting to use physicsBodies to detect contacts between 1 static object and the object being dragged by the user.
I've searched on here for hours, and watched tutorials online, but all I keep seeing is contact detection working with dynamic nodes (such as projectiles). I don't need collisions because nothing happens to any of the nodes when they intersect, I just need contact detection to implement game logic.
I originally had a working solution using CGRectContainsPoint and CGRectContainsRect, but it's super buggy and after doing some research I realized that it's not the correct way to implement contact/collisions in SK.
I've got my bitMask setup like so:
//contact bit masks
typedef NS_OPTIONS(uint32_t, ContactCategory) {
ContactCategoryTile = 1 << 0, //0000
ContactCategoryKey = 1 << 1, //0010
ContactCategoryRotor = 1 << 2, //0100
};
My selected node setup like so:
#import "TileNode.h"
#import "Utilities.h"
#interface TileNode ()
#end
#implementation TileNode
+(instancetype)tileNodeAtPosition:(CGPoint)position
tileComboScore:(NSInteger)comboScore
tileArray:(NSArray*)array
randomNumber:(int)randomNumber
initialCombo:(NSInteger)initCombo
{
//custom node properties
TileNode *tileNode = [array objectAtIndex:randomNumber];
tileNode.position = position;
tileNode.size = CGSizeMake(23, 63);
//using label property from .h file and parenting to generated tileNode
tileNode.comboLabel = [SKLabelNode labelNodeWithFontNamed:#"Arial"];
tileNode.comboLabel.fontColor = [UIColor whiteColor];
tileNode.comboLabel.text = [NSString stringWithFormat: #"%d",initCombo];
tileNode.comboLabel.fontSize = 12;
tileNode.comboLabel.position = CGPointMake(13, 45);
[tileNode setupPhysicsBody];
[tileNode addChild:tileNode.comboLabel];
return tileNode;
}
-(void)setupPhysicsBody
{
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.frame.size];
NSLog(#"tileframe %#", NSStringFromCGRect(self.frame));
self.physicsBody.dynamic = NO;
self.physicsBody.categoryBitMask = ContactCategoryTile;
self.physicsBody.contactTestBitMask = ContactCategoryTile | ContactCategoryRotor | ContactCategoryKey;
self.physicsBody.collisionBitMask = 0;
}
My destination node type setup up like so:
#import "KeyNode.h"
#import "Utilities.h"
#implementation KeyNode
+(instancetype)keyNodeAtPosition:(CGPoint)position
{
//custom class properties for generic yellow nodes in UI
KeyNode *keyTile = [KeyNode spriteNodeWithImageNamed:#"key"];
keyTile.position = position;
keyTile.size = CGSizeMake(25, 65);
keyTile.anchorPoint = CGPointMake(0.5, 0.5);
keyTile.name = #"keyNode";
[keyTile setupPhysicsBody];
return keyTile;
}
-(void)setupPhysicsBody
{
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.frame.size];
NSLog(#"keyframe %#", NSStringFromCGRect(self.frame));
self.physicsBody.dynamic = NO;
self.physicsBody.categoryBitMask = ContactCategoryKey;
self.physicsBody.contactTestBitMask = ContactCategoryTile;
self.physicsBody.collisionBitMask = 0;
}
And my contact detection delegate method like so:
-(void)didEndContact:(SKPhysicsContact *)contact
{
NSLog(#"Working!");
//Condition for tile and key contact
if (contact.bodyA.categoryBitMask == ContactCategoryTile && contact.bodyB.categoryBitMask == ContactCategoryKey)
{
NSLog(#"Tile contact with key");
}
//condition for tile and tile contact
else if (contact.bodyA.categoryBitMask == ContactCategoryTile && contact.bodyB.categoryBitMask == ContactCategoryTile)
{
NSLog(#"Tile contact with tile");
}
//condition for tile and rotor contact
else if(contact.bodyA.categoryBitMask == ContactCategoryTile && contact.bodyB.categoryBitMask == ContactCategoryRotor)
{
NSLog(#"Tile contact with rotor");
}
}
And both nodes are being instantiated in initWithSize like so:
CGPoint position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) - 180);
TileNode *tileNode1 = [TileNode tileNodeAtPosition:position tileComboScore:self.comboScore tileArray:tileImagesArray randomNumber:randomTileGenerator initialCombo:self.initialCombo];
[self addChild:tileNode1];
KeyNode *keyNode1 = [KeyNode keyNodeAtPosition:CGPointMake(CGRectGetMidX(self.frame) - 100, CGRectGetMidY(self.frame) - 15)];
[self addChild:keyNode1];
Any help would be greatly appreciated.

I found the answer in a thread from last year that worked. You can work around wanting static bodies to have collisions.
If you set the gravity to be non-existent and make your static nodes dynamic it works:
self.physicsWorld.gravity = CGVectorMake(0, 0);
self.physicsBody.dynamic = YES;

Related

How to add physicsBody to tiles generated by JSTileMap and change them into one physicsBody

I use JSTileMap to draw a level. I changed it a little bit to add SKPhysicsBody to every tile but sometimes when I apply some impulse to main character and he hits the wall/ground/ceiling he is acting weird. He reflects from surfaces with the way that is contrary to the principles of physics. I think that it happens because the player hits the point where two physics bodies (for example two physics bodies of the ground) connects.
SKPhysicsBody class provides a method to create one physics body from different physics bodies + (SKPhysicsBody *)bodyWithBodies:(NSArray *)bodies; Can I use this method to create one physics body from ale the tiles physics bodies?
Here's a method from JSTileMap where I add physics bodies:
+(id) layerWithTilesetInfo:(NSArray*)tilesets layerInfo:(TMXLayerInfo*)layerInfo mapInfo:(JSTileMap*)mapInfo
{
TMXLayer* layer = [TMXLayer node];
layer.map = mapInfo;
layer.tilesByColumnRow = [NSMutableDictionary dictionary];
// basic properties from layerInfo
layer.layerInfo = layerInfo;
layer.layerInfo.layer = layer;
layer.mapTileSize = mapInfo.tileSize;
layer.alpha = layerInfo.opacity;
layer.position = layerInfo.offset;
// recalc the offset if we are isometriic
if (mapInfo.orientation == OrientationStyle_Isometric)
{
layer.position = CGPointMake((layer.mapTileSize.width / 2.0) * (layer.position.x - layer.position.y),
(layer.mapTileSize.height / 2.0) * (-layer.position.x - layer.position.y));
}
NSMutableDictionary* layerNodes = [NSMutableDictionary dictionaryWithCapacity:tilesets.count];
//MY CODE
NSMutableArray *arrayOfSprites = [[NSMutableArray alloc] init];
SKNode *theSprite;
//----||----
// loop through the tiles
for (NSInteger col = 0; col < layerInfo.layerGridSize.width; col++)
{
for (NSInteger row = 0; row < layerInfo.layerGridSize.height; row++)
{
// get the gID
NSInteger gID = layerInfo.tiles[col + (NSInteger)(row * layerInfo.layerGridSize.width)];
// mask off the flip bits and remember their result.
bool flipX = (gID & kTileHorizontalFlag) != 0;
bool flipY = (gID & kTileVerticalFlag) != 0;
bool flipDiag = (gID & kTileDiagonalFlag) != 0;
gID = gID & kFlippedMask;
// skip 0 GIDs
if (!gID)
continue;
// get the tileset for the passed gID. This will allow us to support multiple tilesets!
TMXTilesetInfo* tilesetInfo = [mapInfo tilesetInfoForGid:gID];
[layer.tileInfo addObject:tilesetInfo];
if (tilesetInfo) // should never be nil?
{
SKTexture* texture = [tilesetInfo textureForGid:gID];
SKSpriteNode* sprite = [SKSpriteNode spriteNodeWithTexture:texture];
sprite.name = [NSString stringWithFormat:#"%ld",(long)(col + row * layerInfo.layerGridSize.width)];
// make sure it's in the right position.
if (mapInfo.orientation == OrientationStyle_Isometric)
{
sprite.position = CGPointMake((layer.mapTileSize.width / 2.0) * (layerInfo.layerGridSize.width + col - row - 1),
(layer.mapTileSize.height / 2.0) * ((layerInfo.layerGridSize.height * 2 - col - row) - 2) );
}
else
{
sprite.position = CGPointMake(col * layer.mapTileSize.width + layer.mapTileSize.width/2.0,
(mapInfo.mapSize.height * (tilesetInfo.tileSize.height)) - ((row + 1) * layer.mapTileSize.height) + layer.mapTileSize.height/2.0);
}
// flip sprites if necessary
if(flipDiag)
{
if(flipX)
sprite.zRotation = -M_PI_2;
else if(flipY)
sprite.zRotation = M_PI_2;
}
else
{
if(flipY)
sprite.yScale *= -1;
if(flipX)
sprite.xScale *= -1;
}
// add sprite to correct node for this tileset
SKNode* layerNode = layerNodes[tilesetInfo.name];
if (!layerNode) {
layerNode = [[SKNode alloc] init];
layerNodes[tilesetInfo.name] = layerNode;
}
//adding physicsbody to every tile
//sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(sprite.frame.size.width, sprite.frame.size.height)];
//sprite.physicsBody.dynamic = NO;
//sprite.physicsBody.categoryBitMask = mapCategory;
//[arrayOfSprites addObject:sprite.physicsBody];
[layerNode addChild:sprite];
NSUInteger indexes[] = {col, row};
NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2];
[layer.tilesByColumnRow setObject:sprite forKey:indexPath];
//MY CODE
sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite.frame.size];
sprite.physicsBody.dynamic = NO;
[arrayOfSprites addObject:sprite.physicsBody];
//-----||------
#ifdef DEBUG
// CGRect textRect = [texture textureRect];
// NSLog(#"atlasNum %2d (%2d,%2d), gid (%d,%d), rect (%f, %f, %f, %f) sprite.pos (%3.2f,%3.2f) flipx%2d flipy%2d flipDiag%2d", gID+1, row, col, [tilesetInfo rowFromGid:gID], [tilesetInfo colFromGid:gID], textRect.origin.x, textRect.origin.y, textRect.size.width, textRect.size.height, sprite.position.x, sprite.position.y, flipX, flipY, flipDiag);
#endif
}
}
}
//MY CODE
NSArray *array = [arrayOfSprites copy];
theSprite = [SKNode node];
theSprite.physicsBody = [SKPhysicsBody bodyWithBodies:array];
theSprite.physicsBody.dynamic = NO;
theSprite.position = CGPointMake(layer.position.x+16, layer.position.y+16);
[layer addChild:theSprite];
//-----||------
// add nodes for any tilesets that were used in this layer
for (SKNode* layerNode in layerNodes.allValues) {
if (layerNode.children.count > 0) {
[layer addChild:layerNode];
}
}
[layer calculateAccumulatedFrame];
return layer;
}
After adding physics bodies to every tile and adding those physics bodies to NSMutableArray I assign a copy of this NSMutableArray to the NSArray and try to create one physics body out of it like this:
//MY CODE
NSArray *array = [arrayOfSprites copy];
theSprite = [SKNode node];
theSprite.physicsBody = [SKPhysicsBody bodyWithBodies:array];
theSprite.physicsBody.dynamic = NO;
theSprite.position = CGPointMake(layer.position.x+16, layer.position.y+16);
[layer addChild:theSprite];
//-----||------
In result, one physics body with the height and width of one tile is added.
If you want to use [SKPhysicsBody bodyWithBodies:array], then you need to make sure that all the bodies in the array are relative to the parent node.
sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite.frame.size]; means that your physics body position is relative to the sprite node. You need the body relative to the parent node.
The only way I know how to do this is with center:
sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite.frame.size center:sprite.position];
This should place the SKPhysicsBody at the location of the sprite when it is added to the parent node.

How can i make a node collide with the sides of moving scene?

Hi i wish to know how can my nodes can collide with a moving scene. I have a main node that cans move along the x axis and cans collide with other nodes. I want to make the non main nodes collide with the sides of the moving scene. How can i make that ? ( the camera only follow the main node).
I have an obstacle class.
in my MLObstacle.m
-(id)init
{
if(self = [super init])
{
int aux = arc4random()%3;
switch (aux) {
case 0:
self = [MLObstaculo spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake((aux+1)*10,(aux+1)*13 )];
break;
case 1:
self = [MLObstaculo spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake((aux+1)*10,(aux+1)*15 )];
break;
case 2:
self = [MLObstaculo spriteNodeWithColor:[UIColor redColor] size:CGSizeMake((aux+1)*10,(aux+1)*14 )];
break;
}
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.size];
self.physicsBody.affectedByGravity = NO;
self.physicsBody.categoryBitMask = obstacleCategory;
self.physicsBody.contactTestBitMask = visibleSidesCategory;
self.physicsBody.collisionBitMask = visibleSidesCategory;
[self setDx:0.0];
[self setDy:2.4];
}
return self;
}
So i add Obstacles to my WorldClass
In my WorlGen.m
-(void)GenerateWorld
{
SKSpriteNode *Ground = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake(self.scene.frame.size.width,100)];
Ground.position = CGPointMake(0, -self.scene.frame.size.height/2 + Ground.size.height/2);
Ground.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:Ground.size];
Ground.physicsBody.dynamic = NO;
[self addChild:Ground];
for(int i = 0; i < 3; i++)
{
MLObstaculo * auxobs= [[MLObstaculo alloc]initObstaculo];
auxobs.position = CGPointMake((i+1)*50, Ground.position.y + Ground.size.height/2 +auxobs.size.height/2 );
[self.Obstaculos addObject:auxobs];
[self addChild:auxobs];
auxobs = NULL;
}
}
and then i add the world to my scene
Scene.m
-(id)initWithSize:(CGSize)size
{
if(self = [super initWithSize:size]
{
WorldGen * world = [[WorldGen alloc] init];
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsWorld.contactDelegate = self;
self.physicsBody.categoryBitMask = VisibleSidesCategory
self.physicsBody.collisionBitMask = ObstacleCategory;
self.anchorPoint = CGPointMake(0.5,0.5);
[self addChild:world];
[world GenerateWorld];
}
}
Update...
-(void)didSimulatePhysics
{
[self centerOnNode:hero];
}
-(void)centerOnNode:(SKNode *)node
{
CGPoint pointinScene = [self convertPoint:node.position fromNode:node.parent];
world.position = CGPointMake(world.position.x -pointinScene.x , world.position.y);
}
I initalized all the category variables with this format ( static const uint32_t ObstacleCategory = 0x1 <<1; )
First edit by defining your collisionBitMask
-(id)init
{
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.size];
self.physicsBody.categoryBitMask = ObstacleCategory;
self.physicsBody.contactTestBitMask = VisibleSidesCategory;
self.physicsBods.collisionBitMask = VisibleSidesCategory;
}
Second make this steps in Xcode -> File -> New -> File -> C and C++ -> Header File
Give a name of Physics.h and add those lines in it:
typedef enum : uint8_t {
ObstacleCategory = 1,
VisibleSidesCategory = 2,
} ColliderType;
and then import Physics.h into all your classes (You don't need to alloc or init for Physics.h, just use ObstacleCategory and VisibleSidesCategory)
From this and another question's problem, I have finally determined there is an error in the API. Specifically, a node's anchorPoint is considered (0,0) during initialization if using a WithRect: or FromRect: based initializer(but not a RectOfSize: or center: based one), so you get unexpected behavior.
Thus, even when the anchorPoint of the Scene and other objects are set to (0.5,0.5), a node places its lower left corner relative to the parent node's midpoint, rather than placing the child's midpoint relative to the parent node's midpoint.
Specifically, this can be solved in any one of these three ways:
Use an anchorPoint of (0,0).
Create your nodes without using Rect based initializers. You can use shapeNodeWithRectOfSize: instead of shapeNodeWithRect: and bodyWithEdgeLoopFromPath: instead of bodyWithEdgeLoopFromRect:
Set the Rect's initial position as lower and to the left, like so:
node.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(-node.size.width/2, -node.size.height/2, node.size.width, node.size.height)];

How to make balls ignore collisions with each other

With sprite kit, I'm trying to make series of balls drop from the air through physics simulation, and I want them to drop without colliding with each other and bounce off. I just want them to go straight down and pass through each other. How should I make this happen with collisionBitMask and categoryBitMask ?
// Common.h
#ifndef Rainy_Poops_common_h
#define Rainy_Poops_common_h
static int poopSize_x = 20;
static int poopSize_y = 20;
static const uint32_t poopCategory = 0x1 << 1;
#endif
// MyScene.h
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
_timer++;
if (_timer % 3 == 0) {
SKSpriteNode * p = [[Poop alloc] init];
NSInteger random_x = arc4random_uniform([[UIScreen mainScreen]applicationFrame].size.width);
p.position = CGPointMake(random_x, [[UIScreen mainScreen]applicationFrame].size.height - 5);
[self addChild: p];
[self enumerateChildNodesWithName:#"poop" usingBlock:
^(SKNode *node, BOOL *stop) {
Poop *poop = (Poop *) node;
if (!poop.isDropping) {
poop.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(poopSize_x, poopSize_y)];
poop.isDropping = YES;
}
if (poop.position.y < 0) {
[poop removeFromParent];
}
}];
}
}
#import "Poop.h"
#implementation Poop
- (id)init {
self = [super initWithImageNamed:#"poop2.png"];
self.isDropping = NO;
self.name = #"poop";
self.size = CGSizeMake(poopSize_x, poopSize_y);
SKPhysicsBody *physicsPoop = self.physicsBody;
physicsPoop.collisionBitMask = 0;
physicsPoop.categoryBitMask = poopCategory;
physicsPoop.affectedByGravity = YES;
physicsPoop.mass = 100;
physicsPoop.allowsRotation = NO;
physicsPoop.dynamic = YES;
return self;
}
#end
I think your problem is related to this line -
poop.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(poopSize_x,poopSize_y)];
You initialise your categoryBitMask to 2 and collisionBitMask to 0 in the init of your node, but this line will reset the physicsBody for your poop and assign the default collisionBitMask and categoryBitMask values of 0xFFFFFFFF
set collisionButMask equal to 0 for no collision or equal to the categoryBitMask of the sprite you would like the balls to collide with.

bug with regular collisions in spriteKit

Im making a very simple jumping game. When my "hero" collides with the ground it calls didBeginContact and make a jump. Pretty basic but it doesn't work well because after a while (sometimes sooner, sometimes later...) it stop working as you can see. I would like to know if Im doing something wrong or if there is a better way to do it, or if there is a known bug in spriteKit Id never heard of...
The image is a gif. If can't see movement open it in a new tab
#interface XYZWorld1()
#property (nonatomic) SKSpriteNode *hero;
#property (nonatomic) SKSpriteNode *groundPhysics;
#end
#implementation XYZWorld1
static const uint32_t ground = 0x2 << 2;
static const uint32_t bee = 0x3 << 3;
Method to create the ground:
-(SKSpriteNode*)createGroundxPos:(float)x yPos:(float)y width:(CGFloat)width height:(CGFloat)height
{
SKSpriteNode *ground = [SKSpriteNode spriteNodeWithColor:[UIColor colorWithHue:1 saturation:1 brightness:1 alpha:1] size:CGSizeMake(width, height)];
SKPhysicsBody *groundFisica =[SKPhysicsBody bodyWithRectangleOfSize:ground.size];
[ground setName:#"ground"];
[groundFisica setDynamic:NO];
[groundFisica setAffectedByGravity:NO];
[groundFisica setAllowsRotation:NO];
[groundFisica setRestitution:0];
[groundFisica setUsesPreciseCollisionDetection:YES];
[groundFisica setCategoryBitMask:ground];
[groundFisica setContactTestBitMask:bee];
[groundFisica setCollisionBitMask:bee];
ground.physicsBody = groundFisica;
[ground setPosition:CGPointMake(x, y)];
return ground;
}
Method to create hero
-(SKSpriteNode*)crearHero
{
SKTexture *temp = _heroWalkingFrames[0];
SKSpriteNode *hero = [SKSpriteNode spriteNodeWithTexture:temp];
hero.name = #"hero";
hero.zPosition = 1;
if(IS_IPHONE_5){
[hero setPosition:CGPointMake(100, 280)];
}
else {
[hero setPosition:CGPointMake(100, 220)];
}
[hero setSize:CGSizeMake(25, 25)];
hero.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(23, 24)];
[hero.physicsBody setDensity:0.95];
[hero.physicsBody setDynamic:YES];
[hero.physicsBody setAffectedByGravity:YES];
[hero.physicsBody setAllowsRotation:NO];
[hero.physicsBody setUsesPreciseCollisionDetection:YES];
[hero.physicsBody setRestitution:0];
[hero.physicsBody setVelocity:CGVectorMake(0, 0)];
return hero;
}
Method to preload all the stuff before the game starts:
-(void)preloadAssets
{
//hero
_heroWalkingFrames = [self cargarArrayAtlas:#"astroJump.atlas" imageName:#"astronautaJump_000"];
_hero = [self crearHero];
_hero.physicsBody.categoryBitMask = bee;
_hero.physicsBody.collisionBitMask = bee | cubico | ground | bicho ;
_hero.physicsBody.contactTestBitMask = bee | cubico | ground | bicho ;
//Ground
_groundPhysics = [self createGroundxPos:100 yPos:142 width:42 height:23.25];
}
Method called when two bodies first contact each other:
-(void) didBeginContact:(SKPhysicsContact *)contact
{
//if hero collides with ground
if((contact.bodyA.categoryBitMask == bee && contact.bodyB.categoryBitMask == ground) ||
(contact.bodyA.categoryBitMask == ground && contact.bodyB.categoryBitMask == bee) )
{
if(_jumping == NO && _groundPhysics.position.y+_groundPhysics.size.height/2<=_hero.position.y-11.5)
{
[self jumping];
}
}
}
Method called to do the jump:
-(void)jumping{
//Play sound file
[self runAction:self.jumpSound withKey:#"jumpSound"];
if(IS_IPHONE_5){
_diferencia=(164 * 9)/_hero.position.y;
}else{
_diferencia=(164 * 7)/_hero.position.y;
}
[_hero.physicsBody applyImpulse:CGVectorMake(0,_diferencia)];
_jumping = YES;
[_hero removeActionForKey:#"astroJump"];
[self astroJump];
}
-(void)astroJump
{
//runAction animation method
[_hero runAction:[SKAction animateWithTextures:_heroWalkingFrames
timePerFrame:0.05f
resize:NO
restore:NO] withKey:#"astroJump"];
return;
}
the boolean (self.jumping) from the if sentence, in update and initGameContent:
-(void)update:(CFTimeInterval)currentTime
{
if(_hero.physicsBody.velocity.dy < 0 )_jumping = NO;
}
-(void)initGameContent
{
_jumping = NO;
You might want to make sure you reset your players position to be above the ground when contact with the ground is detected. Otherwise the player could still be in contact with the ground when [self jumping]; is called which can sometimes result in the player being "stuck". I've seen similar issues in my games when doing collision detection. The code below shows what you could do to resolve this issue.
if (_jumping == NO && _groundPhysics.position.y+_groundPhysics.size.height/2<=_hero.position.y-11.5)
{
_hero.position.y = _groundPhysics.position.y + _groundPhysics.size.height/2 + 1;
[self jumping];
}
I finally found a way to fix it. I've changed the size of physicbody.size.y to the same as his spriteNode.y and the bug has gone.

didBeginContact doesn't fire

Any idea why my didBeginContact method is never firing? The sphere and rectangles are actually touching each other...
Here i declare the two categories
static const uint32_t rectangleCategory = 0x1 << 0;
static const uint32_t ballCategory = 0x1 << 1;
This method creates a sphere
- (SKSpriteNode *)createSphereNode
{
SKSpriteNode *sphereNode =
[[SKSpriteNode alloc] initWithImageNamed:#"sphere.png"];
sphereNode.name = #"sphereNode";
sphereNode.physicsBody.categoryBitMask = ballCategory;
sphereNode.physicsBody.contactTestBitMask = rectangleCategory;
return sphereNode;
}
This method creates a rectangle
- (void) createRectangle
{
SKSpriteNode *rectangle = [[SKSpriteNode alloc] initWithImageNamed:#"balk.png"];
rectangle.position = CGPointMake(randomBetween(0, self.size.width),
self.size.height);
rectangle.name = #"rectangleNode";
rectangle.physicsBody =
[SKPhysicsBody bodyWithCircleOfRadius:(rectangle.size.width/2)-7];
rectangle.physicsBody.usesPreciseCollisionDetection = YES;
rectangle.physicsBody.categoryBitMask = rectangleCategory;
rectangle.physicsBody.contactTestBitMask = ballCategory;
[self addChild:rectangle];
}
This is the didBeginContact method that doesn't fire
- (void) didBeginContact:(SKPhysicsContact *)contact
{
NSLog(#"Contact begin");
SKSpriteNode *firstNode, *secondNode;
firstNode = (SKSpriteNode *)contact.bodyA.node;
secondNode = (SKSpriteNode *) contact.bodyB.node;
if ((contact.bodyA.categoryBitMask == rectangleCategory)
&& (contact.bodyB.categoryBitMask == ballCategory))
{
CGPoint contactPoint = contact.contactPoint;
float contact_x = contactPoint.x;
float target_x = secondNode.position.x;
float margin = secondNode.frame.size.height/2 - 25;
if ((contact_x > (target_x - margin)) &&
(contact_x < (target_x + margin)))
{
NSLog(#"Hit");
self.score++;
}
}
}
Any ideas? All help is much appreciated!
Thanks in advance
Stijn
I originally suspected it could be the lack of a proper SKPhysicsContactDelegate as it was not mentioned in the original post. Upon inspection of the code however, this was not the case and the problem was simply that the SKPhysicsBody used for rectangle was created with the ill-fitting bodyWithCircleOfRadius: rather than the bodyWithRectangleOfSize:.
rectangle.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize: rectangle.size];
Original answer, not correct for this particular case, but leaving it here for anyone with a similar problem down the line:
There is no mention of the SKPhysicsContactDelegate anywhere to be seen in the code you supply. The didBeginContact: method is a delegate method for the physicsWorld property of your SKScene.
So if you for instance want to have the SKScene being its own delegate it must support the SKPhysicsContactDelegate protocol. e. g.:
#interface YourSceneSubclass : NSObject <SKPhysicsContactDelegate>
Then you need to set the delegate somewhere in your scene before the game starts proper...
self.physicsWorld.contactDelegate = self;

Resources