【问题标题】:didBeginContact is being called multiple times for the same SKPhysicsBody同一个 SKPhysicsBody 多次调用 didBeginContact
【发布时间】:2015-01-15 04:04:08
【问题描述】:
 func didBeginContact(contact: SKPhysicsContact) {
    if ( contact.bodyA.categoryBitMask & BodyType.shield.rawValue ) == BodyType.shield.rawValue  {
        contact.bodyB.node?.removeFromParent()
        counter++
        println(counter)


    } else if ( contact.bodyB.categoryBitMask & BodyType.shield.rawValue ) == BodyType.shield.rawValue {
        contact.bodyA.node?.removeFromParent()
        counter++
        println(counter)
    }
}

一个物理体来自纹理shield.physicsBody = SKPhysicsBody(texture: shieldTexture, size: shieldTexture.size())

另一个来自圈子sand.physicsBody = SKPhysicsBody(circleOfRadius: sand.size.width/2)

当两个对象相互接触时,sand.physicsBody = SKPhysicsBody(circleOfRadius: sand.size.width/2) 会被多次调用。我如何让它只为每个对象调用一次,即使我一接触它就从父对象中删除它。

【问题讨论】:

  • 我相信这是按预期工作的,因为纹理中的主体可能会在内部生成多个形状,每个形状都可能导致接触事件。在物理模拟步骤结束之前,移除节点不会移除实体。您必须手动将节点或主体“标记”为已在此联系事件中处理,以便您可以跳过相同主体的任何后续联系事件。
  • 我希望 SKPhysicsBody 有一个选项可以从纹理中通过参数进行设置,因此它的行为与例如来自 circleOfRadius 的 SKPhysicsBody 所以它只计算 1 次命中/接触,因为在某些情况下,这比创建任何额外的逻辑更需要,包括下面的答案。此外,如果不需要,它可以节省资源以不检测更多联系人..

标签: ios swift sprite-kit skphysicsbody


【解决方案1】:

我已经知道如何让func didBeginContact(contact: SKPhysicsContact) 只被调用一次。这允许具有纹理 SKPhysicsBody(texture: size:) 的物理体计算一次碰撞,即使在现实中(由于纹理物理体的性质)该函数将被多次调用。

第 1 步:

为 SKSpriteNode 创建一个 name 属性(我们将在本示例中使用 ball) 并将其设置为唯一名称。我们可以通过创建一个 int 来做到这一点

var number = 0 

ball.name = "ball \(number)"

这允许在创建对象时使用唯一的名称。

第 2 步:

创建一个数组来保存这些,将球附加到数组中,增加数字

    var array: [String] = []
    var number = 0 

ball.name = "ball \(number)" 
array.append(ball.name!)
number ++

第 3 步:现在在 func didBeginContact(contact: SKPhysicsContact) 中查找名称是否在数组中。如果是增加分数,则删除节点,并从数组中删除名称。如果名称不在数组中,则不执行任何操作。

从数组中删除名称允许我们现在只计算一次函数调用。

func didBeginContact(contact: SKPhysicsContact) {
    if ( contact.bodyA.categoryBitMask & BodyType.shield.rawValue ) == BodyType.shield.rawValue  {
        var name = contact.bodyB.node?.name!
        let index = find(array, name!)

        if contains(array, name!) {
            score++
            contact.bodyB.node?.removeFromParent()
            array.removeAtIndex(index!)
        }
    } 
}

【讨论】:

  • 你不能把 SKSpriteNode 添加到数组中吗?它通过引用传递,因此我们可以比较它们,我不明白为什么我们需要分配唯一的名称。
【解决方案2】:

在这种情况下,您可以在没有数组的情况下使其工作。而不是这个:

contact.bodyA.node?.removeFromParent()
counter++

使用这样的东西:

if let node = contact.bodyA.node as? SKSpriteNode {
    if node.parent != nil {
        node.removeFromParent()
        counter++
    }
}

在第一次联系时,您从父节点中删除节点,在随后的调用中,if 语句中的代码将被跳过。

【讨论】:

    【解决方案3】:

    如果您使用contactbitmask 来确定要捕获哪些冲突,则有一个非常简单的解决方案。

    只需将您不想被重复检测到的对象的categoryBitMask 更新为未使用的新值,这样系统就不再认为后续函数调用相关。

    【讨论】:

      【解决方案4】:

      当使用基于纹理的物理实体时,问题似乎更频繁地发生。如果在之前的碰撞触发器中指示了相同的“contact.bodyA.node”,我解决它的方法是禁止“didBegin(_contact:SKPhysicsContact)”继续。即:

      if lastNodeA != contact.bodyA.node {
          lastNodeA = contact.bodyA.node
          ...
      

      【讨论】:

        【解决方案5】:

        LearnCocos2D 是对的,只要两个物体的 SKphysicsbody 接触,SKPhysicsbody didBeginContact 就会不断调用,因为我们在SKPhysicsBody(texture:xxx, size:xx) 中允许的形状可以有多种形式和形状。

        对于那些只需要检测一次的人,我们只需要使用一个布尔值作为标志来检查检测是否完成并结束。

        这是我的做法:

        1. 声明一个var布尔值:

          var contactDone = Bool()
          
        2. 在程序开始时初始化它(例如didMoveToView下面)

          contactDone = false
          
        3. 签入didBeginContact:

          func didBeginContact(contact:SKPhysicsBody){
          
          if((contact.bodyA.categoryBitMask) == scoreCategory ||       (contact.bodyB.categoryBitMask) == scoreCategory){
          
                if (contactDone == false){
          
                   // Increment score           
                   score++
          
                   // Set flag to disable multiple calls by checking in didEndContact
                   contactDone = true
          
                }
          }
          }
          
        4. 清除标志让它再次检查didEndContact

          func didEndContact(contact: SKPhysicsContact) {
          
          if((contact.bodyA.categoryBitMask) == scoreCategory || (contact.bodyB.categoryBitMask) == scoreCategory){
          
                      if(contactDone == true){
          
                          contactDone = false
          
                      }
          
          }
          
          }
          

        它就像我使用 SKPhysicBody(circleOfRadius: object.size.height/2) 时一样工作。

        【讨论】:

        • 当我使用'SKPhysicsBody(texture: ghostTexture, size: Obj.size)'时不起作用
        【解决方案6】:

        我的解决方案是,通过 Date().timeIntervalSince1970 花时间 在联系didBegin

        let passedTime = Date().timeIntervalSince1970 - lastContactTime
        
        lastContactTime = Date().timeIntervalSince1970
        
        if passedTime < 0.01 {
          // same object
          return
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-01-14
          • 1970-01-01
          • 2012-11-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多