swift didBeginContact 错误,如果 运行 较新 iPhone 在模拟器中

swift error in didBeginContact if running newer iPhone in simulator

这是"didBeginContact"方法中的代码:

        if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask
    {

        if contact.bodyA.node!.name == "0" && contact.bodyB.node!.name == "1000003"{

            contact.bodyB.node!.removeFromParent()
        }
        if contact.bodyA.node!.name == "0" && contact.bodyB.node!.name == "1000004"{

            contact.bodyB.node!.removeFromParent()
        }

    }

    else

    {
        if contact.bodyB.node!.name == "0" && contact.bodyA.node!.name == "1000003"{

            contact.bodyA.node!.removeFromParent()
        }
        if contact.bodyB.node!.name == "0" && contact.bodyA.node!.name == "1000004"{

            contact.bodyA.node!.removeFromParent()
        }
    }

它在 "iPhone 4s" 和 "iPhone 5" 模拟中工作,并在更高版本中失败并显示此错误消息:

“致命错误:在展开可选值时意外发现 nil (lldb)"

这是标记为红色的行:

if contact.bodyA.node!.name == "0" && contact.bodyB.node!.name == "1000004"{

很高兴知道: 名称为“1000004”的对象是我在 "GameScene" class 中创建的 SKSpriteNode:let TestPixel = SKSpriteNode(imageNamed: "6")

此对象的 属性 在 "touchesBegan" 方法中。 这是代码:

            TestPixel.name = "1000004"
            TestPixel.anchorPoint = CGPointMake(0.5, 0.5)
            TestPixel.userInteractionEnabled = false
            TestPixel.zPosition = 1000003
            TestPixel.size = CGSizeMake(1, 1)
            TestPixel.position = CGPointMake(positionInScene.x, positionInScene.y)
            TestPixel.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "6"), size: TestPixel.size)
            TestPixel.physicsBody?.categoryBitMask = ClickOnCategory
            TestPixel.physicsBody?.contactTestBitMask = BuildingsCategory
            TestPixel.physicsBody?.affectedByGravity = false
            TestPixel.physicsBody?.dynamic = true
            TestPixel.physicsBody?.collisionBitMask = 0
            addChild(TestPixel)

如果这个 SpriteNode(name:"1000004") 命中了我在 "didMoveToView" 方法中创建的另一个 SpriteNode,就会发生错误,代码如下:

func addBG(){

        let background = SKSpriteNode(imageNamed: "0")
        background.name = "0"
        background.anchorPoint = CGPointMake(0.5, 0.5)
        background.zPosition = 1
        background.position = CGPointMake(CGRectGetMinX(self.frame)+self.frame.width/4,CGRectGetMaxY(self.frame)-self.frame.height/4)
        background.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "0"), size: background.size)
        background.physicsBody?.categoryBitMask = BuildingsCategory
        background.physicsBody?.affectedByGravity = false
        background.physicsBody?.dynamic = true
        background.physicsBody?.collisionBitMask = 0
        self.addChild(background)

    }

    addBG()

并且在"didMoveToView"中还有:

physicsWorld.contactDelegate = self

"GameScene"class中的categoryBitMask是这样设置的:

class GameScene: SKScene, SKPhysicsContactDelegate{

let BuildingsCategory  : UInt32 = 0x1 << 1
let ClickOnCategory : UInt32 = 0x1 << 2

是的,正如我所说,它可以在模拟器中与 "iPhone 4s" && "iPhone 5" 一起使用,但在更高版本中却不行……有什么想法吗? 谢谢

是的,我可以告诉你原因:

if contact.bodyA.node!.name == "0" && contact.bodyB.node!.name == "1000003"{
    contact.bodyB.node!.removeFromParent() <----PROBLEM
}
if contact.bodyA.node!.name == "0" && contact.bodyB.node!.name == "1000004"{
    contact.bodyB.node!.removeFromParent()
}

这是怎么回事,您是在执行第一个 if 语句,该语句从父项中删除,然后您试图再次访问它。好吧,如果你从父节点中删除,是什么在内存中维护节点?可能什么都没有,所以在第一行,它消除了节点。然后你的代码继续进入另一个 if 条件,你现在正试图打开一个 nil。这是您将使用 ELSE IF 而不是 if.

的地方

我强烈建议使用 sprite kit 做的另一件事是永远不要在接触阶段移除节点。您可能会发生多个联系,并且您可能需要处理所有这些联系情况,因此最好让节点保持活动状态。

相反,每当您想要删除节点时,将它们放入队列(数组或集合),然后在您的 didFinishUpdate 方法中循环遍历队列并删除所有节点。

我做的另一个技巧是,我在 categoryBitMask 上保留第 31 位以确定精灵是活的还是死的,然后在 didContact 方法上,我对其进行了防范。

例如

func didBeginContact(contact: SKPhysicsContact!)
{
    guard let bodies = (a:contact.bodyA,b:contact.bodyB) where a.categoryBitMask < 1 << 31 && b.categoryBitMask < 1 << 31
    else
    {
        return
    }
    // do other work here
}

Seift 3.0

func didBeginContact(contact: SKPhysicsContact!)
{
    guard let bodies = (a:contact.bodyA,b:contact.bodyB, a.categoryBitMask < 1 << 31 && b.categoryBitMask < 1 << 31
    else
    {
        return
    }
    // do other work here
}