SpriteKit:检测完整节点重叠

4
我有两个SKShapeNodes - 一个采用基于边缘的SKPhysicsBody,另一个采用基于体积的SKPhysicsBody,并且我想在没有碰撞的情况下检测它们的交集。我已经成功实现了这一点,SKPhysicsContactDelegate接触方法在一个节点经过另一个节点时被调用,但我的问题是,即使一个物体完全包含在另一个物体中,当“边缘”不再相交时,didEndContact也会被调用。最好的方法是确定真正的接触或重叠,而不仅仅是边缘相交吗?我尝试使用usesPreciseCollisionDetection,但无济于事。

添加一个alpha为0的子节点,将其作为逻辑检查的一部分,这样你就看不到它,但它会一直发生碰撞...在didBeginContact中,你应该尝试使用switch语句,可以告诉你哪些对象正在发生碰撞,而不是对所有触摸进行计数。 - Juan Boero
3个回答

7
CGPoint locObj1 = [sprite1 locationInNode:self];
CGPoint locObj2 = [sprite2 locationInNode:self];

if([sprite1 containsPoint: locObj2]) return;
if([sprite2 containsPoint: locObj1]) return;

在didBeginContact和didEndContact的开头添加以下代码。这将检查一个节点是否包含另一个节点。如果是,则不执行任何操作,这将解决didBeginContact和didEndContact被不必要地调用的问题。由于我不在我的Mac上,你可能需要稍微调整一下语法。希望这能让你朝着正确的方向前进。


1
谢谢您的想法!从文档中看来,containsPoint“返回一个布尔值,指示一个点是否位于节点的边界框内。”我担心这可能对我的用例不够准确(即仅测试潜在凹边缘体的边界框)。然而,我找不到比containsPoint更严格的替代方法。 - flatpickles
你的节点中是否必须有一个基于边缘的物理体? - meisenman
是的 - 不幸的是,看起来基于体积的物体需要定义为凸多边形,而我正在处理的形状有时可能是凹的。 - flatpickles
2
你面临的问题非常有趣。我会继续寻找解决方案,因为我不相信SpriteKit应该/会受到这样的限制。我认为SpriteKit社区应该对这篇文章更感兴趣,可能会有人提供一个定制的containsPoint方法来满足您的凸形需求。 - meisenman
1
我一直在进行实验,我认为文档是错误的!containsPoint似乎可以用于凸多边形(至少在我的环境中,并且使用iOS 8),但还有一个更复杂的问题需要考虑。我会点赞这个问题,然后为任何遇到这个问题的人发布一个更完整的答案。 - flatpickles

4
正如meisenman 所建议的,似乎最好的方法是使用containsPoint方法来确定节点重叠。文档说明这个方法“返回一个布尔值,指示一个点是否在节点的边界框内”,但在我的实验中,它看起来也适用于凹边形状。
因此,如果我想检测两个对象何时不再重叠,那么在didEndContact内进行containsPoint检查是有意义的——但事实证明,即使同一形状的另一个边仍与相同的对象相交,当每个边不再相交时,didEndContact也会被调用。例如,球从矩形通过其角落退出将产生两个didEndContact事件。因此,需要保持接触事件的绝对计数(开始和结束之间的差异),只有当这个计数为零时才测试包含性。
我的解决方案,在Swift中:
var _contactCount = 0

func didBeginContact(contact: SKPhysicsContact!) {
  if ((contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask) == (CATEGORY_ONE | CATEGORY_TWO)) {
    if (_contactCount == 0) {
      // Contact is actually beginning
    }
    _contactCount += 1
  }
}

func didEndContact(contact: SKPhysicsContact!) {
  if ((contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask) == (CATEGORY_ONE | CATEGORY_TWO)) {
    _contactCount -= 1
    let overlap = contact.bodyA.node.containsPoint(contact.bodyB.node.position) || contact.bodyB.node.containsPoint(contact.bodyA.node.position)
    if (!overlap && _contactCount == 0) {
      // Contact is actually ending
    }
  }
}

干得好!很高兴你找到了完整的答案! - meisenman

0
这是我对Swift 3中#meisenman示例的想法。如果我们不想通过遮罩来进行碰撞检测,而是想知道一个节点是否在另一个节点内部,那么可以使用#meisenman中提到的每个节点的位置。

Image Example of CGPoint's of two different nodes.

下面这张图片就是我想要完成的内容。

Image example of two CGPoint's within one another.

#meisenman使用的代码不需要SKPhysics。这是Swift 3.0中的代码:

let Object_1: CGPoint! = Sprite_Object_1.position
//this obtains the location of the sprite node, through CGPoint.
if Object_Sprite_2.contains(Object_1){
    print("Then it is true, object 2 is within the location of object 1")
}

我发现唯一的限制是,如果这些节点的中心不在所需节点的主体内,则此if语句将不成立。这还没有通过设置节点的主体定义(掩码)进行测试。

我喜欢的是,能够在不同的函数或覆盖函数中使用此if语句,这不仅限于“did begin”或“touches moved”。
两个CGPoint相互包含的图像示例,可能存在限制。


请注意 - 对象3展示了这种替代方法的局限性示例。 - dnaatwork.com

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接