omerc
omerc

Reputation: 179

best way to detect if node touch the frame

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!

Upvotes: 3

Views: 614

Answers (1)

Discoveringmypath
Discoveringmypath

Reputation: 1077

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.

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.

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.

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.

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:

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.

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.

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.

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 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.

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.

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.

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.

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!

Upvotes: 1

Related Questions