Configure Physics Categories

Before you can set up physics categories, you first need to understand what SpriteKit does when two physics bodies attempt to occupy the same space, and what types of interactions are possible. With SpriteKit, there are two types of interactions: a contact and a collision. A contact occurs the moment body A touches body B. A collision occurs when two bodies collide. The difference between these two interactions is subtle but important. The first type of interaction is generally used to handle some type of game logic. For example, knowing when the player collects an item. The second type of interaction is used more for simulating real-world physics—for example, a ball bouncing off the side of a wall.

SpriteKit determines how to handle contacts and collisions between bodies through the use of physics categories. Physics categories are set up by you, the developer; you can have up to 32 different categories per scene. With physics categories, you’re able to set up game logic based on these interactions. Furthermore, by precisely restricting the interaction between certain bodies, you limit the number of interactions in each frame, which increases your game’s overall performance.

For Gloop Drop, you’ll use four physics categories: none, player, collectible, and foreground.

Open the SpriteKitHelper.swift file and add the following code:

 // SpriteKit Physics Categories
 enum​ ​PhysicsCategory​ {
 static​ ​let​ none: ​UInt32​ = 0
 static​ ​let​ player: ​UInt32​ = 0b1 ​// 1
 static​ ​let​ collectible: ​UInt32​ = 0b10 ​// 2
 static​ ​let​ foreground: ​UInt32​ = 0b100 ​// 4
 }

Here, you’re using an enum to set up the different physics categories to keep track of what nodes belong to what category. You could also use a struct if you prefer, but it’s unnecessary because you’re not going to add any members or methods. Also, note that the names you use here do not need to match the node names.

Numbers and Physics Categories

images/aside-icons/info.png

By default, integer literals are expressed in decimals using the numbers 0 through 9. However, you can modify this default behavior and use binary literals instead, which might make it easier. With binary literals, you use the prefix 0b. Unlike integer literals, binary literals use only 0 and 1. So, in this example, 0b100 equals the number 4. Essentially, this is shorthand for 000000100. When setting your categories, it’s best to do so using powers of two: 1, 2, 4, 8, 16, and so on.

You can also use the OptionSet protocol[28] inside a custom struct to represent the different categories. Sometimes, it’s just a matter of preference.

Your next step is to apply these categories to the different physics bodies. Open the GameScene.swift file, and in the didMove(to:) method, after the code that sets up the foreground.physicsBody, add this block of code:

 // Set up physics categories for contacts
 foreground.physicsBody?.categoryBitMask = ​PhysicsCategory​.foreground
 foreground.physicsBody?.contactTestBitMask = ​PhysicsCategory​.collectible
 foreground.physicsBody?.collisionBitMask = ​PhysicsCategory​.​none

This code sets the category, contact, and collision bit masks for the foreground node’s physics body. Effectively, this code tells SpriteKit that the foreground node’s physics body belongs to the PhysicsCategory.foreground. It also tells SpriteKit that it should be concerned only about contacts from the PhysicsCategory.collectible category and to ignore collisions altogether. For an in-depth look at bit masks and bitwise operators, read the Advanced Operators section in The Swift Programming Language guide.[29]

Now it’s time to set up the player node. Open the Player.swift file, and at the end of the init() method, add this code:

 // Set up physics categories for contacts
 self​.physicsBody?.categoryBitMask = ​PhysicsCategory​.player
 self​.physicsBody?.contactTestBitMask = ​PhysicsCategory​.collectible
 self​.physicsBody?.collisionBitMask = ​PhysicsCategory​.​none

Like the foreground node, you’re setting up the player node’s physics body category and its contact and collision bit masks. In this case, SpriteKit will react only to a player/collectible contact event.

Finally, open the Collectible.swift file, and at the end of the init() method, add this code:

 // Set up physics categories for contacts
 self​.physicsBody?.categoryBitMask = ​PhysicsCategory​.collectible
 self​.physicsBody?.contactTestBitMask = ​PhysicsCategory​.player
  | ​PhysicsCategory​.foreground
 self​.physicsBody?.collisionBitMask = ​PhysicsCategory​.​none

Notice here that the contactTestBitMask is set to both the PhysicsCategory.player | PhysicsCategory.foreground categories, which means SpriteKit will keep tabs when a collectible makes contact with either the player or the foreground.

Now that your categories and physics bodies are set up, you’re ready to handle the interactions.

Configure the Physics Contact Delegate

Before you can take action on these physics body interactions, the GameScene class needs to declare its intention to implement the SKPhysicsContactDelegate protocols—in other words, it needs to conform to that protocol.[30] Like other protocols in Swift, your class, structure, or enumeration needs to implement the protocol’s requirements. In this case, the SKPhysicsContactDelegate protocols, didBegin(_:) and didEnd(_:), are what makes it possible for your scenes to respond to contact events.

While you could update the GameScene class definition to implement the SKPhysicsContactDelegate directly, it’s better to use an extension to keep your code better organized. Open the GameScene.swift, and at the bottom of the file, add a new extension to handle the collision detection:

 // MARK: - COLLISION DETECTION
 
 /* ####################################################################### */
 /* COLLISION DETECTION METHODS START HERE */
 /* ####################################################################### */
 
 extension​ ​GameScene​: ​SKPhysicsContactDelegate​ {
 
 }

This extension declares that the GameScene class can act as a delegate for SKPhysicsContactDelegate. Your next step is to make it official by setting the delegate in the game scene. In the didMove(to:) method, add the following code at the top:

 // Set up the physics world contact delegate
 physicsWorld.contactDelegate = ​self

This code sets the physicsWorld.contactDelegate property to self, making the scene the contact delegate. The next step is to implement the protocol responsible for handling contacts.

Detect Contact Between Physics Bodies

Inside the SKPhysicsContactDelegate extension, add the following method:

 func​ ​didBegin​(_ contact: ​SKPhysicsContact​) {
 // Check collision bodies
 let​ collision = contact.bodyA.categoryBitMask |
  contact.bodyB.categoryBitMask
 // Did the [PLAYER] collide with the [COLLECTIBLE]?
 if​ collision == ​PhysicsCategory​.player | ​PhysicsCategory​.collectible {
 print​(​"player hit collectible"​)
  }
 
 // Or did the [COLLECTIBLE] collide with the [FOREGROUND]?
 if​ collision == ​PhysicsCategory​.foreground | ​PhysicsCategory​.collectible {
 print​(​"collectible hit foreground"​)
  }
 }

With this protocol method, you are grabbing the value stored in the categoryBitMask of each body involved, and you are printing a statement to the console depending on those results.

Build and run the project to see the collision detection in action.

images/WorkingWithPhysicsAndCollisionDetection/spritekit-gloop-build-01.png

Each time the player hits a collectible or a collectible hits the foreground, you’ll see the corresponding statement print to the console. This is the beginning of adding your game logic based on those contacts.

Now that you know when and what type of contact was made between physics bodies, you’re ready to handle those interactions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset