Ios 同一SKPhysicsBody多次调用didBeginContact

Ios 同一SKPhysicsBody多次调用didBeginContact,ios,swift,sprite-kit,skphysicsbody,Ios,Swift,Sprite Kit,Skphysicsbody,一个物理实体来自纹理shield.physicsBody=SKPhysicsBody(纹理:shieldTexture,大小:shieldTexture.size()) 另一个来自圆sand.physicsBody=SKPhysicsBody(圆半径:sand.size.width/2) 当两个对象有时相互接触时sand.physicsBody=SKPhysicsBody(圆圈半径:sand.size.width/2) 被多次调用。如何使每个对象只被调用一次,即使我在它接触后立即将其从父对象

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

另一个来自圆
sand.physicsBody=SKPhysicsBody(圆半径:sand.size.width/2)

当两个对象有时相互接触时
sand.physicsBody=SKPhysicsBody(圆圈半径:sand.size.width/2)

被多次调用。如何使每个对象只被调用一次,即使我在它接触后立即将其从父对象中移除。

我已经找到了如何使
func didBeginContact(contact:skphysiccontact)
只被调用一次的方法。这允许具有纹理
SKPhysicsBody(纹理:大小:)
的物理实体计算一次碰撞,即使在现实中(由于纹理的物理实体的性质),此函数将被多次调用

步骤1:

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

 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)
    }
}
这允许每次创建对象时都有一个唯一的名称

步骤2:

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

var number = 0 

ball.name = "ball \(number)"
步骤3:现在在
func didBeginContact(contact:skphysiccontact)
中查找名称是否在数组中。如果是递增分数,则删除节点,并从数组中删除名称。如果名称不在数组中,请不要执行任何操作

从数组中删除名称后,我们现在只需对函数调用计数一次

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

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

LearnCos2D是正确的,只要两个对象的SKPhysicsbody接触,SKPhysicsbody didBeginContact就会连续调用,因为我们在
SKPhysicsbody(纹理:xxx,大小:xx)
中允许的形状可以有多种形式和形状

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

我是这样做的:

  • 声明一个
    var
    boolean:

    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!)
            }
        } 
    }
    
  • 在程序开始时初始化它(例如,在
    didMoveToView
    下面)

  • 执行签入
    didBeginContact

    contactDone = false
    
    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
    中再次检查:

    contactDone = false
    
    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
    
          }
    }
    }
    

  • 它的工作原理与我使用SKPhysicBody(circleOfRadius:object.size.height/2)时的工作原理一样。

    在这种情况下,您可以在没有阵列的情况下使其工作。与此相反:

    func didEndContact(contact: SKPhysicsContact) {
    
    if((contact.bodyA.categoryBitMask) == scoreCategory || (contact.bodyB.categoryBitMask) == scoreCategory){
    
                if(contactDone == true){
    
                    contactDone = false
    
                }
    
    }
    
    }
    
    使用类似以下内容:

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

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

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


    只需更新您不想被重复检测到的对象的<代码> CythyByPase<代码>,这样系统就不会考虑后续的函数调用了。p> 使用基于纹理的物理体时,问题似乎更常见。我绕过它的方法是,如果在上一次碰撞触发器中指示相同的“contact.bodyA.node”,则禁止“didbeagin(uu'contact:skphysiccontact)”继续。i、 e:

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

    我的解决方案是,通过Date()计算时间。timeIntervalSince1970 联系迪德贝京

    if lastNodeA != contact.bodyA.node {
        lastNodeA = contact.bodyA.node
        ...
    
    let passedTime=Date().timeIntervalSince1970-lastContactTime
    lastContactTime=Date()。时间间隔1970
    如果通过时间<0.01{
    //同一物体
    返回
    }
    
    我相信这是按预期工作的,因为“来自纹理的身体”可能会在内部生成多个形状,每个形状都可能导致接触事件。在物理模拟步骤结束之前,移除节点不会移除实体。您必须手动“标记”已在此接触事件中处理的节点或主体,以便您可以跳过相同主体的任何后续接触事件。我希望有一个选项,用于从纹理设置SKPhysicsBody,使其通过参数进行设置,以便其行为与circleOfRadius中的SKPhysicsBody完全相同,因此在某些情况下,它只计算1次命中/接触比创建任何额外的逻辑(包括下面的答案)更令人渴望。此外,如果不需要更多联系人,也可以节省不检测联系人的资源。您不能将SKSpriteNode添加到阵列中吗?它是通过引用传递的,所以我们可以比较它们,我不明白为什么我们需要指定唯一的名称。当我使用“SKPhysicsBody(纹理:ghostTexture,大小:Obj.size)”时不起作用