【问题标题】:When projectile hits two "monsters" the didBeginContact method crashes. I know why but i don't know how to avoid it当弹丸击中两个“怪物”时,didBeginContact 方法会崩溃。我知道为什么,但我不知道如何避免它
【发布时间】:2015-06-25 18:19:43
【问题描述】:

所以我有这个教程中的代码:

func didBeginContact(contact: SKPhysicsContact) {

    // 1
    var firstBody: SKPhysicsBody?
    var secondBody: SKPhysicsBody?
    var body: SKPhysicsBody

    //contact.bodyB.node!.physicsBody!.allContactedBodies().count


        if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
            firstBody = contact.bodyA
            secondBody = contact.bodyB
            println("1 = A, 2 = B")
        } else {
            firstBody = contact.bodyB
            secondBody = contact.bodyA
            println("2 = A, 1 = B")
        }



        // 2
        if ((firstBody!.categoryBitMask & PhysicsCategory.Monster != 0) &&
            (secondBody!.categoryBitMask & PhysicsCategory.Projectile != 0)) {

                for var c = 1; c <= contact.bodyB.node!.physicsBody!.allContactedBodies().count; c++ {
                    projectileDidCollideWithMonster(firstBody!.node as! SKSpriteNode, monster: secondBody!.node as! SKSpriteNode)

                }
                secondBody!.node?.removeFromParent()
        }
}

func projectileDidCollideWithMonster(projectile:SKSpriteNode, monster:SKSpriteNode) {
    println("Hit")
    changeScore(1)
    changeAmo(true)
    projectile.removeFromParent()
    monster.removeFromParent()
}

然后发生的事情是一个射弹有时会同时击中两个怪物。

当这种情况发生时 - didBeginContact 方法崩溃,说 secondBody 为 nil。

经过深入研究,我找到了原因:

当射弹同时与其他两个节点碰撞时 - 此方法运行两次。第一次运行后 - 如果将 bodyA 作为弹丸,将 bodyB 作为怪物 - 将它们传递给 projectileDidCollideWithMonster 并将它们都删除。然后它立即再次运行,但此时弹丸不再存在并且它崩溃无法分配它的节点。

我不知道如何克服这个问题:(有什么建议吗?

解决方案:感谢以下想法,我做了一些更改。

在类的顶部添加了数组和触发器:

    var bodiesToBeRemoved = [SKSpriteNode]()
    var shouldRemoveBodies = false

并做了这些修改:

 func projectileDidCollideWithMonster(projectile:SKSpriteNode, monster:SKSpriteNode) {
 /* Called when collisions is detected and collided nodes are passed to it */
    //score and stuff
    println("Hit")
    changeScore(1)
    changeAmo(true)

    //collect the nodes to be removed
    bodiesToBeRemoved.append(projectile)
    bodiesToBeRemoved.append(monster)

    //change the trigger to remove collected nodes later on
    shouldRemoveBodies=true

}

override func update(currentTime: CFTimeInterval) {
    /* Called before each frame is rendered */

    //check the trigger
    if shouldRemoveBodies == true {
        println("\(bodiesToBeRemoved.count)")
        //remove collected nodes
        for var i = 0; i<bodiesToBeRemoved.count; i++ {
            bodiesToBeRemoved[i].removeFromParent()
        }

        //reset array
        bodiesToBeRemoved.removeAll(keepCapacity: false)

        //reset the trigger
        shouldRemoveBodies = false
    } else {
        //do nothing:)
    }

}

【问题讨论】:

  • 我并没有真正了解 swift,但这些想法通常与语言无关。您很可能正在运行某种事件循环?如果是这样,则应在对象发生碰撞时将其标记为删除,但在处理完需要检查的所有内容之后再删除。
  • @MiltoxBeyond 谢谢!你和“talrnu”(下)给了我相同的方向,我做到了:)

标签: ios swift sprite-kit sprite collision-detection


【解决方案1】:

不要立即移除你的弹丸,只需将其标记为移除(例如,通过设置一个布尔标志,或者如果它尚未在集合中,则将其添加到某个集合中)。然后稍后,在下一次物理检查之前(例如在这一帧结束时),检查并移除所有标记为要移除的射弹。

【讨论】:

  • 是的,关于帧的结尾是个好主意。在帧结束时调用的方法是什么?有吗?
  • 谢谢!我已经根据你的建议更新了我的答案:)
【解决方案2】:

只检查它是 nil 还是不比检查每个节点的标记更好。

func removeNodeFromPhysicsBody(ps: SKPhysicsBody){
    if (ps.node != nil){
        ps.node?.removeFromParent()
    }
}

func wallDidCollideWithMeteor(wall:SKPhysicsBody, meteor:SKPhysicsBody) {
    removeNodeFromPhysicsBody(wall)
    removeNodeFromPhysicsBody(meteor)
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-01
    • 2017-08-13
    • 1970-01-01
    • 2013-09-13
    相关资源
    最近更新 更多