为什么didBeginContact被多次调用?

时间:2014-06-15 09:32:42

标签: ios cocoa sprite-kit

在使用Sprite Kit以及Sprite Kit的内置物理引擎中的接触检测的iOS游戏中,每当他与敌人接触时,我将Hero的数量减少一个。这是通过didBeginContact方法完成的。 然而,似乎这种方法不仅仅是在接触开始时调用一次,而是在Hero和敌人重叠时连续调用:当我在该方法中设置断点时,我可以看到,它是完全相同的存在为contact.bodyAcontact.bodyB的物理实体实例。结果是,英雄将失去多重生命,即使他只通过一个敌人。

如果Hero稍后再遇到同一个敌人,他应该减去一个实际的生命,因此我不能只保留一个seenEnemies散列集来处理上面的问题。

现在的问题是:你如何确保每个英雄/敌人联系人只减去一个生命?

6 个答案:

答案 0 :(得分:16)

我遇到了同样的问题(单个敌人被摧毁的分数增加了多倍,单个伤害实例丢失了多个生命点。)Apple论坛上的用户认为它是a bug in [SKPhysicsBody bodyWithTexture:size:]但我不知道我相信是这样的,因为它也与其他建设者一起发生。

首先,显然categoryBitMaskcontactTestBitMask非常重要。看一下Apple的SpriteKit Physics Collisions sample code

  

//联系人通常是双重调度问题;您想要的效果取决于联系人中两个主体的类型。通过检查每种类型,以蛮力的方式对此进行采样。更复杂的示例可能使用对象上的方法来执行类型检查。

     

// 联系人可以按任意顺序出现,因此通常您需要检查   每一个对抗另一个。在此示例中,类别类型排序很好,因此   如果它们出现故障,代码会交换这两个实体。 这允许代码   只测试一次碰撞。

我要解决的是在处理每个条件后设置一个标志。就我而言,我在bodyA.node.parent测试didBeginContact是否为零,因为我在导弹/敌方节点上调用removeFromParent()来销毁它们。

我认为您应该期望事件多次触发,并且您的代码必须确保它只处理一次。

答案 1 :(得分:16)

多次触发didBeginContact的原因是因为凹形上有多个接触点。

如果你看下面的图片,你会看到我有2个精灵,一个黑色的星星和一个红色的矩形。当黑色星星击中红色矩形时,它会在多个点上击中它,并以蓝色圈出。然后,Sprite Kit将为每个行交集调用,以便开发人员可以为每个联系人使用contactPoint变量。

enter image description here

答案 2 :(得分:6)

我想出了简单的解决方案:

在检测到联系后立即将正文的categoryBitMask值更改为0或未使用的值。

例如:

if (firstBody.categoryBitMask == padCategory && secondBody.categoryBitMask == colorBallCategory) {

      secondBody.categoryBitMask = 0;

      // DO OTHER THING HERE

}

答案 3 :(得分:5)

我遇到了同样的问题。在我的情况下,一个子弹与敌人的一次接触,多次召唤didBeginContact()(我数到5次)。由于子弹是一种简单的圆形格式,我同意@SFX它不能仅仅是纹理体中的一个错误。测试显示update()次来电之间没有didBeginContact()来电。所以解决方案很简单(Swift):

var updatesCalled = 0
...
internal update() {
  updatesCalled ++
}
...
internal func didBeginContact(contact: SKPhysicsContact) {
    NSLog("didBeginContact: (\(contact.contactPoint.x), \(contact.contactPoint.y)), \(updatesCalled)")
    if(updatesCalled == 0) {return} // No real change since last call
    updatesCalled = 0
    ... your code here ...
}

我尝试了didEndContact(),但根本没有调用。我没有进一步调查此事。

BTW:我刚刚从Android切换,我对这个系统的简易性和稳定性印象深刻: - )

答案 4 :(得分:1)

这是一个让玩家在被击中一段时间​​后无法攻击的选项:

一个。创建一个变量,使玩家在被击中几秒钟后无懈可击,失去生命。

  1. 创建一个名为isInvuln的全局布尔变量(设置为FALSE)和一个名为invulnTime的NSTimeInterval。
  2. 在处理玩家和敌人接触的方法中,在生命之前检查isInvuln是否为假。 (如果isInvuln是真的......什么都不做)
  3. 如果isInvuln为false,请将生命设置为isInvuln为真。

     if(self.isInvuln == FALSE){
          self.player.lives-=1;
          self.isInvuln = True;}
    
  4. 添加到您的updateWithCurrentTime:

     if(self.isInvuln==True){
     self.invulnTime += timeSinceLast;}
    
     if (self.invulnTime > 3) {             
         self.isInvuln = FALSE:}
         self.invulnTime= 0;
    
  5. 这将使得当敌人和玩家发生碰撞时,玩家失去生命并且在3秒内变得无懈可击。在3秒后,玩家可以再次受到伤害。如果敌人在3个无敌时间内与玩家联系,则联系方法不执行任何操作。希望这有助于激发想法来解决您的问题。

答案 5 :(得分:0)

根据我的经验,didEndContact& didBeginContact在对象重叠时被多次调用。这也发生在使用iOS 9的SceneKit中,因此我必须假设它是一种预期的行为。