【问题标题】:why didBegin has no data about contact bodies?为什么 didBegin 没有关于联系人的数据?
【发布时间】:2017-01-30 13:48:22
【问题描述】:

我正在制作一个玩家(球)和敌人(球)互相撞击的游戏。有时didBegin func 保存有关两个碰撞体的信息,有时没有,这会导致崩溃并抛出exc_bad_instruction 的错误,然后我用断点检查玩家和敌人并找到它们nil。这是怎么发生的,有时它起作用,有时却不起作用。请帮忙。

我的代码:

private var player: Player!

private func drawPlayer(color: SKColor, radius: CGFloat, cn: String) {
        player = Player.factory.createPlayer(color: color, radius: radius)
        player.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
        player.name = cn
        world.addChild(player)
    }

if (contact.bodyA.categoryBitMask == bodyType.enemy.rawValue) && (contact.bodyB.categoryBitMask == bodyType.player.rawValue) {
        print(contact.bodyA.node?.name)
        print(contact.bodyB.node?.name)
        let player = contact.bodyB.node as? Player
        let hitter = contact.bodyA.node as? Enemy
}

我使用打印来获取名称,有时它给我两个名字,有时它给我两个名字 nil 。其余代码我没有放,因为这里不需要。

解决方案 ::

史蒂夫在他的回答中提到的问题是,有时didBegin 方法会在相同的碰撞对象上被调用两次。所以它崩溃了,因为在第一次调用时我删除了对象,所以它第二次找到nil。所以正如史蒂夫提到的你应该用if包围逻辑并检查两个主体是否是nil然后这是didBegin的第二次调用并且对象已经被删除所以你不应该运行逻辑否则它会什么都没有运行然后崩溃。如果它们不是nil,您可以运行代码并且它可以工作。

【问题讨论】:

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


    【解决方案1】:

    如果bodyA 是玩家而bodyB 是敌人会发生什么?我怀疑这种情况是您的“有时不是,这会导致崩溃”问题的原因。

    didBegin() 中,您是否从场景中删除任何节点(使用removeFromParent)?这可能会导致这种行为,因为 SK 有时会为单个碰撞生成多次调用 didBegin。如果您在第一次调用 didBegin 时删除了冲突中涉及的一个或两个节点,那么第二次调用它(这是在同一个游戏循环中)您的一些节点和/或它们的属性可能是 @ 987654327@。

    请记住,在SKMPhysicsContact 对象中传递给didBegin 的对象是物理体,而不是节点本身。即使您已经删除了节点本身(在之前的 didBegin 中),物理实体仍然可能存在。这意味着除非您访问 didBegin 中的实际节点,否则您可能不会崩溃,并且如果您正在进行一些简单的检查,例如如果玩家击中了硬币或敌人,那么你可能会增加两倍的分数,或者移除两点生命值等。

    有很多方法可以处理这个问题 - 包括但不限于:

    • 在对身体进行任何操作之前检查身体是否为零。 (如果它是 nil,那么假设您已经处理了它的碰撞并将其删除)。
    • 将要移除的节点添加到集合中,然后移除所有节点 在didFinishUpdate 的集合中,在所有对didBegin 的调用之后 已经做出来了。您可能需要在节点 userData 属性中设置一个标志,以阻止它被处理两次。

    加上其他一些技巧。搜索 sprite-kit 多次碰撞。

    为了处理“哪个是bodyA,哪个是bodyB”的问题,我喜欢编写didBegin:像这样:

        func didBeginContact(contact: SKPhysicsContact) {
            let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
    
            switch contactMask {
    
            case categoryBitMask.player | categoryBitMask.enemy:
               print("Collision between player and enemy")
               let enemy = contact.bodyA.categoryBitMask == categoryBitMask.thisMine ? contact.bodyA.node! : contact.bodyB.node!
               enemy.explode()
    
            default :
               //Some other contact has occurred
               print("Some other contact")
        }  
    }
    

    这只有在您的节点一次只属于一个类别时才是安全的,这由您决定。

    【讨论】:

    • 是的,我对敌人和玩家都使用了 remove parent,然后我在同一个地方再次绘制玩家,所以游戏继续,但它在前 3 或 2 次使用它,然后在稍后
    • 我怀疑你需要指出这个冲突已经被处理了。您可以在 userData 中设置一个标志,然后在 update() 中清除该标志,因为届时您将进入一个新的游戏循环,并且玩家和敌人之间的碰撞将是新的。
    • @AhmedNader - 这可能有效,然后在 update() 中将其设置为 false。然后在你的 didBegin 开始时,如果它是真的就简单地返回。
    • @AhmedNader。很高兴它正在工作。第一次调用 didBegin 时,body 不会为 nil,并且您的删除代码会运行。然后当再次为相同的碰撞调用 didbegin 时,body 为 nil 并且代码没有运行。如果您将一些打印语句放入诸如“玩家已击中敌人”和“零检查防止双重碰撞”中,您可能会看到比“零”消息更多的“碰撞”消息。 .
    • @AhmedNader - 是的 - didBegin 并不总是为同一个碰撞多次调用。这取决于 2 个物理体如何相互接触。
    猜你喜欢
    • 1970-01-01
    • 2012-10-14
    • 1970-01-01
    • 2011-07-13
    • 2015-01-14
    • 2018-06-24
    • 1970-01-01
    • 1970-01-01
    • 2011-07-20
    相关资源
    最近更新 更多