当SpriteKit一次与两个节点发生冲突时,如何选择我想要联系的节点?

时间:2017-09-19 14:51:04

标签: swift sprite-kit

我有一个在Swift 3中编码的iOS应用程序,其中一个球被射击并在屏幕上弹出砖块。如果我将砖块作为一个PhysicsBody(一个矩形),我就不能轻易确定砖块的哪个侧面/角落被击中。我决定做的不是这个,而是让砖块的每一面都是它自己独立的节点。我现在面临的问题是球不能同时与两个节点(比如左边和底部)接触。我每次接触球后都会降低砖的值,这反过来又会使这一击中的值减少2。我怎样才能做到这样,如果一个球击中两个节点,只执行一个联系人的代码?

有时下面的代码会执行两次,两次都会与两个brickNodes接触。

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
            }
        }
    }
}

3 个答案:

答案 0 :(得分:1)

顺便说一下史蒂夫上面所说的,我实现了下面的代码,我不再每次更新都有双联系人:

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()
}

答案 1 :(得分:0)

我会废弃多个体的多个节点,如果你有很多块,那将会产生糟糕的性能。

相反,您应该分步处理您的工作。

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 :(得分:-1)

是的 - 这发生了。意见的共识似乎是,如果两个物理实体之间存在多个同时接触点,SK将为每个接触点调用didBegin,从而导致同一对物理对的多次调用(在sam游戏循环中)体。

处理它的方法(你不能让sprite-kit在某些情况下多次调用didBegin)是为了确保你的联系代码适应这一点,多次处理合同不会导致问题(例如多次添加分数,删除多个生命,尝试访问已被删除的节点或物理等。)

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

  • 如果删除了联系的节点,请检查它是否为零 你删除它(对于重复的联系人)
  • 将节点添加到集合中,然后删除didFinishUpdate

  • 中集合中的所有节点
  • 将“非活动”标志添加到节点的userData

  • 使节点成为SKSpriteNode的子类并添加“非活动”属性(或类似)

  • Etc等。

对于单个联系人,请多次查看有关SK致电didBegin的问题和答案:

Sprite-Kit registering multiple collisions for single contact

此外,SKPhysicsContact不仅包含已碰撞的2个物理实体的细节,还包含接触点。由此,以及所涉及的2个节点的位置属性,您确实可以计算出砖块的哪个边/角被击中。