同一个 SKPhysicsBody 多次调用 didBeginContact

Posted

技术标签:

【中文标题】同一个 SKPhysicsBody 多次调用 didBeginContact【英文标题】:didBeginContact is being called multiple times for the same SKPhysicsBody 【发布时间】: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 次命中/接触,因为在某些情况下,这比创建任何额外的逻辑更需要,包括下面的答案。此外,如果不需要,它可以节省资源以不检测更多联系人.. 【参考方案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) 中允许的形状可以有多种形式和形状。

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

这是我的做法:

    声明一个var布尔值:

    var contactDone = Bool()
    

    在程序开始时初始化它(例如didMoveToView下面)

    contactDone = false
    

    签入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
    
          
    
    
    

    清除标志让它再次检查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

【讨论】:

以上是关于同一个 SKPhysicsBody 多次调用 didBeginContact的主要内容,如果未能解决你的问题,请参考以下文章

SKPhysicsBody 改变动画/物理速度

调整使用 SKPhysicsBody(circleOfRadius:) 创建的 SpriteKit SKPhysicsBody

SpriteKit:SKPhysicsBody 溜走

SKPhysicsBody 不如预期

SpriteKit 的 SKPhysicsBody 与多边形辅助工具

SKPhysicsBody 未与 SKShapeNode 对齐