检测节点是否接触框架的最佳方法

best way to detect if node touch the frame

我正在使用 SpriteKit 构建一些包含球作为 SKShapeNode 的游戏。我创建了一个 class 来定义球及其属性(包括 SKPhysicsBody)。球应该 运行 在屏幕上,框架是屏幕边框(通过使用 edgeLoopF​​rom: self.frame)。我还创建了一个位于屏幕顶部的路径节点。现在,我想在某个球到达框架的顶部边界时执行此操作,以便执行某些功能。 我阅读了一些相关内容,但我不确定这样做的正确方法是使用 contactBitMask 还是有其他更好的选择。 如果正确的方法是通过 contactBitMask - 我是否必须为 balls 节点设置一个结构,或者我可以将它设置在它们的 class 中吗? 谢谢!

如果我做对了,当球击中位于屏幕上半部分的路径节点时,您需要调用一个函数。

首先,我不确定路径节点是否比精灵节点更有效,事实上我从来没有真正使用过路径节点,但这是你可以做的。

Spritekit

查看上面的 link。您需要做的是实现 SKPhysicsContactDelegate。这将允许您访问函数 didBegin() 和 didEnd()。当在物理世界中进行接触时,将调用这些函数。

class YourClass: SKScene, SKPhysicsContactDelegate {
    func didBegin(_ contact: SKPhysicsContact) {
    }

    func didEnd(_ contact: SKPhysicsContact) {
    }
}

为了调用这些函数,您需要将物理世界的 contactDelegate 设置为将处理调用的 class。这将是您的场景,设置它的好地方是 didMove() 函数。

class YourClass: SKScene, SKPhysicsContactDelegate {
    func didMove(to view: SKView) {
        physicsWorld.contactDelegate = self
    }

现在,当发生可检测到的接触时,将调用您的 didBegin(),当接触结束时,将调用 didEnd()。

现在我们需要给我们的节点一些物理体,我们可以在它们上设置不同的位掩码以检测 collisions/contacts。这些是我们关心的 3 个位掩码:

categoryTestBitMask
collisionTestBitMask
contactTestBitMask

categoryTestBitMask: 您可以给类似类型的节点一个类别,例如"ball" 可以是类别。您所有不同的球对象都可以具有相同的类别。当我想检测接触时,我使用 "noCollision" 类别,但我不希望发生碰撞。不过,您仅限于 32 个不同的类别,所以不要为大量不同的类别而疯狂。

collisionTestBitMask: 为您的 "ball" 指定一个您希望发生碰撞的类别。例如:将 "ball" 的碰撞测试掩码设置为 "wall" 的类别位掩码。碰撞是指两个物体 运行 相互碰撞;所以你的球会从墙上反弹。

contactTestBitMask:一个Contact是2个节点重叠的时候。因此,球不会弹开某物,而是调用我们委托的联系方法。请注意,您可以将碰撞和接触位掩码设置为相同的东西。

现在我们如何设置这些掩码。我使用 Struct 以便我可以为位掩码分配名称并使用代码设置这 3 个不同的掩码。像这样:

struct Mask {
static var ball: UInt32 = 0b10 //2
static var wall: UInt32 = 0b100 //4
static var pathNode: UInt32 = 0b1000 //8
}

现在您可以在代码中设置掩码:

let ball = SKSpriteNode()
ball.name = "ball"
ball.physicsBody = SKPhysicsBody()
ball.physicsBody.categoryTestBitMask = Mask.ball
ball.physicsBody.collisionTestBitMask = Mask.wall
ball.physicsBody.contactTestBitMask = Mask.pathNode | Mask.wall

let pathNode = SKSpriteNode()
pathNode.name = "pathNode"
pathNode.physicsBody = SKPhysicsBody()
pathNode.physicsBody.categoryTestBitMask = Mask.pathNode
pathNode.physicsBody.collisionTestBitMask = 0
pathNode.physicsBody.contactTestBitMask = Mask.pathNode

让我们看看我们在这里说了什么。我们创建一个球对象并将其类别设置为 "ball",我们说我们希望它与 "wall" 对象发生碰撞,我们希望我们的接触委托函数与 "pathNode" 对象触发 OR "wall" 个对象。我们的 pathNode 对象不会发生碰撞,并且会与球接触。

基本上球会从墙上反弹,并穿过路径节点。它将使用 pathNode 和 wall 对象调用联系人委托函数 didbegin() 和 didend()。

还没完... 那么当函数被调用时,我们如何处理呢?调用didbegin 或didend 函数时,它有一个参数"contact"。这个接触参数有 2 个主体可以使用,这些是相互接触的主体。我们有多种方法可以处理这个问题,但我现在只向您展示一种简单的方法。

func didBegin(_ contact: SKPhysicsContact) {
    if contact.bodyA!.node!.name == "ball" {
    // bodyA is our ball
        switch contact.bodyB!.node!.name {
        case "pathNode":
            thisIsMyBallHitPathNodeFunction()
        case "wall":
            thisIsMyBallHitWallFunction()
        default:
            break
        }
    }
    else if contact.bodyB!.node!.name == "ball" {
    // bodyB is our ball
        switch contact.bodyA!.node!.name {
        case "pathNode":
            thisIsMyBallHitPathNodeFunction()
        case "wall":
            thisIsMyBallHitWallFunction()
        default:
            break
        }

    }

}

更新: 我们在这里所做的是找出 bodyA 和 bodyB 的类型。所以它从 bodyA 开始,如果 bodyA 是一个 "ball",那么我们知道 bodyA 是一个“球”,而 bodyB 是球接触到的东西。然后我们使用 switch 语句找出 bodyB 是什么。一旦我们知道 bodyB 是什么,我们就调用我们需要为这两个节点之间的特定联系调用的函数。

然后你只要把你的代码放到你想做的那些指定的函数中就可以了。

一次要接受的内容可能很多,如果您是新手,我建议您尝试一下并尝试让它发挥作用。之后,我会在 youTube 上播放一些有关如何执行此操作的视频。看看不同的人如何处理同一件事是个好主意,然后您可以自己决定如何做。这可能不是处理联系人的最优雅的方式,但它运作良好并且通过一些练习它会成为第二天性,祝你好运!