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

时间:2023-01-22 21:21:02

Im building some game with SpriteKit that include balls as SKShapeNode. I create a class that define the balls and their properties (including SKPhysicsBody). the balls should run on the screen, and the frame is the screen border (by using edgeLoopFrom: self.frame). I also created a path node that is located at the top of the screen. now, I want to do that if some ball reach the top border of the frame so some function will execute. I read some about it and i'm not sure what is the right way to do so, if by using contactBitMask or if there is another and better option. If the right way is by contactBitMask - do I have to set a struct for the balls node or can I set it inside their class? thanks!

我用SpriteKit开发了一些游戏,其中包括SKShapeNode游戏。我创建一个类来定义球及其属性(包括SKPhysicsBody)。球应该在屏幕上运行,帧是屏幕边框(通过使用edgeLoopFrom: self.frame)。我还创建了一个位于屏幕顶部的path节点。现在,我想做的是,如果某个球到达框架的顶边,那么某个函数就会执行。我读了一些关于它的文章,我不确定正确的方法是什么,如果使用contactBitMask,或者有其他更好的方法。如果正确的方法是通过contactBitMask——我是否必须为balls节点设置结构体,还是可以在它们的类中设置结构体?谢谢!

1 个解决方案

#1


1  

If I'm getting this right, when a ball hits the path node that is located at the top half of the screen you want a function to be called.

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

First, I'm not sure if a path node is more efficient than a sprite node, in fact I have never really used path nodes, but here is what you can do.

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

Spritekit

Spritekit

check out the link above. What you need to do is implement the SKPhysicsContactDelegate. This will allow you to access the functions didBegin() and didEnd(). These functions are called when a contact is made within the physicsworld.

看看上面的链接。您需要做的是实现SKPhysicsContactDelegate。这将允许您访问didBegin()和didEnd()函数。这些功能在物理世界中联系时被调用。

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

    func didEnd(_ contact: SKPhysicsContact) {
    }
}

In order for these functions to be called, you need to set the physicsworld's contactDelegate to the class that will handle the calls. This would be your scene and a good place to set this is the didMove() function.

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

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

Now when a contact happens that is detectable, your didBegin() will be called, and when the contact ends the didEnd() will be called.

现在当一个联系人发生时,你的didBegin()将被调用,当联系人结束时,didEnd()将被调用。

Now we need to give our nodes some physicsbodies and we can set the different bitmasks on them in order to detect collisions/contacts. These are the 3 bitmasks we are concerned about:

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

categoryTestBitMask
collisionTestBitMask
contactTestBitMask

categoryTestBitMask: You can give nodes of similar type a category, example "ball" could be the category. All your different ball objects could have this same category. I use the "noCollision" category for when I want to detect a contact, but I don't want a collision to happen. You are limited to 32 different categories though so don't go crazy with tons of different ones.

你可以给类似类型的节点一个类别,例如“球”可以是类别。所有不同的球都有相同的类别。当我想检测一个联系人时,我使用“noCollision”类别,但是我不希望碰撞发生。但是你只能有32个不同的类别,所以不要对大量不同的类别疯狂。

collisionTestBitMask: Give your "ball" a category that you want a collision to happen with. Ex: set the collisiontestmask for your "ball" to the category bitmask of "wall". A collision is when 2 objects will physically run into each other; so your ball will bounce off the walls.

collisionTestBitMask:给你的“球”一个你想要发生碰撞的类别。将“球”的碰撞测试掩码设置为“墙”的位掩码。碰撞是指两个物体在物理上相遇;所以你的球会从墙上弹回来。

contactTestBitMask: a Contact is when 2 nodes overlap. So instead of the ball bouncing off something, it would call the contact method for our delegate. Note that you can set both the collision and contact bit masks to the same thing.

contactTestBitMask:当两个节点重叠时,一个联系人。因此,它将调用委托的contact方法,而不是弹回来的球。注意,您可以将碰撞和接触位掩码设置为相同的内容。

Now how do we set these masks. I use a Struct so that I can assign names to the bitmasks and set these 3 different masks with code. Something like this:

现在我们如何设置这些遮罩。我使用一个Struct,这样我就可以为位掩码分配名称,并用代码设置这三个不同的掩码。是这样的:

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

now within code you can set the masks:

现在在代码中你可以设置遮罩:

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

Lets look at what we are saying here. We create a ball object and we set its category to "ball", we say we want it to have collisions with "wall" objects and we want our contact delegate functions to trigger with "pathNode" objects OR "wall" objects. Our pathNode object will have no collisions, and will have contacts with the ball.

让我们来看看我们在说什么。我们创建一个球对象,并将它的类别设置为“ball”,我们说我们希望它与“wall”对象发生碰撞,我们希望我们的contact delegate函数与“pathNode”对象或“wall”对象触发。我们的路径节点对象将不会发生碰撞,并且会与球有接触。

Basically the ball will bounce off the walls, and will pass through the pathNode. It will call the contact delegate functions didbegin() and didend() with both the pathNode and wall objects.

基本上,球会从壁上弹回来,然后通过路径节点。它将同时调用pathNode和wall对象的contact delegate委托函数didbegin()和didend()。

Not finished yet... So when the function is called, how do we handle this? When the didbegin or didend function is called, it has a parameter of "contact". this contact param has 2 bodies to work with and these are the bodies that contacted each other. There are multiple ways we can handle this, but I'll just show you a simple way for now.

未完成……当函数被调用时,我们该如何处理呢?当didbegin或didend函数被调用时,它有一个“contact”参数。这个接触param有两个身体要处理这些是相互联系的身体。我们可以有多种方法来处理这个问题,但是现在我只给你们展示一种简单的方法。

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
        }

    }

}

Update: What we are doing here is figuring out the type of bodyA and bodyB. So it starts with bodyA, if bodyA is a "ball", then we know that bodyA is a "ball and bodyB is the thing the ball came in contact with. We then use a switch statement to figure out what bodyB is. Once we know what bodyB is, we call the function that we need to call for that specific contact between these 2 nodes.

更新:我们在这里做的是找出a和b的类型。它从物体a开始,如果物体a是一个球,那么我们知道物体a是一个球,物体b是球接触到的东西。然后我们使用一个switch语句来计算bodyB是什么。一旦我们知道了bodyB是什么,我们就会调用这个函数来调用这两个节点之间的特定联系。

Then you just put your code into those specified functions of what you want to do.

然后你只需把你的代码放入你想要做的那些指定的函数中。

This could be a lot to take in at once, If you are new I would suggest trying this out and trying to get it to work. After, I would youTube some videos on how to do this. It is a good idea to see how different people handle the same thing and then you can decide for your self on how to do it. This might not be the most elegant way of handling the contacts, but it works well and with some practice it will become second nature, Good luck!

如果你是新手,我建议你尝试一下,试着让它发挥作用。之后,我会在youTube上看一些关于如何做到这一点的视频。这是一个好主意,看看不同的人如何处理相同的事情,然后你可以自己决定如何去做。这可能不是处理联系人的最优雅的方式,但它工作得很好,通过一些实践,它将成为第二天性,祝您好运!

#1


1  

If I'm getting this right, when a ball hits the path node that is located at the top half of the screen you want a function to be called.

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

First, I'm not sure if a path node is more efficient than a sprite node, in fact I have never really used path nodes, but here is what you can do.

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

Spritekit

Spritekit

check out the link above. What you need to do is implement the SKPhysicsContactDelegate. This will allow you to access the functions didBegin() and didEnd(). These functions are called when a contact is made within the physicsworld.

看看上面的链接。您需要做的是实现SKPhysicsContactDelegate。这将允许您访问didBegin()和didEnd()函数。这些功能在物理世界中联系时被调用。

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

    func didEnd(_ contact: SKPhysicsContact) {
    }
}

In order for these functions to be called, you need to set the physicsworld's contactDelegate to the class that will handle the calls. This would be your scene and a good place to set this is the didMove() function.

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

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

Now when a contact happens that is detectable, your didBegin() will be called, and when the contact ends the didEnd() will be called.

现在当一个联系人发生时,你的didBegin()将被调用,当联系人结束时,didEnd()将被调用。

Now we need to give our nodes some physicsbodies and we can set the different bitmasks on them in order to detect collisions/contacts. These are the 3 bitmasks we are concerned about:

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

categoryTestBitMask
collisionTestBitMask
contactTestBitMask

categoryTestBitMask: You can give nodes of similar type a category, example "ball" could be the category. All your different ball objects could have this same category. I use the "noCollision" category for when I want to detect a contact, but I don't want a collision to happen. You are limited to 32 different categories though so don't go crazy with tons of different ones.

你可以给类似类型的节点一个类别,例如“球”可以是类别。所有不同的球都有相同的类别。当我想检测一个联系人时,我使用“noCollision”类别,但是我不希望碰撞发生。但是你只能有32个不同的类别,所以不要对大量不同的类别疯狂。

collisionTestBitMask: Give your "ball" a category that you want a collision to happen with. Ex: set the collisiontestmask for your "ball" to the category bitmask of "wall". A collision is when 2 objects will physically run into each other; so your ball will bounce off the walls.

collisionTestBitMask:给你的“球”一个你想要发生碰撞的类别。将“球”的碰撞测试掩码设置为“墙”的位掩码。碰撞是指两个物体在物理上相遇;所以你的球会从墙上弹回来。

contactTestBitMask: a Contact is when 2 nodes overlap. So instead of the ball bouncing off something, it would call the contact method for our delegate. Note that you can set both the collision and contact bit masks to the same thing.

contactTestBitMask:当两个节点重叠时,一个联系人。因此,它将调用委托的contact方法,而不是弹回来的球。注意,您可以将碰撞和接触位掩码设置为相同的内容。

Now how do we set these masks. I use a Struct so that I can assign names to the bitmasks and set these 3 different masks with code. Something like this:

现在我们如何设置这些遮罩。我使用一个Struct,这样我就可以为位掩码分配名称,并用代码设置这三个不同的掩码。是这样的:

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

now within code you can set the masks:

现在在代码中你可以设置遮罩:

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

Lets look at what we are saying here. We create a ball object and we set its category to "ball", we say we want it to have collisions with "wall" objects and we want our contact delegate functions to trigger with "pathNode" objects OR "wall" objects. Our pathNode object will have no collisions, and will have contacts with the ball.

让我们来看看我们在说什么。我们创建一个球对象,并将它的类别设置为“ball”,我们说我们希望它与“wall”对象发生碰撞,我们希望我们的contact delegate函数与“pathNode”对象或“wall”对象触发。我们的路径节点对象将不会发生碰撞,并且会与球有接触。

Basically the ball will bounce off the walls, and will pass through the pathNode. It will call the contact delegate functions didbegin() and didend() with both the pathNode and wall objects.

基本上,球会从壁上弹回来,然后通过路径节点。它将同时调用pathNode和wall对象的contact delegate委托函数didbegin()和didend()。

Not finished yet... So when the function is called, how do we handle this? When the didbegin or didend function is called, it has a parameter of "contact". this contact param has 2 bodies to work with and these are the bodies that contacted each other. There are multiple ways we can handle this, but I'll just show you a simple way for now.

未完成……当函数被调用时,我们该如何处理呢?当didbegin或didend函数被调用时,它有一个“contact”参数。这个接触param有两个身体要处理这些是相互联系的身体。我们可以有多种方法来处理这个问题,但是现在我只给你们展示一种简单的方法。

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
        }

    }

}

Update: What we are doing here is figuring out the type of bodyA and bodyB. So it starts with bodyA, if bodyA is a "ball", then we know that bodyA is a "ball and bodyB is the thing the ball came in contact with. We then use a switch statement to figure out what bodyB is. Once we know what bodyB is, we call the function that we need to call for that specific contact between these 2 nodes.

更新:我们在这里做的是找出a和b的类型。它从物体a开始,如果物体a是一个球,那么我们知道物体a是一个球,物体b是球接触到的东西。然后我们使用一个switch语句来计算bodyB是什么。一旦我们知道了bodyB是什么,我们就会调用这个函数来调用这两个节点之间的特定联系。

Then you just put your code into those specified functions of what you want to do.

然后你只需把你的代码放入你想要做的那些指定的函数中。

This could be a lot to take in at once, If you are new I would suggest trying this out and trying to get it to work. After, I would youTube some videos on how to do this. It is a good idea to see how different people handle the same thing and then you can decide for your self on how to do it. This might not be the most elegant way of handling the contacts, but it works well and with some practice it will become second nature, Good luck!

如果你是新手,我建议你尝试一下,试着让它发挥作用。之后,我会在youTube上看一些关于如何做到这一点的视频。这是一个好主意,看看不同的人如何处理相同的事情,然后你可以自己决定如何去做。这可能不是处理联系人的最优雅的方式,但它工作得很好,通过一些实践,它将成为第二天性,祝您好运!