Home > OS >  Swift // Sprite Kit: Category Bit Mask Limitations
Swift // Sprite Kit: Category Bit Mask Limitations

Time:01-28

There's something that's always puzzled me about category bit masks, and I'm reaching a point where I'm going to need a greater understanding of them. I understand how they work on a fundamental level. Say I was making a dungeon crawler basic hack and slash capabilities. I might use a collection of categories like these:

enum PhysicsCategory{
    static let none: UInt32 = 0
    static let playerCategory: UInt32 = 0b1
    static let enemyCategory: UInt32 = 0b10
    static let weaponCategory: UInt32 = 0b100
    static let collectibleCategory: UInt32 = 0b1000
    static let enemyProjectileCategory: UInt32 = 0b10000
}

This would probably suffice, I could test if I'm attacking the enemy, they're attacking me, etc. That said, if I wanted to make a dungeon crawler with different enemy classes, different weapon types, and different enemy weaknesses and strengths, I feel like I'd run out of categories really fast:

enum PhysicsCategory{
    static let none: UInt32 = 0
    static let playerCategory: UInt32 = 0b1

    static let toxicWeaponCategory: UInt32 = 0b10
    static let iceWeaponCategory: UInt32 = 0b100
    static let explosiveWeaponCategory: UInt32 = 0b1000
    static let bluntWeaponCategory: UInt32 = 0b10000

    static let toxicEnemyCategory: UInt32 = 0b100000
    static let iceEnemyCategory: UInt32 = 0b1000000
    static let explosiveEnemyCategory: UInt32 = 0b10000000
}

I run out of options for enemies and haven't even gotten to things like collectibles, environmental objects, or bosses whose weaknesses and/or strengths make entirely new combinations. How are these things typically accounted for? What I'm trying to make demands more than what you see above and the books / guides I've read only explain this on a very basic level.

CodePudding user response:

categoryBitmasks are used mostly for contact and collision detection and to establish what has made contact with what at a high level i.e. player with collectible, enemy with weapon etc. I'd be tempted to subclass these nodes (enemies, collectibles, bosses) and make the 'effect' (toxic, ice, explosive) a property within the node so that after making contact with something, you can call the appropriate routine from didBegin() based upon the enemy 'effectType'.

All contacts go through didBegin() andway, and you're going to have to test the toxic/ice/explosive category anyway to work out how to handle the contact, so changing this logic to test a property within the node contacted won't actually add much (if any) code to your program.

Edit: You could code your didBegin() like this:

      func didBegin(_ contact: SKPhysicsContact) {
         print("didBeginContact entered for \(String(describing: contact.bodyA.node!.name)) and \(String(describing: contact.bodyB.node!.name))")
    
         let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
    
         switch contactMask {
         case  playerCategory | enemyCategory:
            print("Playerand enemy have contacted.")
            let playerNode = contact.bodyA.categoryBitMask == playerCategory ? contact.bodyA.node : contact.bodyB.node
            let enemyNode = contact.bodyA.categoryBitMask == enemyCategory ? contact.bodyA.node : contact.bodyB.node
            let enemyType = enemyNode.contactType // toxic, ice, explosive
            processCollision(between: playerNode and: enemyNode ofType: enemyType)
    
         default:
            print("Some other contact occurred")
         }

Although you'd be more likely to extract the enemey's type in processCollision()

If you're not familiar with Swift's argument labels for parameters, the definition of processCollision could be:

func: processCollision(between node: SKSpriteNode, and node2: SkSpriteNode ofType type: string)

which allows you to refer to node1 and node2 in the function, but the calling syntax is more readable. Also type would probably be a enum of the available enemy types.

  • Related