2
votes

Please help me, I've been trying to solve this problem for 24 hours and I'm going crazy:) I am trying to detect collision in Sprite Kit through the didBeginContact delegate method, but this method is not fired, I've tried all the answers on the Internet

#import "MyScene.h"
#import "GameOverScene.h"


static const uint32_t basketCategory      =  1 << 0;
static const uint32_t ballCategory        =  1 << 1;


// 1
@interface MyScene () <SKPhysicsContactDelegate>
//@interface MyScene ()
@property BOOL contentCreated;
@property (nonatomic) SKSpriteNode * basket;
@property (nonatomic) SKSpriteNode * ball;
@property (nonatomic) NSTimeInterval lastSpawnTimeInterval;
@property (nonatomic) NSTimeInterval lastUpdateTimeInterval;
@property (nonatomic) int ballDestroyed;
@property (nonatomic) int score;
@end

static inline CGPoint rwAdd(CGPoint a, CGPoint b) {
    return CGPointMake(a.x + b.x, a.y + b.y);
}

static inline CGPoint rwSub(CGPoint a, CGPoint b) {
    return CGPointMake(a.x - b.x, a.y - b.y);
}

static inline CGPoint rwMult(CGPoint a, float b) {
    return CGPointMake(a.x * b, a.y * b);
}

static inline float rwLength(CGPoint a) {
    return sqrtf(a.x * a.x + a.y * a.y);
}

// Makes a vector have a length of 1
static inline CGPoint rwNormalize(CGPoint a) {
    float length = rwLength(a);
    return CGPointMake(a.x / length, a.y / length);
}



@implementation MyScene
float balltime;

-(id)initWithSize:(CGSize)size {    
    if (self = [super initWithSize:size]) {
        /* Setup your scene here */

    }
    return self;
}


- (void)didMoveToView:(SKView *)view
{

        if (!self.contentCreated) {
            self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
            self.physicsWorld.gravity = CGVectorMake(0,0);
            self.physicsWorld.contactDelegate = self;
            self.basket = [SKSpriteNode spriteNodeWithImageNamed:@"basket"];
            self.basket.size = CGSizeMake(100, 100);
            self.basket.position = CGPointMake(self.frame.size.width/2, self.basket.size.height/2);
            self.basket.physicsBody.dynamic = YES; // 2
            self.basket.physicsBody.categoryBitMask = basketCategory; // 3
            self.basket.physicsBody.contactTestBitMask = ballCategory; // 4
            self.basket.physicsBody.collisionBitMask = 0; // 5
            self.basket.physicsBody.usesPreciseCollisionDetection = YES;
            [self addChild:self.basket];
         //   self.physicsWorld.contactDelegate = self;
        }

}





- (void)addball {
    balltime=1;
    // Create sprite
    self.ball = [SKSpriteNode spriteNodeWithImageNamed:@"basketball"];
    self.ball.size = CGSizeMake(100, 100);
    self.ball.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.ball.size]; // 1
    self.ball.physicsBody.dynamic = YES; // 2
    self.ball.physicsBody.categoryBitMask = ballCategory; // 3
    self.ball.physicsBody.contactTestBitMask = basketCategory; // 4
    self.ball.physicsBody.collisionBitMask = 0; // 5
    self.ball.physicsBody.usesPreciseCollisionDetection = YES;
  //  self.physicsWorld.contactDelegate = self;

    // Determine where to spawn the ball along the x axis
    int minX = self.ball.size.width / 2;
    int maxX = self.frame.size.width - self.ball.size.width / 2;
    int rangeX = maxX - minX;
    int actualX = (arc4random() % rangeX) + minX;

    // Create the monster slightly off-screen along the right edge,
    // and along a random position along the X axis as calculated above
   // ball.position = CGPointMake(actualX,self.frame.size.height + ball.size.height/2);
    self.ball.position = CGPointMake(actualX,self.frame.size.height + 300);
    [self addChild:self.ball];

    // Determine speed of the ball
    int minDuration = 3.99;
    int maxDuration = 4.0;
    int rangeDuration = maxDuration - minDuration;
    int actualDuration = (arc4random() % rangeDuration) + minDuration;

    // Create the actions
    SKAction * actionMove = [SKAction moveTo:CGPointMake(actualX,-self.ball.size.height/2) duration:actualDuration];
    SKAction * actionMoveDone = [SKAction removeFromParent];
    SKAction * loseAction = [SKAction runBlock:^{
        SKTransition *reveal = [SKTransition flipHorizontalWithDuration:0.5];
        SKScene * gameOverScene = [[GameOverScene alloc] initWithSize:self.size won:NO];
        [self.view presentScene:gameOverScene transition: reveal];
    }];
    [self.ball runAction:[SKAction sequence:@[actionMove,  actionMoveDone]]];

}

- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast {

    self.lastSpawnTimeInterval += timeSinceLast;
    if (self.lastSpawnTimeInterval > balltime) {
        self.lastSpawnTimeInterval = 0;
        [self addball];
    }
}

- (void)update:(NSTimeInterval)currentTime {
    // Handle time delta.
    // If we drop below 60fps, we still want everything to move the same distance.
    CFTimeInterval timeSinceLast = currentTime - self.lastUpdateTimeInterval;
    self.lastUpdateTimeInterval = currentTime;
    if (timeSinceLast > 1) { // more than a second since last update
        timeSinceLast = 1.0 / 60.0;
        self.lastUpdateTimeInterval = currentTime;
    }

    [self updateWithTimeSinceLastUpdate:timeSinceLast];

}



- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {

    UITouch *touch = [touches anyObject];
    CGPoint positionInScene = [touch locationInNode:self];
    self.basket.position=positionInScene;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

    UITouch *touch = [touches anyObject];
    CGPoint positionInScene = [touch locationInNode:self];
    self.basket.position=positionInScene;
}




- (void)ball:(SKSpriteNode *)ball didCollideWithbasket:(SKSpriteNode *)basket {
    NSLog(@"Hit");
    [self.ball removeFromParent];
    self.ballDestroyed++;
    if (self.ballDestroyed > 20) {
        SKTransition *reveal = [SKTransition flipHorizontalWithDuration:0.5];
        SKScene * gameOverScene = [[GameOverScene alloc] initWithSize:self.size won:YES];
        [self.view presentScene:gameOverScene transition: reveal];
    }
}

- (void)didBeginContact:(SKPhysicsContact *)contact
{

        NSLog(@"contact");
    // 1
    SKPhysicsBody *firstBody, *secondBody;

    if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
    {
        firstBody = contact.bodyA;
        secondBody = contact.bodyB;
    }
    else
    {
        firstBody = contact.bodyB;
        secondBody = contact.bodyA;
    }

    // 2
    if ((firstBody.categoryBitMask & basketCategory) != 0 &&
        (secondBody.categoryBitMask & ballCategory) != 0)
    {
        [self ball:(SKSpriteNode *) firstBody.node didCollideWithbasket:(SKSpriteNode *) secondBody.node];
    }
}
3

3 Answers

2
votes

Try moving the <SKPhysicsContactDelegate> to your MyScene.h file, and changing your category bit masks to look like this. static const uint32_t basketCategory = 0x1 << 0; and make sure you set the collision bit mask. I have found that if the collision bit mask is not set, it will not trigger the contact method.

2
votes

You're forgetting to create the ball's physicsBody as it doesn't get created automatically for you. So your code

        self.basket = [SKSpriteNode spriteNodeWithImageNamed:@"basket"];
        self.basket.size = CGSizeMake(100, 100);
        self.basket.position = CGPointMake(self.frame.size.width/2, self.basket.size.height/2);
        self.basket.physicsBody.dynamic = YES; // 2
        self.basket.physicsBody.categoryBitMask = basketCategory; // 3
        self.basket.physicsBody.contactTestBitMask = ballCategory; // 4
        self.basket.physicsBody.collisionBitMask = 0; // 5
        self.basket.physicsBody.usesPreciseCollisionDetection = YES;
        [self addChild:self.basket];

is not actually doing anything to the self.basket.physicsBody because self.basket.physicsBody is nil.

You should change your code to become

        self.basket = [SKSpriteNode spriteNodeWithImageNamed:@"basket"];
        self.basket.size = CGSizeMake(100, 100);
        self.basket.position = CGPointMake(self.frame.size.width/2, self.basket.size.height/2);

        // Next line is new
        self.basket.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.basket.frame.size];

        self.basket.physicsBody.dynamic = YES; // 2
        self.basket.physicsBody.categoryBitMask = basketCategory; // 3
        self.basket.physicsBody.contactTestBitMask = ballCategory; // 4
        self.basket.physicsBody.collisionBitMask = 0; // 5
        self.basket.physicsBody.usesPreciseCollisionDetection = YES;
        [self addChild:self.basket];

That should do the trick!

0
votes

Add

self.physicsWorld.contactDelegate = self;

at the end of

-(id)initWithSize:(CGSize)size

before return self.