基于 SpriteKit 中的碰撞检测更新分数的问题

Posted

技术标签:

【中文标题】基于 SpriteKit 中的碰撞检测更新分数的问题【英文标题】:Issue with updating score based on collision detection in SpriteKit 【发布时间】:2018-04-28 23:07:55 【问题描述】:

我正在开发一款 SpriteKit 游戏,在其中一个场景中,我必须根据两个精灵之间的碰撞来更新分数。每当这两个 SKSpriteNodes 相互碰撞时,我打算将分数增加 1。 SpriteKit 中有一个碰撞检测方法,它自己负责碰撞检测。我正在使用默认方法 didBeginContact: 来检测碰撞,删除涉及碰撞的对象之一并将分数增加 1。有多个相同类型的对象从顶部掉落,并且有一个玩家可以移动的篮子状对象水平地接住那些坠落的物体。与篮筐相撞时,从顶部掉落的物体会被移除,并且得分会增加。问题很简单,分数不仅增加了 1,而且增加了 2、3、4、5。这意味着它不会检测到应有的单个碰撞,而是检测到多个碰撞并因此相应地增加分数。 我在这里查看了另一个类似的问题,但该解决方案永远不适用于我的。在我的限时游戏中,类似的物体不断从顶部掉落,直到时间结束。有没有办法解决这个问题。我尝试在碰撞检测方法中使用 bool 变量,例如 didCollide,甚至在单独的分数递增方法中,但问题没有得到解决。 这是我在碰撞检测方法中尝试的代码。

-(void)didBeginContact:(SKPhysicsContact *)contact 

if (contact.bodyA.categoryBitMask == Stones) 
    [contact.bodyA.node removeFromParent];

    if (contactOccurred == NO) 
     contactOccurred = YES;
     [self updateScore:contactOccurred];
     


else 
    [contact.bodyB.node removeFromParent];

    if (contactOccurred == NO) 
    ContactOccurred = YES;
    [self updateScore:contactOccurred];
             
  
  

增加分数的方法的代码sn-p在这里。

-(void)updateScore:(BOOL)collisionOccurred 

if (collisionOccurred == YES) 

    contactOccurred = NO;
    self.score= self.score + 1;

【问题讨论】:

您需要解释您尝试过的内容、获得的结果以及您认为是问题根源的相关小sn-ps代码。以目前的形式,您的问题无法在指南内回答。 我已经添加了代码 sn-ps。必须有某种方法来阻止对 didBeginContact 方法的多次调用,而每次碰撞时它应该只调用一次。 我认为这是***.com/q/39505583/1430420的副本 这可能与所指出的问题相同,但该问题未得到成功回答。您已经提供了最终对您有用的答案,但这并不是真正的解决方案。通过确保在一次碰撞期间不多次调用 didBeginContact 方法,可以从逻辑上解决问题。 【参考方案1】:

在阅读了一些 Apple 的文档并尝试了不同的选项后,我自己解决了这个问题。问题是,didBeginContact 方法是 SpriteKit 提供的用于检测接触和碰撞的默认方法,正如 Joern 试图在他的回答中解释的那样,它被多次调用。但是在我的游戏中涉及碰撞的物体并不是不规则的形状。一种对象是椭圆形的,而另一种对象或多或少是矩形的。然而,每当两个对象之间发生接触时,都会多次调用该方法。

我是怎么解决的? 我尝试应用 Joern 建议的技术,尽管我知道这不是我正在寻找的真正解决方案,而是更多的临时掩饰。但令人惊讶的是,它对我的​​游戏不起作用,因为分数仍然会随机增加。我删除了我的椭圆形精灵并用简单的圆形纯色精灵替换它们,以防我的椭圆形精灵在边缘附近不光滑。即便如此,问题仍然存在,导致我在此链接a link 上找到了 Apple 的文档。我开始知道,为了从物理体中获得最佳性能并提高碰撞检测的准确性,应该尽可能选择更简单的物理体。圆形物理身体的性能效率最高,因为它的处理速度非常快且最简单。接下来是矩形物理体,然后是多边形物理体,最后是图像纹理的物理体。物理体越复杂,计算成本和失去准确性的机会就会增加。我使用图像纹理创建了碰撞对象的物理实体。不知何故,从纹理创建的物理实体是(或至少在我的情况下)为什么 didContactMethod 被多次调用的原因。即使是从纹理创建的简单圆形精灵的物理体也将分数增加 2 而不是 1。我更改了代码以为我的椭圆形精灵创建圆形物理体,现在一切都完美了,无需更改要删除的类别节点或任何布尔标志。

通过使用布尔标志或任何其他方式避免多次调用 didBeginContact 方法可能是一种掩饰,但不是在少数情况下有效但在其他情况下无效的解决方案。尽可能尝试使用最简单的物理实体,尤其是当您开始从碰撞和接触中获得不准确的结果时。

【讨论】:

【参考方案2】:

当您使用其 SKPhysicsBodies 具有不规则形状的 SKSpriteNodes 时,它们在接触时可以有多个接触点。这就是为什么 didBeginContact 在从其父节点移除之前可以为相同的两个 SKSpriteNode 调用多次的原因。

请参阅此示例以了解具有 2 个接触点的联系人(这会导致 didBeginContact 为相同的两个对象调用两次):

为避免这种情况,您可以在didBeginContact 中更改physicsBody 的categoryBitMask,以便不再识别以下所有联系人:

-(void)didBeginContact:(SKPhysicsContact *)contact 

    if (contact.bodyA.categoryBitMask == Stones) 
        contact.bodyA.categoryBitMask = None
        [contact.bodyA.node removeFromParent];
        self.score= self.score + 1;
     else if (contact.bodyB.categoryBitMask == Stones) 
        contact.bodyB.categoryBitMask = None
        [contact.bodyB.node removeFromParent];
        self.score= self.score + 1;
           

显然,您必须定义None 位掩码值并将其从篮子的contactTestBitMask 中排除,例如SKSpriteNode。这样,在将 Stone SKSpriteNode 的 categoryBitMaskStones 更改为 None 后,所有进一步的联系人都将被忽略。

【讨论】:

你确定这有效吗?我试了一下,发现它阻止了随后的碰撞,但不是已经排队的多个联系人,并且调用了“didBegin”。 它对我有用,我上周遇到了同样的问题并用这个解决方案修复了它。 感谢您的回答乔恩,但它对我不起作用。我创建了一个新类别来去除石头,但它仍然不起作用。此外,我的石头形状是完美的椭圆形,因此不可能在多个点上与篮子接触。 篮子移动的速度越快,与石头碰撞的分数越多,即从2到6甚至7。篮子用手指水平移动。 大家好,我刚刚看到我在else 之后忘记了bodyBif 子句。这可能是它不适合您的原因。 (我更新了答案)。

以上是关于基于 SpriteKit 中的碰撞检测更新分数的问题的主要内容,如果未能解决你的问题,请参考以下文章

Spritekit 碰撞检测无法正常工作

覆盖 SpriteKit 中的碰撞

未检测到 spritekit 物理碰撞

Sprite kit:删除特定节点而不是所有节点

未检测到 SpriteKit 碰撞

未检测到 SpriteKit 碰撞