当与SpriteKit一次与两个节点发生碰撞时,我该如何选择要与哪个节点接触



我有一个在Swift 3中编码的iOS应用程序,在该应用程序中,球被射击并从屏幕上弹起。如果我的砖是一个物理机构(矩形),我将无法轻易确定砖的哪一侧/角都被击中。我决定做的不是这样做的是将砖的每一侧都是其自己的单独节点。我现在遇到的问题是,一个球不能一次与两个节点(例如左和底部)接触。与球的每次接触后,我正在降低砖的值,而这一命中又将值降低了2。我该怎么做,以便如果一个球命中两个节点,则只能执行一个联系人?

有时以下代码被执行两次,球两次都与两个砖块接触。

func didBegin(_ contact: SKPhysicsContact) {
    var firstBody:SKPhysicsBody
    var secondBody:SKPhysicsBody
    let countPoint = true
    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
        firstBody = contact.bodyA
        secondBody = contact.bodyB
    } else {
        firstBody = contact.bodyB
        secondBody = contact.bodyA
    }
    if (firstBody.categoryBitMask & ballCategory) != 0 {
        if (firstBody.node != nil && secondBody.node != nil){
            if (secondBody.categoryBitMask & brickCategory) != 0  {
                ballDidHitBrick(ballNode: firstBody.node as! SKShapeNode, brickNode: secondBody.node as! SKShapeNode, decreasePoint: countPoint)
            } else if (secondBody.categoryBitMask & roofCategory) != 0 || (secondBody.categoryBitMask & rightWallCategory) != 0 || (secondBody.categoryBitMask & leftWallCategory) != 0 || (secondBody.categoryBitMask & bottomCategory) != 0 {
                ballDidHitWall(ballNode: firstBody.node as! SKShapeNode, wallNode: secondBody.node as! SKShapeNode)
            } else {
                //Nothing as of yet
            }
        }
    }
}

因此,与史蒂夫(Steve)所说的那样,我实现了下面的代码,并且每个更新都不再具有双重联系人:

if !bricksHit.contains("(secondBody.node?.name ?? ""), (firstBody.node?.name ?? "")") {
    //If ball hasnt hit the object more than once
    bricksHit.append("(secondBody.node?.name ?? ""), (firstBody.node?.name ?? "")")
    ballDidHitBrick(ballNode: firstBody.node as! SKShapeNode, brickNode: secondBody.node as! SKShapeNode, decreasePoint: countPoint, contact: contact)
}

我还将下面的代码添加到我的代码中,每次更新后都清除了Birckshit:

override func didFinishUpdate() {
    bricksHit.removeAll()
}

我会用多个身体刮下多个节点,如果您有很多块,这将产生可怕的性能。

相反,您应该按步骤处理工作。

didBegin阶段,您需要跟踪联系点在哪里。这可以用userData

完成
func didBegin(_ contact: SKPhysicsContact) {
    var firstBody:SKPhysicsBody
    var secondBody:SKPhysicsBody
    let countPoint = true
    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
        firstBody = contact.bodyA
        secondBody = contact.bodyB
    } else {
        firstBody = contact.bodyB
        secondBody = contact.bodyA
    }
    if (firstBody.categoryBitMask & ballCategory) != 0,  (secondBody.categoryBitMask & brickCategory) != 0  {
        let userData = firstBody.node!.userData ??  [String,AnyObject]()
        let contactPoints = userData["contactPoints"] as? [CGPoint] ?? [CGPoint]()
        contactPoints.append(contact.contactPoint) //if need be add other info like the vector or relative to the node
       userData["contactPoints"] = contactPoints
    }
}

然后在稍后的过程中,就像didSimulatePhysics一样,您可以评估已接触的节点,确定接触的优先级(例如底部将取代侧面,或者速度x>速度y,侧面,无论您需要什么做)并以这种方式做出反应。

请注意,这只是示例代码,它将逐字化。您需要将其构造到您的代码中才能使其正常工作。

是的 - 这发生了。意见共识似乎是,如果2个物理体之间有多个同时的联系点,SK将为每个接触点致电didBegin,从而导致相同物理体的多个呼叫(在SAM游戏循环中)。p>处理它的方法(在某些情况下,您无法多次拨打sprite-kit,请勿多次致电didbegin)是为了确保您的联系代码适应此情况,并且多次处理合同不会引起问题(例如多次增加分数,删除多个生命,试图访问已删除的节点或物理机构等)。

您可以做的一些事情包括:

  • 如果您删除了与已联系的节点,请检查它是零之前的您将其删除(用于重复的联系人)
  • 将节点添加到集合中,然后删除didFinishUpdate

  • 中的集合中的所有节点
  • 向节点的UserData

  • 添加一个"非活动"标志'
  • 使节点成为SKSpriteNode的子类,并添加"非活动"属性(或类似)

  • 等等。

请参阅此问题,并回答有关SK多次致电didBegin以获取单个联系人:

Sprite-Kit注册了单个联系人的多个碰撞

SkphysicsContact不仅包含碰撞的两个物理体的细节,还包含接触点。由此,以及涉及的两个节点的位置属性,您确实可以计算砖的哪一侧/角。

最新更新