【问题标题】:Stop objects from colliding using SpriteKit使用 SpriteKit 阻止对象碰撞
【发布时间】:2016-10-30 18:50:59
【问题描述】:

我正在测试 SpriteKit 的功能,但遇到了问题。我正在阅读位掩码、碰撞、类别和联系。我知道它们是什么,至少,我不明白类别位掩码的意义,但是我得到了冲突位掩码,这是我解决问题所需要的。

好的,所以我的问题是我有两种不同类型的精灵:objectsecond。 名称并没有多大意义,但这只是为了的测试。我想让第二个有冲动,我想让对象有一种力量。我能够在精灵上应用各自的向量,但我不希望它们相互碰撞。我希望他们直接通过,忽略彼此的存在。

我试图通过相互分配不同的冲突位掩码来解决这个问题:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    let texture = SKTexture(imageNamed: "pokeball")
    let object = SKSpriteNode(texture: texture)

    object.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: object.size.width,height: object.size.height))
    object.physicsBody?.affectedByGravity = false
    object.yScale = 0.5
    object.xScale = 0.5

    for t in touches {
        object.position = t.location(in: self)
    }
    self.addChild(object)
    object.physicsBody?.collisionBitMask = UInt32(4)
    object.physicsBody?.applyForce(CGVector(dx: 0, dy: 10))

}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    let texture = SKTexture(imageNamed: "purple")
    let second = SKSpriteNode(texture: texture)
    let impulse : Double = 20
    let x = (impulse * Double(cosf(45)))
    let y = Double(impulse * Double(sinf(45)))
    let vector = CGVector(dx: x, dy: y)
    second.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: second.size.width,height: second.size.height))

    second.yScale = 1.5
    second.xScale = 1.5
    second.physicsBody?.isDynamic = true

    for t in touches {
        second.position = t.location(in: self)
    }
    self.addChild(second)
    second.physicsBody?.collisionBitMask = UInt32(1)
    second.physicsBody?.applyImpulse(vector)
}

所以对象的位掩码为 4:

object.physicsBody?.collisionBitMask = UInt32(4)

第二个位掩码为 1:

second.physicsBody?.collisionBitMask = UInt32(1)

我运行了模拟器,但它们仍然相互碰撞,所以我上网并试图寻找一些答案:我找到了一个说我必须使用如下数字的答案:

这些是位掩码,你不能使用任意数字 1,2,3,4,5 - 你必须使用 1,2,4,8,16 等等 -

有人可以解释为什么吗?但是,这不是问题,因为我使用的是 1 和 4

我遇到的下一个问题说我必须使用二进制数(0100)和(0010),我试过了,同样的问题:仍然碰撞。

我会留下碰撞的照片: Collisions

有人知道为什么会这样吗?如果这是一个非常愚蠢的错误或已经问过的问题,我提前道歉,我只是找不到它。

【问题讨论】:

  • 看看这个:stackoverflow.com/a/39064710/3402095 寻找类别位掩码的默认值。那里都有解释。
  • 指定一个物理体的碰撞位掩码为 4 只是意味着它与 categoryBitMask 为 4 的物理体发生碰撞(实际上是设置了 '4' 位,但让我们从最简单的开始例子)。如果您还没有看到任何 categoryBitMasks,那么事情就不会起作用。

标签: swift sprite-kit


【解决方案1】:

有很多关于这些主题的文档,但这里有一个实际示例。

categoryBitMasks 的威力

假设您拥有三个节点 poolbasketballbowlingball 的集合。现在,很明显,我们希望basketballbowlingball 相互碰撞。所以你像这样设置collisionBitMasks:

basketball.physicsBody?.collisionBitMask    = UInt32(2)
bowlingball.physicsBody?.collisionBitMask   = UInt32(2)

太好了。现在,我们希望bowlingball 下沉到@98​​7654331@ 的底部,并且basketballpool 相撞(可能更多的是飞溅,但请耐心等待)。我们将如何做到这一点?我们可以试试:

pool.physicsBody?.collisionBitMask = UInt32(2) // ?

但是等等,这会使basketballbowlingballpool 发生冲突。我们只希望basketball 与池碰撞,而我们希望bowlingball 忽略pool 并直接沉入底部而不会发生碰撞。这就是categoryBitMasks 派上用场的地方:

let basketballBitMask     = UInt32(1)
let bowlingballBitMask    = UInt32(2)
let poolBitMask           = UInt32(4)  // Why 4? See next section

basketball.physicsBody?.categoryBitMask    = basketballBitMask
bowlingball.physicsBody?.categoryBitMask   = bowlingballBitMask
pool.physicsBody?.categoryBitMask          = poolBitMask

因为每个对象都有一个唯一的编号,您现在可以选择您希望另一个对象与哪些对象发生碰撞:

// basketball physics body collides with bowlingball(2) OR pool(4)
basketball.physicsBody?.collisionBitMask    = bowlingballBitMask | poolBitMask
// ( '|' = logical OR operator)

// bowlingball physics body only collides with basketball(1)
bowlingball.physicsBody?.collisionBitMask   = basketballBitMask

// pool physics body only collides with basketball(1)
pool.physicsBody?.collisionBitMask          = basketballBitmask

如果你不确定奇怪的 '|' 是什么符号正在做,我强烈推荐高级运算符上的swift documentation,以帮助您了解这里发生的事情。

为什么不直接使用 collisionBitMasks?

好的,我们已经设置了一些位掩码。但是它们是如何使用的呢?如果我们只有两个对象,为什么不能只比较碰撞位掩码?

简单地说,这不是它的工作原理。当bowlingballpool 接触时,SpriteKit 物理引擎会将bowlingballcategoryBitMaskpool 的碰撞位掩码(或反之亦然;结果相同):

objectsShouldCollide = (bowlingball.physicsBody?.categoryBitMask & 
                        pool.physicsBody?.collisionBitMask)
// objectsShouldCollide = (ob010 & 0b100) = 0b000 

因为bowlingballcategoryBitMaskpoolcollisionBitMask 共有零位,所以objectsShouldCollide 等于零,SpriteKit 将阻止对象发生碰撞。

但是,在您的情况下,您没有设置对象的categoryBitMasks。所以它们的默认值为 2^32,或 0xFFFFFFFF (hexadecimal representation) 或二进制 0b1111111111111111111111111111111。所以当一个“物体”碰到一个“第二个”物体时,SpriteKit 会这样做:

objectsShouldCollide = (0b11111111111111111111111111111111 & // Default categoryBitMask for "object" 
                        0b00000000000000000000000000000001)  // collisionBitMask for "second" object
//                   =  0b00000000000000000000000000000001

所以当你还没有定义object的categoryBitMask时,无论你将second对象的collisionBitMask设置成什么,objectsShouldCollide永远不会为零,它们总是会发生碰撞。

注意:您可以将对象的collisionBitMask 设置为0;但是那个物体将永远无法与任何东西发生碰撞。

对 categoryBitMask 使用 2 的幂(0、1、2、4、8 等)

现在假设我们想要包含多个相互碰撞的bowlingballs。简单:

bowlingball.physicsBody?.collisionBitMask  = basketballBitMask | bowlingballBitMask
// bowlingball collision bit mask (in binary)  = 0b10 | 0b01 = 0b11
// bowlingball collision bit mask (in decimal)  = 2 | 1 = 3 

在这里您可以看到,如果我们将 pools 物理类别设置为 UInt32(3),它将不再与 bowlingballbasketball 区分开来。

进一步的建议

有目的地学习name 变量,即使您只是将它们用于测试(尽管巧合的是,“objectsecond 对象”效果很好)。

使用位掩码结构来简化代码并提高可读性:

struct PhysicsCategory {
    static let Obj1         : UInt32 = 0b1 << 0
    static let Obj2         : UInt32 = 0b1 << 1
    static let Obj3         : UInt32 = 0b1 << 2
    static let Obj4         : UInt32 = 0b1 << 3
}

obj1.physicsBody?.categoryBitmask = PhysicsCategory.Obj1 // etc

【讨论】:

  • 当我可以直接进入碰撞位掩码时,我不明白您为什么需要类别位掩码?你能解释一下吗?就像我修改碰撞位掩码时不会覆盖默认值一样吗?
  • 没有。类别位掩码与冲突位掩码不同。阅读我更新的答案。
【解决方案2】:

为什么不下载并使用这个简单的 Sprite-Kit 项目呢?它创建各种几何形状,将其中一些设置为碰撞,一些设置为接触,使用checkPhysics() 函数来显示将要发生的事情,然后让您四处滑动这些形状。

Attack button in SpriteKit

关于它的工作原理的任何问题,我都非常乐意尝试和解释。

【讨论】:

  • 需要有一种方式来奖励您,而不是对文档中的链接项目投赞成票。非常好!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多