当前位置: 首页 > 面试题库 >

Swift / SpriteKit多碰撞检测?

佴波鸿
2023-03-14
问题内容

你好。

我有多个碰撞问题。有一颗子弹击中了敌人(红色矩形)。然后,它+分数。有一个螺旋线(红色圆圈)
被推测为当敌人(红色矩形)触摸场景时触发场景结束。

在这种情况下,当敌人击中螺旋桨时,螺旋桨将起作用,场景结束,然后我们进入菜单屏幕。但是,当子弹击中敌人时,同样的事情也会发生,我也不知道为什么。

现在,这是我的代码:

struct PhysicsCategory {
    static let None : UInt32 = 0
    static let All : UInt32 = UInt32.max
    static let enemyOne : UInt32 = 0b1
    static let enemyTwo : UInt32 = 0b1
    static let bullet : UInt32 = 0b10
    static let spiral : UInt32 = 0b111
}

 spiral.physicsBody = SKPhysicsBody(rectangleOfSize: spiral.size)
        spiral.physicsBody?.categoryBitMask = PhysicsCategory.spiral
        spiral.physicsBody?.contactTestBitMask = PhysicsCategory.enemyOne
        spiral.physicsBody?.collisionBitMask = PhysicsCategory.None
...
        enemyOne.physicsBody = SKPhysicsBody(rectangleOfSize: enemyOne.size)
        enemyOne.physicsBody?.dynamic = true
        enemyOne.physicsBody?.categoryBitMask = PhysicsCategory.enemyOne
        enemyOne.physicsBody?.contactTestBitMask = PhysicsCategory.bullet | PhysicsCategory.spiral
        enemyOne.physicsBody?.collisionBitMask = PhysicsCategory.None

...

        bullet.physicsBody = SKPhysicsBody(circleOfRadius: bullet.size.width / 2)
        bullet.physicsBody?.dynamic = true
        bullet.physicsBody?.categoryBitMask = PhysicsCategory.bullet
        bullet.physicsBody?.contactTestBitMask = PhysicsCategory.enemyOne
        bullet.physicsBody?.collisionBitMask = PhysicsCategory.None
        bullet.physicsBody?.usesPreciseCollisionDetection = true

...

    func bulletDidCollideWithEnemy(bullet: SKSpriteNode, enemyOne: SKSpriteNode) {

        scoreOnScreen.text = String(score)
        score++
        bullet.removeFromParent()
        enemyOne.removeFromParent()
    }

    func enemyDidCollideWithSpiral(enemyOne: SKSpriteNode, spiral: SKSpriteNode) {

        let transition = SKTransition.revealWithDirection(SKTransitionDirection.Down, duration: 1.0)
        let skView = self.view! as SKView
        let scene = MenuScene(size: skView.bounds.size)
        scene.scaleMode = SKSceneScaleMode.AspectFill

        skView.presentScene(scene, transition: SKTransition.crossFadeWithDuration(0.5))
    }

 // Did Begin Contact
    func didBeginContact(contact: SKPhysicsContact) {
        var firstBody : SKPhysicsBody
        var secondBody : SKPhysicsBody
        var thirdBody : SKPhysicsBody
        var fourthBody : SKPhysicsBody

        if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
            firstBody = contact.bodyA
            secondBody = contact.bodyB
        } else {
            firstBody = contact.bodyB
            secondBody = contact.bodyA
        }

        if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
            thirdBody = contact.bodyA
            fourthBody = contact.bodyB
        } else {
            thirdBody = contact.bodyB
            fourthBody = contact.bodyA
        }



        if (firstBody.categoryBitMask & PhysicsCategory.enemyOne != 0) && (secondBody.categoryBitMask & PhysicsCategory.bullet != 0) {
            bulletDidCollideWithEnemy(firstBody.node as SKSpriteNode, enemyOne : secondBody.node as SKSpriteNode)
        }

        if (thirdBody.categoryBitMask & PhysicsCategory.enemyOne != 0) && (fourthBody.categoryBitMask & PhysicsCategory.spiral != 0) {
            enemyDidCollideWithSpiral(thirdBody.node as SKSpriteNode, spiral : fourthBody.node as SKSpriteNode)
        }

现在,我知道那是一团糟,但是有人可以帮助我吗?我认为问题与bodyA.categoryBitMask和bodyB设置为不同的东西有关,甚至认为它们是相同的(?)。我不知道。任何人?


问题答案:

这里有几个问题。

您在定义类别时要避免对其进行轻松测试。
您正在以无法获得所需唯一答案的方式测试类别。
您已经通过尝试在一个联系人中跟踪多达四个主体来混淆了代码。任何接触都将始终只有两个实体。
让我们一次解决一个问题…

1.定义类别
您想要定义碰撞类别,以便游戏中的每种身体都在
遮罩中使用自己的位。(使用Swift的二进制
文字表示法是个好主意,但是您要定义重叠的类别。)以下
是非重叠类别的示例:

struct PhysicsCategory: OptionSet {
    let rawValue: UInt32
    init(rawValue: UInt32) { self.rawValue = rawValue }

    static let enemy  = PhysicsCategory(rawValue: 0b001)
    static let bullet = PhysicsCategory(rawValue: 0b010)
    static let spiral = PhysicsCategory(rawValue: 0b100)
}

我OptionSet为此使用Swift 类型,因为它使创建
和测试唯一值的组合变得容易。
与an相比,它确实使定义我的类型及其成员的语法有点笨拙enum,但这
也意味着我以后不必做很多装箱和拆箱原始值的工作,
特别是如果我还像这样使便捷访问器:

extension SKPhysicsBody {
    var category: PhysicsCategory {
        get {
            return PhysicsCategory(rawValue: self.categoryBitMask)
        }
        set(newValue) {
            self.categoryBitMask = newValue.rawValue
        }
    }
}

另外,我在代码中使用了二进制文字表示法,额外的空格和零,
以便轻松地确保每个类别都有自己的位-
enemy仅获得最低有效位,bullet下一个等等。

2&3.测试和跟踪类别
我喜欢使用两层方法来联系处理程序。首先,我要检查
碰撞的类型-是子弹/敌人碰撞还是子弹/螺旋
碰撞或螺旋/敌人碰撞?然后,如有必要,我检查
一下碰撞中的哪个身体。就
计算而言,这并不会花费太多,而且在我的代码的每一点上都非常清楚正在发生什么

func didBegin(_ contact: SKPhysicsContact) {
    // Step 1. To find out what kind of contact we have,
    // construct a value representing the union of the bodies' categories
    // (same as the bitwise OR of the raw values)
    let contactCategory: PhysicsCategory = [contact.bodyA.category, contact.bodyB.category]

    if contactCategory.contains([.enemy, .bullet]) {
        // Step 2: We know it's an enemy/bullet contact, so there are only
        // two possible arrangements for which body is which:
        if contact.bodyA.category == .enemy {
            self.handleContact(enemy: contact.bodyA.node!, bullet: contact.bodyB.node!)
        } else {
            self.handleContact(enemy: contact.bodyB.node!, bullet: contact.bodyA.node!)
        }
    } else if contactCategory.contains([.enemy, .spiral]) {
        // Here we don't care which body is which, so no need to disambiguate.
        self.gameOver()

    } else if contactCategory.contains([.bullet, .spiral]) {
        print("bullet + spiral contact")
        // If we don't care about this, we don't necessarily
        // need to handle it gere. Can either omit this case,
        // or set up contactTestBitMask so that we
        // don't even get called for it.

    } else {
        // The compiler doesn't know about which possible
        // contactCategory values we consider valid, so
        // we need a default case to avoid compile error.
        // Use this as a debugging aid:
        preconditionFailure("Unexpected collision type: \(contactCategory)")
    }
}

Extra Credit

为什么要使用if语句和OptionSet类型的contains()方法?为什么不这样做switch,使测试值的语法短很多呢?

switch contactCategory {
    case [.enemy, .bullet]:
        // ...
    case [.enemy, .spiral]:
        // ...

    // ...

    default:
        // ...
}

使用switch此处的问题在于它会测试您的OptionSets是否相等 -也就是说,案例1会在if触发contactCategory == [.enemy, .bullet],而在if则不会触发[.enemy, .bullet, .somethingElse]。

对于在此示例中定义的联系人类别,这不是问题。但是类别/接触式位掩码系统的一个不错的功能之一
是您可以在单个项目上编码多个类别。例如:

struct PhysicsCategory: OptionSet {
    // (don't forget rawValue and init)
    static let ship   = PhysicsCategory(rawValue: 0b0001)
    static let bullet = PhysicsCategory(rawValue: 0b0010)
    static let spiral = PhysicsCategory(rawValue: 0b0100)
    static let enemy  = PhysicsCategory(rawValue: 0b1000)
}

friendlyShip.physicsBody!.category = [.ship]
enemyShip.physicsBody!.category = [.ship, .enemy]
friendlyBullet.physicsBody!.category = [.bullet]
enemyBullet.physicsBody!.category = [.bullet, .enemy]

在这种情况下,您可能有一个类别为[.ship,.bullet,.enemy]—的联系人,并且如果您的联系人处理逻辑专门针对进行测试[.ship, .bullet],您会错过它。如果contains改为使用,则可以
测试您关心的特定标志,而无需关心是否存在其他标志。



 类似资料:
  • 问题内容: 我的场景中有3个SKSpriteNodes。现场有 一只鸟 ,一枚 硬币 和一个 边界 。我不想 硬币 和 小鸟 互相碰撞,而是与 边界 碰撞。我为每个节点分配了一个不同的collisionBitMask和categoryBitMask: 像这样: 但是 硬币 和 鸟 仍然相互碰撞。我究竟做错了什么? 问题答案: 位掩码为32位。像您一样声明它们对应于: 您要做的是将边界值设置为4。为

  • 我有4个物理体,它们都很好地检测到碰撞。然而,有两个物理体不会检测到它们何时相互碰撞。不过,它们会检测到它们何时与其他物理体碰撞。我有所有这些物理体的联系人测试位掩码,所以我不明白为什么会有问题。以下是一些代码:这是我设置物理体的地方: 以下是我用于设置玩家物理体(其中一个问题物理体)的代码: 以下是检测碰撞的函数: 下面是我用来设置蓝球的代码。这是另一个有问题的物理体: 这里的任何想法都会有所帮

  • 碰撞检测 现在你知道了如何制造种类繁多的图形对象,但是你能用他们做什么?一个有趣的事情是利用它制作一个简单的 碰撞检测系统 。你可以用一个叫做:hitTestRectangle 的自定义的函数来检测两个矩形精灵是否接触。 hitTestRectangle(spriteOne, spriteTwo) 如果它们重叠, hitTestRectangle 会返回 true。你可以用 hitTestRect

  • 本节暂未进行完全的重写,错误可能会很多。如果可能的话,请对照原文进行阅读。如果有报告本节的错误,将会延迟至重写之后进行处理。 当试图判断两个物体之间是否有碰撞发生时,我们通常不使用物体本身的数据,因为这些物体常常会很复杂,这将导致碰撞检测变得很复杂。正因这一点,使用重叠在物体上的更简单的外形(通常有较简单明确的数学定义)来进行碰撞检测成为常用的方法。我们基于这些简单的外形来检测碰撞,这样代码会变得

  • 我正在使用SpriteKit在XCode中制作一个游戏。游戏中有一个玩家,他必须避免不同类型的投射物。当玩家与投射物相撞时,分数发生变化,投射物消失。然而,当两个弹丸碰撞时,它们会反弹。我想让两个炮弹每次相撞时,它们表现得像什么都没发生过一样,并保持原来的轨迹。我该怎么办? *注意:这并不是整个代码,重要的是它。

  • 我正在尝试做一个平台游戏,其中没有斜坡。我正在尝试将碰撞检测降下来,但是我在pygame中找不到一种方法来获得哪一边与另一个Sprite发生了碰撞。有没有人能给我一个好的方法来做到这一点,那不是太庞大,但也能很好地工作在我的情况? 下面是我的玩家类: 我已经将它添加到我的player类中,每次播放器更新时我都运行它,并且它工作...差一点。 在平台顶部的碰撞起作用,在侧面的碰撞几乎总是起作用,除非