Make App Pie

Training for Developers and Artists

The SlippyFlippy Challenge: Working With Sprite Kit Collision Detection

icon2_120 In our last installment, we ran into a problem trying to get collisions to work correctly with the obstacle. We want it to interact with the penguin, but not the game world. If we  turn on physics, either the edgeLoop on the scene  blocks the obstacle from entering the scene by , or causes very jittery obstacle. A more selective collision system will remedy this.  In Sprite Kit, there are two kinds of collisions

  • Collisions where the physics engine bounces the objects apart. We have that now as the penguin bounces off the walls.
  • Contact where some other action is necessary. In SlippyFlippy, the game should end when the penguin hits the top and bottom boundaries. In a role-playing game, a character coming into contact with a door should move the game to another room.

There are several steps to setting up collisions

  1. Create categories of collisions
  2. Tell the sprites what they collide with and what they don’t
  3. Create the delegate methods to handle when contact begins and ends.

Add the Physics Contact Delegate

Collision detection uses the delegate SKPhysicsContactDelegate. Add the collision delegate in the header (.h) file:

@interface MFSMyScene : SKScene

Next, tell the physicsWorld there is a delegate. Add this in initWithSize: under the other physicsWorld assignment.

self.physicsWorld.contactDelegate = self;

Make the Bit Mask Categories

The Physics engine uses a 32 bit integer to interpret collisions. Each bit is a flag for a category of sprite or other SKNode. Create a list of categories and code the bitmap with constants. Group nodes into categories with similar behaviors for contact and collision. For SlippyFlippyPenguin, we have the following categories:

  • Penguin
  • World
  • Obstacle
  • Boundary

Create constants for these categories as bit masks.

//constants for the collision bitmap
static const uint32_t penguinCategory = 1 << 0;
static const uint32_t worldCategory = 1 << 1;
static const uint32_t obstacleCategory = 1 << 2;

Set Sprites to Collision Categories

Using these constants, set the categories for the sprites. We assign to the categoryBitMask property of the physicsBody the category. In our code for defining a penguin, add this after we set up physics:

//collision  and contact detection
    penguin.physicsBody.categoryBitMask = penguinCategory; //is a penguin

In our code for defining the world, add:

//collision and contact detection
        self.physicsBody.categoryBitMask = worldCategory;
	 self.name=@“world”

We added the name world to the parent sprite self. For identification in the delegate methods, the name is a good identifier.

For the obstacles, we didn’t set up physics last time. We need to set up physics on the lower and upper obstacle, then assign the bit mask. For obstacle1, add:

//physics and collision detection
    obstacle1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:obstacle1.size];
    obstacle1.physicsBody.dynamic = NO;  //no gravity
    obstacle1.physicsBody.categoryBitMask = obstacleCategory;

For obstacle2, add

//physics and collision detection
obstacle2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:obstacle2.size];
    obstacle2.physicsBody.dynamic = NO;  //no gravity
    obstacle2.physicsBody.categoryBitMask = obstacleCategory;

For each, we made a solid physics body, and turned off gravity, then assigned the obstacle category to the lower and upper obstacle, not the parent obstacle. This way, the penguin can get through the gap, but will collide with the obstacle.

Set Collisions and Contact

After defining all nodes in categories, decide how the nodes will react with each other. In this case, the only active sprite is the penguin, which will crash into the world walls and the obstacle. The other nodes doesn’t need any code. Change the collision code in initWithSize: for the penguin to:

//collision detection
    penguin.physicsBody.categoryBitMask = penguinCategory;
    penguin.physicsBody.collisionBitMask = worldCategory | obstacleCategory;
    penguin.physicsBody.contactTestBitMask = worldCategory | obstacleCategory;

Code didBeginContact: for the Delegate

Set up the delegate method when a contact occurs. The delegate method is optional. We don’t need it yet, but will be informative on how collisions happen.

- (void)didBeginContact:(SKPhysicsContact *)contact {
    NSLog(@"Body A: %@ contacting Body B: %@",contact.bodyA.node.name, contact.bodyB.node.name);
}

The delegate gets the names of our nodes and prints them out. The SKPhysicsContact object has properties listing the two contacting bodies. We can then use logic on those two bodies to change what we do. In the original Flappy Bird, any contact would end the game. SlippyFlippyPenguin will work differently. Build and run the app.

collision
SlippyFlippy colliding with an obstacle

This is starting to look like a game. SlippyFlippy crashes into the obstacles, and we can avoid them by tapping on the screen enough for him to pass through the gap. If he hits an obstacle, the physics engine starts spinning and bouncing him out of control. The obstacles try to push SlippyFlippy around and can push him off the frame completely. Set up some simple logic for the two contact events. Change the delegate to the following:

- (void)didBeginContact:(SKPhysicsContact *)contact {
    NSString *bodyA = contact.bodyA.node.name;
    NSString *bodyB = contact.bodyB.node.name;
    NSLog(@"Body A: %@  Body B: %@",bodyA, bodyB);

    if ([bodyA isEqualToString:@“penguin"]){ //collision into world
        NSLog(@"THUD!!\n\n");
    }
    if ([bodyB isEqualToString:@"penguin"]) { //collision into obstacle
        NSLog(@"BOING!!!\n\n");
    }
}

The penguin when it hits a wall will now print BOING!! or THUD!!! when it hits an an obstacle. That is the beginning of collision detection. There is a lot more that we can do with this, which I’ll explore in later lessons. For SlippyFlippy, the four boundaries of the world do not make for good game play. We want three sides for collision and the left side open. That we will address next time.

One response to “The SlippyFlippy Challenge: Working With Sprite Kit Collision Detection”

  1. […] This code should be familiar by now. It has been covered in other posts. We  make the boundary  size proportional to self.frame. With two boundaries, take a fifth of the frame height for the boundary, and the width of the screen. Make the boundary a solid blue, and set up physics. Like the obstacles, this is a non dynamic physics body. Set the collision bit masks to find this as a boundary. If there is a penguin collision, the physics engine executes didBeginContact: as we discussed last time. […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: