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

jesse050717
Title: Web Development with Clojure, Third Edition, pg 116 Hi - I just started chapter 5 and I am stuck on page 116 while trying to star...
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
oaklandgit
Hi, I completed chapter 6 but am getting the following error when running: thread 'main' panicked at 'Failed to load texture: IoError(O...
New
taguniversalmachine
Hi, I am getting an error I cannot figure out on my test. I have what I think is the exact code from the book, other than I changed “us...
New
rainforest
Hi, I’ve got a question about the implementation of PubSub when using a Phoenix.Socket.Transport behaviour rather than channels. Before ...
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
New
SlowburnAZ
Getting an error when installing the dependencies at the start of this chapter: could not compile dependency :exla, "mix compile" failed...
New
roadbike
From page 13: On Python 3.7, you can install the libraries with pip by running these commands inside a Python venv using Visual Studio ...
New
dachristenson
I’ve got to the end of Ch. 11, and the app runs, with all tabs displaying what they should – at first. After switching around between St...
New

Other popular topics Top

PragmaticBookshelf
Stop developing web apps with yesterday’s tools. Today, developers are increasingly adopting Clojure as a web-development platform. See f...
New
dasdom
No chair. I have a standing desk. This post was split into a dedicated thread from our thread about chairs :slight_smile:
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
PragmaticBookshelf
Rust is an exciting new programming language combining the power of C with memory safety, fearless concurrency, and productivity boosters...
New
Exadra37
I am asking for any distro that only has the bare-bones to be able to get a shell in the server and then just install the packages as we ...
New
AstonJ
We’ve talked about his book briefly here but it is quickly becoming obsolete - so he’s decided to create a series of 7 podcasts, the firs...
New
PragmaticBookshelf
Rails 7 completely redefines what it means to produce fantastic user experiences and provides a way to achieve all the benefits of single...
New
PragmaticBookshelf
Build efficient applications that exploit the unique benefits of a pure functional language, learning from an engineer who uses Haskell t...
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
AstonJ
Curious what kind of results others are getting, I think actually prefer the 7B model to the 32B model, not only is it faster but the qua...
New

Sub Categories: