为什么didBeginContact被多次调用?

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

如果英雄再次遇到同一个敌人,他应该seenEnemies一次,所以我不能只保留一个被seenEnemies哈希集来处理上面的问题。

现在的问题是:你如何确保每个英雄/敌人的联系人都只有一个生命被扣除?

我有同样的问题(单个敌人被摧毁的分数会增加多倍,并且单个实例的伤害会损失多个生命分数)。苹果论坛的用户认为这是[SKPhysicsBody bodyWithTexture:size:]中的一个错误,但是我不要相信是这样的,因为它也和其他的构造函数一样。

首先, categoryBitMaskcontactTestBitMask显然非常重要。 看看苹果的SpriteKit物理碰撞示例代码 :

//联系人往往是一个双重调度问题; 你想要的效果是基于联系人中两个实体的types。 这个以暴力的方式对这个样本进行检查,通过检查每个types。 一个更复杂的例子可能使用对象上的方法来执行types检查。

// 联系人可以按照任意顺序显示 ,因此通常您需要检查每个联系人 。 在这个例子中,类别types是有序的,所以如果代码错乱,代码会交换这两个对象。 这允许代码只testing一次冲突。

我做了什么来解决它是在处理每个条件后设置一个标志。 在我的情况下,我testingbodyA.node.parentdidBeginContact是否为零,因为我在导弹/敌人节点上调用removeFromParent()来销毁它们。

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

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

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

在这里输入图像说明

我遇到了同样的问题。 在我的情况下, didBeginContact()被多次调用(我计算了5次)。 由于子弹是一种简单的圆形格式,我同意@SFX,它不能只是在纹理体中的错误。 testing显示在didBeginContact()调用之间没有调用update() 。 所以解决scheme很简单(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()但是根本没有被调用。 我没有进一步调查。

顺便说一句:我只是从Android切换,我对这个系统的简单性和稳定性印象深刻:-)

这是一个让玩家在命中一段时间​​后无懈可击的选项:

A.创build一个variables,使得玩家在被击中几秒钟后不会丧失生命。

  1. 创build一个名为isInvuln(设置为FALSE)的全局布尔variables和一个名为invulnTime的NSTimeInterval。
  2. 在处理玩家和敌人进行联系的方法中,在生命前检查isInvuln是否为False。 (如果isInvuln是真的…什么都不做)
  3. 如果isInvuln为false,则取一个生命值,然后将isInvuln设置为true。

      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; 

这样当敌方和玩家发生碰撞时,玩家就会失去生命,并且会变成无敌的3秒。 之后3秒,玩家可以再次受到伤害。 如果敌人在3秒内无法接触玩家,那么联系方式什么也不做。 希望这有助于激发想法来解决您的问题。

我想出了一个简单的解决scheme:

只要在检测到联系后立即将其身体的categoryBitMask值更改为0或未使用的值。

例如:

 if (firstBody.categoryBitMask == padCategory && secondBody.categoryBitMask == colorBallCategory) { secondBody.categoryBitMask = 0; // DO OTHER THING HERE } 

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