【问题标题】:Can't understand how collision bit mask works无法理解碰撞位掩码的工作原理
【发布时间】:2026-02-14 20:10:01
【问题描述】:

我正在尝试在 Swift 中使用碰撞位掩码和接触测试位掩码,我希望两个对象不要碰撞在一起,所以我这样做了:

firstNode.physicsBody?.collisionBitMask = 0b01
secondNode?.collisionBitMask = 0b10

既然 SpriteKit 对这两个数字进行了 AND 操作,那么结果不应该是 00,因为 10 & 01 = 00 吗?

那么为什么会发生碰撞呢?

谢谢。

【问题讨论】:

    标签: swift sprite-kit skphysicsbody


    【解决方案1】:

    这不是碰撞处理的工作原理。当两个物体相交时,物理引擎在当前物体collisionBitMask和其他物体categoryBitMask之间执行逻辑AND运算符:

    当两个物理体相互接触时,可能会发生碰撞。 将此主体的碰撞掩码与其他主体的类别进行比较 通过执行逻辑与运算来屏蔽。如果结果是非零 值,这个物体会受到碰撞的影响。每个身体独立 选择是否要被对方影响。为了 例如,您可以使用它来避免碰撞计算 对身体的速度进行微不足道的变化。

    source

    所以结果取决于你如何在这两个物体上设置categoryBitMaskcategoryBitMask 的默认值是 0xFFFFFFFF,表示所有位都已设置。因此,当您在0xFFFFFFFF0b100b01 之间执行& 时,结果将是非零值,因此会发生冲突。

    例如,像这样设置你的身体:

    spriteA.physicsBody?.categoryBitMask = 0b01
    spriteA.physicsBody?.collisionBitMask = 0b01
    

    spriteB.physicsBody?.categoryBitMask = 0b10
    spriteB.physicsBody?.collisionBitMask = 0b10
    

    会给你想要的结果。此外,这可能不是您需要的确切设置,它只是一个简单的示例,您必须根据需要更改值。在这种情况下,spriteA 只会与 categoryBitMask 设置为 0b01 的物体发生碰撞。 spriteB 也是如此,它会与 categoryBitMask 设置为 0b10 的物体发生碰撞。

    另外,如果您不希望这些精灵与任何东西发生碰撞,只需将它们的 collisionBitMask 属性设置为 0。

    【讨论】:

    • 哇,谢谢您的详细回答!解决了我的问题! ?
    【解决方案2】:

    这不是您使用碰撞位测试节点之间交互的方式。

    • categoryBitMask 是对象的类别。
    • collisionBitMask 是对象在碰撞时的响应。
    • contactTestBitMask 用于在指定位掩码发生交叉时发出通知。

    假设我有以下内容:

    struct PC {
        static var player: UInt32 = 0b10 //2
        static var enemy: UInt32 = 0b100 //4
        static var rock: UInt32 = 0b1000 //8
    }
    
    player.physicsBody!.categoryBitMask = PC.player
    player.physicsBody!.collisionBitMask = PC.enemy | PC.rock
    enemy.physicsBody!.categoryBitMask = PC.enemy
    enemy.physicsBody!.collisionBitMask = PC.player
    

    因此,当您在 didBeginContact 函数中检查何时发生交互时,您可以使用位逻辑检查它们是否发生了交互。

    func didBeginContact(contact: SKPhysicsCountact) {
        //1
        let collision: UInt32 = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
    
        //2
        if collision == PC.player | PC.enemy {
            //An interaction occured between the player and enemy.
        }
    
    1. 变量冲突使用按位或,即|。在这种情况下(如果玩家碰到一个敌人),它会得到玩家(bodyA)的类别,即 2,它会得到敌人(bodyB)的类别,即 4。所以 2 (0b10) OR 4 ( 0b100) 等于分配给冲突的 6 (0b110)。

    2. 然后在 if 语句中,它检查 6 的碰撞是否等于 (PC.player | PC.enemy),这是正确的,因此玩家和敌人之间发生了交互,因为它会如果 6 == 6。

    您可以使用变量碰撞来测试任何交互。例如,在我的测试游戏中,我有以下函数来测试触摸的对象。

    func didBeginContact(contact: SKPhysicsContact) {
        let collision: UInt32 = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
    
        //Collision between the penguin and land
        if collision == PhysicsCategory.Land | PhysicsCategory.Animal {
            lostLevel()
        } else if collision == PhysicsCategory.Animal | PhysicsCategory.Pillow {
            animalCounter -= 1
            if animalCounter == 0 {
                wonLevel()
            }
        }
    }
    

    【讨论】:

    • 非常感谢您的回答!!
    • 这是一个详细的解释。所以碰撞位掩码被系统用来识别碰撞,这会触发 didBeginContant 函数,在这个函数中 categoryBitMask 可以用来隔离发生了哪个碰撞。