Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SpriteKit - Determine Side A Square Collided With

So for the App I'm working on, I have two cubes colliding. I check for this in the standard way. The app tells me when they collide in my "didBeginContact" method.

-(void)didBeginContact:(SKPhysicsContact *)contact {
    if (contact.bodyA.categoryBitMask == WALL_CATEGORY && contact.bodyB.categoryBitMask == CHARACTER_CATEGORY) {
        CGPoint point = contact.contactPoint;
    }
}

So i know where the collision takes place, but because it is two squares it can be at any point along the side including the corners. So how would I check if the collision on the left/right/top/bottom exclusively?

Edit: Correct Answer: Might not be the cleanest way to do it but it works. Hopefully it'll help someone in the future.

m_lNode = [SKNode node];
m_lNode.position = CGPointMake(-(CHARACTER_SIZE / 2), 0);
m_lNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(1, m_character.size.height)];
m_lNode.physicsBody.allowsRotation = NO;
m_lNode.physicsBody.usesPreciseCollisionDetection = YES;
m_lNode.physicsBody.categoryBitMask = CHARACTER_L_CATEGORY;

m_rNode = [SKNode node];
m_rNode.position = CGPointMake((CHARACTER_SIZE / 2), 0);
m_rNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(1, m_character.size.height)];
m_rNode.physicsBody.allowsRotation = NO;
m_rNode.physicsBody.usesPreciseCollisionDetection = YES;
m_rNode.physicsBody.categoryBitMask = CHARACTER_R_CATEGORY;

m_tNode = [SKNode node];
m_tNode.position = CGPointMake(0, (CHARACTER_SIZE / 2));
m_tNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(m_character.size.width , 1)];
m_tNode.physicsBody.allowsRotation = NO;
m_tNode.physicsBody.usesPreciseCollisionDetection = YES;
m_tNode.physicsBody.categoryBitMask = CHARACTER_T_CATEGORY;

m_bNode = [SKNode node];
m_bNode.position = CGPointMake(0, -(CHARACTER_SIZE / 2));
m_bNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(m_character.size.width, 1)];
m_bNode.physicsBody.allowsRotation = NO;
m_bNode.physicsBody.usesPreciseCollisionDetection = YES;
m_bNode.physicsBody.categoryBitMask = CHARACTER_B_CATEGORY;

[m_character addChild:m_tNode];
[m_character addChild:m_bNode];
[m_character addChild:m_lNode];
[m_character addChild:m_rNode];

-(void)didBeginContact:(SKPhysicsContact *)contact {
    if (contact.bodyA.categoryBitMask == WALL_CATEGORY) {
        switch (contact.bodyB.categoryBitMask) {
            case CHARACTER_T_CATEGORY:
                NSLog(@"Top");
                m_isHitTop = true;
                break;
            case CHARACTER_B_CATEGORY:
                NSLog(@"Bottom");
                m_isHitBottom = true;
                break;
            case CHARACTER_L_CATEGORY:
                NSLog(@"Left");
                m_isHitLeft = true;
                break;
            case CHARACTER_R_CATEGORY:
                NSLog(@"Right");
                m_isHitRight = true;
                break;
        }
    }
}

Added some relevant code. It's my code so there is variables amongst other things but you should be able to figure it out.

like image 816
CMilby Avatar asked Jan 07 '15 22:01

CMilby


2 Answers

I think the best way to determine what side is involved in your contact (top,left,bottom,right side) is to:

  • calculate the points leaved by a centering cross for up, down, left and right sides (for example if you have a squared sprite..)

enter image description here

let halfWidth = self.frame.width/2
let halfHeight = self.frame.height/2
let down = CGPointMake(self.frame.origin.x+halfWidth,self.frame.origin.y)
let up = CGPointMake(self.frame.origin.x+halfWidth,self.frame.origin.y+self.frame.size.height)
let left = CGPointMake(self.frame.origin.x,self.frame.origin.y+halfHeight)
let right = CGPointMake(self.frame.origin.x+self.frame.size.width,self.frame.origin.y+halfHeight)
  • Calculate the distance between the contactPoint and each point sides (up, left , right and down)

This step can be possible with this little function:

func getDistance(p1:CGPoint,p2:CGPoint)->CGFloat {
    let xDist = (p2.x - p1.x)
    let yDist = (p2.y - p1.y)
    return CGFloat(sqrt((xDist * xDist) + (yDist * yDist)))
}

After, the point involved with the lowest distance calculated is the side point nearest the contactPoint.

What are the advantages of this method:

  1. You don't paint zombies or ghost nodes and relative physic bodies
  2. This method work also inside a dynamic mutable CGPath, not only with a knowed rectangular boundaries
  3. Is fast, few lines of code and if you add other few lines you can be able to determine also diagonals and make more precise your algorithm.
like image 91
Alessandro Ornano Avatar answered Oct 20 '22 00:10

Alessandro Ornano


the easiest way is to add child sprites the top, left, right, bottom of your squares. Add physics bodies to those, and then you can tell where things are colliding. I'd recommend trying this method first unless you have many many squares on the screen.

If you have a ton of squares on the screen and youre worried about performance. then maybe use contact.contactPoint and convert that point to square coordinates. given the center point of the square, the angle of the squares rotation and that point, you should be able to tell where the square collided. That would require some math.. and I was afraid writing up that kind of solution when the first one may be all that you really need.

like image 42
hamobi Avatar answered Oct 20 '22 01:10

hamobi