Search code examples
objective-csprite-kitcollision-detection

IOS SpriteKit Colision Detection: Resetting Position of an Object


I'm working on Collision Detection where the hope is when Object1 moves down the screen and eventually hits Object2 it triggers the didBeginContact method and in turn, the resetPosition on Object1 which will bring Object1 back to the top of the screen. I've used NSLogs to test to make sure if the program reaches the didBeginContact method, and it does. On top of that, the program also works through the called upon method (resetPosition) as well. The only problem is, it does not change the position of Object1. I the tried to see if I could call the method resetPosition in the touchesBegan method, and it works. Object1's position is actually reset. Are there limitations to what you can do in the didBeginContact method? And if so, what would be the best way of trying to achieve my intended goal.

Here is a sample of my didBeginContact Method:

-(void)didBeginContact: (SKPhyicsContacts *)contact { 
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 == Category1 && secondBody.categoryBitMask == Category2){
    NSLog(@"Collision Detected!");
    [Object1 resetPosition];
}

To clarify any confusion, Category1 corresponds to Object1, Category2 corresponds to Object2.

Here is a sample of my resetPosition method inside of my Object1 class:

-(void)resetPosition{
    self.position = CGPointMake(0, 200);
    NSLog(@"Reached Method!");
}

Solution

  • You should take a look at run loop. Nothing is drawn before physics simulation is done. So I guess that changing position like you are doing is overwritten by physics simulation.

    This is the order of run loop:

    • update is called
    • scene evaluate actions
    • didEvaluateActions is called
    • physics simulation goes here
    • didSimulatePhysics is called
    • scene applies constraints
    • didApplyConstraints is called
    • didFinishUpdate is called
    • rendering the frame

    I could bet if you move repositioning of node in didSimulatePhysics instead of doing it in didBeginContact that everything will work. I guess this is happening because you are trying to change node's position before simulation is done. You can try this code (look at didSimulatePhysics part):

    #import "GameScene.h"
    
    typedef enum uint_8{
        ColliderWall = 1,
        ColliderPlayer = 2,
        ColliderBrick =  4
    }CollisionCategory;
    
    
    @interface Object1 : SKSpriteNode
    
    @end
    
    @implementation Object1
    
    -(instancetype)initWithColor:(UIColor *)color size:(CGSize)size{
    
        if(self = [super initWithColor:color size:size]){
    
    
            self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:size];
    
            self.physicsBody.categoryBitMask = ColliderPlayer;
            self.physicsBody.contactTestBitMask = ColliderBrick;
            self.physicsBody.collisionBitMask = ColliderBrick | ColliderWall;
            self.physicsBody.dynamic = YES;
            self.physicsBody.affectedByGravity = YES;
            self.physicsBody.allowsRotation = NO;
    
        }
        return self;
    }
    
    
    
    @end
    
    @interface GameScene  ()<SKPhysicsContactDelegate>
    
    @property (nonatomic, strong) Object1 *player;
    @property (nonatomic, strong) SKSpriteNode *brick;
    
    @property (nonatomic, assign) BOOL shouldMove;
    
    @end
    
    @implementation GameScene
    
    -(void)didMoveToView:(SKView *)view {
        /* Setup your scene here */
    
        _shouldMove = NO;
    
        self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
        self.physicsBody.contactTestBitMask = 0;
        self.physicsBody.collisionBitMask =  ColliderPlayer;
        self.physicsBody.categoryBitMask = ColliderWall;
    
        self.physicsWorld.contactDelegate = self;
    
        _player = [[Object1 alloc] initWithColor:[SKColor greenColor] size:CGSizeMake(50,30)];
        _player.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMaxY(self.frame)- _player.size.height-30);
        [self addChild:_player];
    
        _brick = [SKSpriteNode spriteNodeWithColor:[SKColor greenColor] size:CGSizeMake(330,80)];
        _brick.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_brick.size];
        _brick.position = CGPointMake(CGRectGetMidX(self.frame),100);
        _brick.physicsBody.contactTestBitMask = ColliderPlayer;
        _brick.physicsBody.collisionBitMask = ColliderPlayer;
        _brick.physicsBody.categoryBitMask = ColliderBrick;
        _brick.physicsBody.affectedByGravity = NO;
        _brick.physicsBody.dynamic = NO;
    
        [self addChild:_brick];
    
    
    }
    
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
        /* Called when a touch begins */
    
        for (UITouch *touch in touches) {
    
            [self.player.physicsBody applyImpulse:CGVectorMake(0,55)];
    
        }
    }
    
    -(void)didBeginContact:(SKPhysicsContact *)contact{
    
        NSLog(@"Contact");
    
        self.shouldMove = YES;
    
    }
    
    -(void)didSimulatePhysics{
    
        if(self.shouldMove){
    
    
            self.player.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMaxY(self.frame)- _player.size.height-30);
    
    
            self.shouldMove = NO;
        }
    }
    
    
    @end