mikecargal

mikecargal

Apple Game Frameworks and Technologies: Changes to GameScene+PhysicsContact

Title: Changes to GameScene+PhysicsContact

(I understand, it may be to late in your process to make such a change, but I’ll share here anyway, and would welcome feedback)

The GameScene+PhysicsContact extension was the source of one of my typos, and I also found the code to be quite “dense” to read.

I’m taking a bit of time at the end of Val’s Revenge to go in and make changes that seem good, but mainly to test that I’m getting the hang of Swift.

I added a couple of functions to factor out a lot of the common code and reduce the noise of reading the cases and then matching up the nodes. (On the plus side, my typo was accidentally having a collisionBitMask one place rather than a categoryBitMask (probably just a cursor key error with code completion, but I really don’t want to admit how long it took me to track that down). This code prevents that sort of error by placing it into the function where it’s done once, and will either be always right, or always wrong)

Anyway… as the tag says… Just a suggestion

This is my revised version of GameScene+PhysicsContact.swift:

import SpriteKit

extension GameScene: SKPhysicsContactDelegate {
    func match(_ contact: SKPhysicsContact, body: PhysicsBody) -> (match:SKNode, other:SKNode) {
        return contact.bodyA.categoryBitMask == body.categoryBitMask ?
            (contact.bodyA.node!, contact.bodyB.node!) :
            (contact.bodyB.node!, contact.bodyA.node!)
    }
    
    func collisionOf(_ bodyA :PhysicsBody, _ bodyB: PhysicsBody) -> UInt32 {
        bodyA.categoryBitMask | bodyB.categoryBitMask
    }

    func didBegin(_ contact: SKPhysicsContact) {
        let collision = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
        switch collision {
        // MARK: - Player | Collectible

        case collisionOf(.player,.collectible):
            let (playerNode, collectible) = match(contact, body: .player)

            if let player = playerNode as? Player {
                player.collectItem(collectible)
            }

        // MARK: - Player | Door

        case collisionOf(.player,.door):
            let (playerNode, door) = match(contact, body: .player)

            if let player = playerNode as? Player {
                player.useKeyToOpenDoor(door)
            }

        // MARK: - Projectile | Collectible

        case collisionOf(.projectile,.collectible):
            let (projectileNode, collectibleNode) = match(contact, body: .projectile)
            if let collectibleComponent = collectibleNode.entity?.component(ofType: CollectibleComponent.self) {
                collectibleComponent.destroyedItem()
            }
            projectileNode.removeFromParent()

        // MARK: - Player | Monster

        case collisionOf(.player,.monster):
            let (playerNode, _) = match(contact, body: .player)
            if let healthComponent = playerNode.entity?.component(ofType: HealthComponent.self) {
                healthComponent.updateHealth(-1, forNode: playerNode)
            }

        // MARK: - Projectile | Monster

        case collisionOf(.projectile,.monster):
            let (monsterNode, _) = match(contact, body: .monster)

            if let healthComponent = monsterNode.entity?.component(ofType: HealthComponent.self) {
                healthComponent.updateHealth(-1, forNode: monsterNode)
            }

            // MARK: - Player | Platform

        case collisionOf(.player,.exit):
            let (playerNode, _) = match(contact, body: .player)
            // update the saved stats
            if let player = playerNode as? Player {
                GameData.shared.keys = player.getStats().keys
                GameData.shared.treasure = player.getStats().treasure
            }

            GameData.shared.level += 1
            loadSceneForLevel(GameData.shared.level)
        default:
            break
        }
    }
}

First Post!

mikecargal

mikecargal

slight improvement to avoid “evil” ! unwraps.

import SpriteKit

extension GameScene: SKPhysicsContactDelegate {
    func match(_ contact: SKPhysicsContact, body: PhysicsBody) -> (match: SKNode, other: SKNode) {
        guard let nodeA = contact.bodyA.node,
              let nodeB = contact.bodyB.node
        else {
            fatalError("Expected contact bodies to both have attached nodes")
        }
        return contact.bodyA.categoryBitMask == body.categoryBitMask ?
            (nodeA, nodeB) :
            (nodeB, nodeA)
    }

    func collisionOf(_ bodyA: PhysicsBody, _ bodyB: PhysicsBody) -> UInt32 {
        bodyA.categoryBitMask | bodyB.categoryBitMask
    }

    func didBegin(_ contact: SKPhysicsContact) {
        let collision = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
        switch collision {
        // MARK: - Player | Collectible

        case collisionOf(.player, .collectible):
            let (playerNode, collectible) = match(contact, body: .player)

            if let player = playerNode as? Player {
                player.collectItem(collectible)
            }

        // MARK: - Player | Door

        case collisionOf(.player, .door):
            let (playerNode, door) = match(contact, body: .player)

            if let player = playerNode as? Player {
                player.useKeyToOpenDoor(door)
            }

        // MARK: - Projectile | Collectible

        case collisionOf(.projectile, .collectible):
            let (projectileNode, collectibleNode) = match(contact, body: .projectile)
            if let collectibleComponent = collectibleNode.entity?.component(ofType: CollectibleComponent.self) {
                collectibleComponent.destroyedItem()
            }
            projectileNode.removeFromParent()

        // MARK: - Player | Monster

        case collisionOf(.player, .monster):
            let (playerNode, _) = match(contact, body: .player)
            if let healthComponent = playerNode.entity?.component(ofType: HealthComponent.self) {
                healthComponent.updateHealth(-1, forNode: playerNode)
            }

        // MARK: - Projectile | Monster

        case collisionOf(.projectile, .monster):
            let (monsterNode, _) = match(contact, body: .monster)

            if let healthComponent = monsterNode.entity?.component(ofType: HealthComponent.self) {
                healthComponent.updateHealth(-1, forNode: monsterNode)
            }

            // MARK: - Player | Platform

        case collisionOf(.player, .exit):
            let (playerNode, _) = match(contact, body: .player)
            // update the saved stats
            if let player = playerNode as? Player {
                GameData.shared.keys = player.getStats().keys
                GameData.shared.treasure = player.getStats().treasure
            }

            GameData.shared.level += 1
            loadSceneForLevel(GameData.shared.level)
        default:
            break
        }
    }
}

Where Next?

Popular Pragmatic Bookshelf topics Top

New
brian-m-ops
#book-python-testing-with-pytest-second-edition Hi. Thanks for writing the book. I am just learning so this might just of been an issue ...
New
AndyDavis3416
@noelrappin Running the webpack dev server, I receive the following warning: ERROR in tsconfig.json TS18003: No inputs were found in c...
New
nicoatridge
Hi, I have just acquired Michael Fazio’s “Kotlin and Android Development” to learn about game programming for Android. I have a game in p...
New
brunogirin
When installing Cards as an editable package, I get the following error: ERROR: File “setup.py” not found. Directory cannot be installe...
New
AufHe
I’m a newbie to Rails 7 and have hit an issue with the bin/Dev script mentioned on pages 112-113. Iteration A1 - Seeing the list of prod...
New
jonmac
The allprojects block listed on page 245 produces the following error when syncing gradle: “org.gradle.api.GradleScriptException: A prob...
New
creminology
Skimming ahead, much of the following is explained in Chapter 3, but new readers (like me!) will hit a roadblock in Chapter 2 with their ...
New
dtonhofer
@parrt In the context of Chapter 4.3, the grammar Java.g4, meant to parse Java 6 compilation units, no longer passes ANTLR (currently 4....
New
ggerico
I got this error when executing the plot files on macOS Ventura 13.0.1 with Python 3.10.8 and matplotlib 3.6.1: programming_ML/code/03_...
New

Other popular topics Top

wolf4earth
@AstonJ prompted me to open this topic after I mentioned in the lockdown thread how I started to do a lot more for my fitness. https://f...
New
brentjanderson
Bought the Moonlander mechanical keyboard. Cherry Brown MX switches. Arms and wrists have been hurting enough that it’s time I did someth...
New
AstonJ
I have seen the keycaps I want - they are due for a group-buy this week but won’t be delivered until October next year!!! :rofl: The Ser...
New
AstonJ
In case anyone else is wondering why Ruby 3 doesn’t show when you do asdf list-all ruby :man_facepalming: do this first: asdf plugin-upd...
New
New
Help
I am trying to crate a game for the Nintendo switch, I wanted to use Java as I am comfortable with that programming language. Can you use...
New
New
PragmaticBookshelf
Author Spotlight: Peter Ullrich @PJUllrich Data is at the core of every business, but it is useless if nobody can access and analyze ...
New
First poster: bot
zig/http.zig at 7cf2cbb33ef34c1d211135f56d30fe23b6cacd42 · ziglang/zig. General-purpose programming language and toolchain for maintaini...
New
New

Sub Categories: