Swift 理解SpriteKit冲突位掩码

Swift 理解SpriteKit冲突位掩码,swift,xcode,sprite-kit,Swift,Xcode,Sprite Kit,我正在学习使用SpriteKit。我正在努力理解以下代码: struct PhysicsCategory { static let None : UInt32 = 0 static let All : UInt32 = UInt32.max static let Monster : UInt32 = 0b1 // 1 static let Projectile: UInt32 = 0b10 // 2 } 我们为什么要将这些东西命名

我正在学习使用SpriteKit
。我正在努力理解以下代码:

struct PhysicsCategory {
  static let None      : UInt32 = 0
  static let All       : UInt32 = UInt32.max
  static let Monster   : UInt32 = 0b1       // 1
  static let Projectile: UInt32 = 0b10      // 2
}
我们为什么要将这些东西命名为
位图
,它们在下面的代码中是如何工作的

func didBegin(_ contact: SKPhysicsContact) {

    // 1
    var firstBody: SKPhysicsBody
    var secondBody: SKPhysicsBody
    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
        firstBody = contact.bodyA
        secondBody = contact.bodyB
    } else {
        firstBody = contact.bodyB
        secondBody = contact.bodyA
    }

    // 2
    if ((firstBody.categoryBitMask & PhysicsCategory.Monster != 0) &&
        (secondBody.categoryBitMask & PhysicsCategory.Projectile != 0)) {
        if let monster = firstBody.node as? SKSpriteNode, let
            projectile = secondBody.node as? SKSpriteNode {
            projectileDidCollideWithMonster(projectile: projectile, monster: monster)
func didbeagin(uu联系人:skphysiccontact){
// 1
var firstBody:SKPhysicsBody
var第二个主体:SKPhysicsBody
如果contact.bodyA.categoryBitMask

谢谢!

操纵contactTest和collison位掩码以启用/禁用特定的接触和冲突。

在本例中,我们将使用4个实体,为简单起见,仅显示位掩码的最后8位。4个实体是3个SKSPRITENODE(每个实体有一个物理实体)和一个边界:

let edge = frame.insetBy(dx: 0, dy: 0)
physicsBody = SKPhysicsBody(edgeLoopFrom: edge)
请注意,“边缘”物理体是场景的物理体,而不是节点

我们定义了4个独特的类别

let purpleSquareCategory:   UInt32 = 1 << 0  // bitmask is ...00000001
let redCircleCategory:      UInt32 = 1 << 1  // bitmask is ...00000010
let blueSquareCategory:     UInt32 = 1 << 2  // bitmask is ...00000100
let edgeCategory:           UInt32 = 1 << 31  // bitmask is 10000...00000000
如果实体的collisionBitMask中的某个位设置为1,则它会碰撞(反弹)在其categoryBitMask中相同位置具有“1”的任何实体。contactTestBitMask也是如此

除非您另行指定,否则所有内容都会与其他内容冲突,并且不会生成任何联系人(当任何内容与其他内容接触时,您的代码不会收到通知):

每个位置中的每个位都是“1”,因此当与任何其他categoryBitMask进行比较时,Sprite Kit会找到一个“1”,因此会发生碰撞。如果不希望此实体与某个类别发生碰撞,则必须将collisonBitMask中的正确位设置为“0”

其contactTestbitMask设置为所有0:

redCircle.physicsBody.contactTestBitMask = 00000000000000000000000000000000  // 32 '0's
与collisionBitMask相同,但反向除外

可以使用以下方法关闭实体之间的接触或碰撞(保持现有接触或碰撞不变):

我们使用nodeB类别位掩码的倒数(逻辑NOT,~运算符)逻辑和nodeA的冲突位掩码,以“关闭”该位nodeA的位掩码。例如,为了防止红色圆圈与紫色方块碰撞:

redCircle.physicsBody?.collisionBitMask = redCircle.physicsBody?.collisionBitMask & ~purpleSquareCategory
可缩短为:

redCircle.physicsBody?.collisionBitMask &= ~purpleSquareCategory
说明:

redCircle.physicsBody.collisonBitMask = 11111111111111111111111111111111
purpleSquareCategory  = 00000000000000000000000000000001
~purpleSquareCategory = 11111111111111111111111111111110 
11111111111111111111111111111111 & 11111111111111111111111111111110 = 11111111111111111111111111111110 
redCircle.physicsBody.collisonBitMask现在等于1111111111111 0 红色圆圈不再与类别为…0001(紫色方形)的实体碰撞

您可以直接设置它,而不是关闭CollisonSBitMask中的单个位:

blueSquare.physicsBody?.collisionBitMask = (redCircleCategory | purpleSquareCategory)
i.e. blueSquare.physicsBody?.collisionBitMask = (....00000010 OR ....00000001)
这等于blueSquare.physicsBody?.collisionBitMask=..00000011

blueSquare将仅与类别为或..01或..10的实体碰撞

可以在任意点使用以下选项启用两个实体之间的接触或碰撞(不影响任何现有接触或碰撞):

redCircle.physicsBody?.contactTestBitMask |= purpleSquareCategory
我们在逻辑上将redCircle的位掩码与purpleSquare的类别位掩码相结合,以“打开”redCircle位掩码中的该位。这将使redCircel位掩码中的任何其他位不受影响

您可以确保每个形状都从屏幕边缘“反弹”,如下所示:

// Make sure everything collides with the screen edge
enumerateChildNodes(withName: "//*") { node, _ in
    node.physicsBody?.collisionBitMask |= self.edgeCategory  //Add edgeCategory to the collision bit mask
}
注:

碰撞可以是单面的,即对象A可以碰撞(反弹)对象B,而对象B则像什么都没有发生过一样继续进行。如果您想让两个对象相互反弹,则必须让它们都与另一个碰撞:

blueSquare.physicsBody?.collisionBitMask = redCircleCategory
redcircle.physicsBody?.collisionBitMask = blueSquareCategory
但是,触点不是单面的;如果您想知道对象A何时接触(接触)对象B,就可以在对象A上设置与对象B相关的触点检测。您不必为对象A在对象B上设置触点检测

blueSquare.physicsBody?.contactTestBitMask = redCircleCategory

我们不需要redcircle.physicsBody?.contactTestBitMask=blueSquareCategory
位掩码是用于以二进制格式描述项的标志

假设你有8种描述事物的方法

我们可以将这8项内容放入一个字节中,因为一个字节中有8位,这样可以节省空间并更快地执行操作

下面是8个描述的示例

Attackable 1 << 0  
Ranged     1 << 1  
Undead     1 << 2  
Magic      1 << 3  
Regenerate 1 << 4  
Burning    1 << 5  
Frozen     1 << 6  
Poison     1 << 7  
现在让我们假设他的箭是火箭,我会这样分类:

arrow.categoryBitmask=Burning

00100000
||||||||_ Attackable
|||||||_ Ranged
||||||_ Undead
|||||_ Magic
||||_ Regenerate
|||_ Burning
||_ Frozen
|_ Poison
最后,我们有一个僵尸,可以被击中,并随着时间的推移再生

zombie.categoryBitmask=可攻击+不死生物+再生

00010101
||||||||_ Attackable
|||||||_ Ranged
||||||_ Undead
|||||_ Magic
||||_ Regenerate
|||_ Burning
||_ Frozen
|_ Poison
现在我希望我的箭只击中可攻击的精灵(在这种情况下是僵尸)

我会设置
contactTestBitmask
来告诉箭头他能击中什么

arrow.contactTestBitmask = Attackable 00000001
现在我们需要检查箭头何时击中僵尸,这就是
didBeginContact
的作用

didBeginContact
将要做的是,通过使用AND操作查找匹配项,检查移动项的
contactTestBitmask
,以将其移动到其命中的
categoryBitmask

就我们而言

arrow.contactTestBitmask =  00000001
zombie.categoryMask      =  00010101 AND
                            --------
                            00000001
由于我们的值大于0,因此联系成功

这意味着你被解雇了

现在我们已经进入了一个新的阶段,我们需要确定哪个物理体是我们的箭,哪个物理体是我们的僵尸

这就是下一句话的意义所在

func didBegin(_ contact: SKPhysicsContact) {

    // 1
    var firstBody: SKPhysicsBody
    var secondBody: SKPhysicsBody
    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
        firstBody = contact.bodyA
        secondBody = contact.bodyB
    } else {
        firstBody = contact.bodyB
        secondBody = contact.bodyA
}
但是,如果这个箭是冰箭呢?我们不想讨论这个if语句

现在我们可以添加第二个条件来冻结僵尸

if (firstBody & Undead > 0) && (secondBody & Frozen > 0)
{
//freeze zombie
}
这些ifs所做的是确保身体的某些功能已打开,然后执行一些动作来响应这些功能

为了进一步了解位掩码是如何工作的,我将研究如何制作真值表。这就是问题的本质。我们只是创建了一些t
arrow.contactTestBitmask = Attackable 00000001
arrow.contactTestBitmask =  00000001
zombie.categoryMask      =  00010101 AND
                            --------
                            00000001
func didBegin(_ contact: SKPhysicsContact) {

    // 1
    var firstBody: SKPhysicsBody
    var secondBody: SKPhysicsBody
    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
        firstBody = contact.bodyA
        secondBody = contact.bodyB
    } else {
        firstBody = contact.bodyB
        secondBody = contact.bodyA
}
if (firstBody & Undead > 0) && (secondBody & Burning > 0)
{
//burn zombie
}
if (firstBody & Undead > 0) && (secondBody & Frozen > 0)
{
//freeze zombie
}