Flag when two sk nodes intersect

87 Views Asked by At

In my swift code below, a left and right button that moves around a node called stick. I would like when the user hits the button that makes stick intersect with bounds. The debug section will print "flag". I know in uikit there is an intersect command, but I don't know withe sprite kit. The teal is bounding.

enter image description here

import SpriteKit
import GameplayKit

class GameScene: SKScene , SKPhysicsContactDelegate {
    
    var stick: SKSpriteNode!
    var leftButton: SKSpriteNode!
    var rightButton: SKSpriteNode!
    var bounds: SKSpriteNode!
    
    var objectThatHandlesContactLogic: ContactDetectionLogic!
    
    override func didMove(to view: SKView) {
        physicsWorld.contactDelegate = self  // << from the previous step

          backgroundColor = SKColor.white
              
        objectThatHandlesContactLogic = ContactDetectionLogic()
           
        physicsWorld.contactDelegate = objectThatHandlesContactLogic
        
        // Create left and right buttons
        leftButton = SKSpriteNode(color: .blue, size: CGSize(width: 100, height: 50))
        leftButton.position = CGPoint(x: size.width * 0.2, y: size.height * 0.1)
        addChild(leftButton)
        
        rightButton = SKSpriteNode(color: .red, size: CGSize(width: 100, height: 50))
        rightButton.position = CGPoint(x: size.width * -0.1, y: size.height * 0.1)
        addChild(rightButton)
          // Create and position the stick
        physicsWorld.contactDelegate = self  // << from the previous step

            backgroundColor = SKColor.white
                
            // Create and position the stick
            let stickSize = CGSize(width: 160, height: 20)
            stick = SKSpriteNode(color: .brown, size: stickSize)
            stick.position = CGPoint(x: size.width * -0.2, y: size.height * -0.1)
            addChild(stick)
                
            // Add physics body to stick
            stick.physicsBody = SKPhysicsBody(rectangleOf: stickSize)
            stick.physicsBody!.categoryBitMask = 0b0000  // Means the stick doesn't belong to any category (all zeros)
            stick.physicsBody!.collisionBitMask = 0b0000  // Means the stick collides with no category, i.e., doesn't collide with anything (all zeros)
            stick.physicsBody!.contactTestBitMask = 0b0001  // Means the stick will only collide with nodes belonging to category 0001 (in binary. This also happens to mean category "1" in decimal)
            
            stick.physicsBody!.affectedByGravity = false  // since you dont want it falling off of the screen
            
            // Create and position the bounds box
            let boundsSize = CGSize(width: 150, height: 300)
            bounds = SKSpriteNode(color: .systemTeal, size: boundsSize)
            bounds.position = CGPoint(x: size.width * -0.4, y: size.height * -0.2)
            addChild(bounds)
                
            // add physics body to bounds
            bounds.physicsBody = SKPhysicsBody(rectangleOf: boundsSize)
            bounds.physicsBody!.collisionBitMask = 0b0000  // Means bounds collides with no category (all zeros)
            bounds.physicsBody!.categoryBitMask = 0b0001  // Means the bounds belongs to category 0001 (in binary. Which again, translates to "1" in decimal), which if you remember, is what we set the stick to collide with

            // the default value for bounds.physicsBody!.contactBitMask
            // is already 0b0000 so no need to set it explicitly here.
            // (You could do it for consistency sake if you'd like but it
            // wouldn't matter since you're setting it to the same value it
            // already is)
            
            bounds.physicsBody!.affectedByGravity = false  // since you dont want it falling off of the screen

            // ...blah blah blah all the other code
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch in touches {
            let location = touch.location(in: self)

            if leftButton.contains(location) {
                // Rotate the stick counterclockwise
                let rotateAction = SKAction.rotate(byAngle: CGFloat(Double.pi / 4), duration: 1.0)
                stick.run(rotateAction)
                print("hit Left")
            }

            if rightButton.contains(location) {
                // Rotate the stick clockwise
                let rotateAction = SKAction.rotate(byAngle: -CGFloat(Double.pi / 4), duration: 1.0)
                stick.run(rotateAction)
            }
        }
    }
}

class ContactDetectionLogic: NSObject, SKPhysicsContactDelegate {  // << Make sure you also add NSObject
    // if you add this function, this function will be called when a contact between two nodes begins
    func didBegin(_ contact: SKPhysicsContact) {
        print("flag")
    }
}

I don't think an intersection is an option in this case.

1

There are 1 best solutions below

8
rayaantaneja On

Step 1:

SpriteKit has a detection system that can tell you when two nodes come into contact with one another. You can add contact detection callbacks to a class by conforming that class to the SKPhysicsContactDelegate.

eg.

class ContactDetectionLogic: NSObject, SKPhysicsContactDelegate {  // << Make sure you also add NSObject
    // if you add this function, this function will be called when a contact between two nodes begins
    func didBegin(_ contact: SKPhysicsContact) {
        print("flag")
    } 
}

you could even conform your own GameScene to this protocol! (I usually do this and I think this is pretty typical to do)

class GameScene: SKScene, SKPhysicsContactDelegate {  // << Here we add the conformance
    // ...blah blah blah all the other code

    func didBegin(_ contact: SKPhysicsContact) {
        print("flag")
    }
}

Step 2:

Once you've decided which object will receive callbacks make sure you assign this object to the delegate property in your physicsWorld.

eg:

var objectThatHandlesContactLogic: ContactDetectionLogic!

override func didMove(to view: SKView) {
    objectThatHandlesContactLogic = ContactDetectionLogic()
    physicsWorld.delegate = objectThatHandlesContactLogic

    backgroundColor = SKColor.white
    // ...blah blah blah all the other code
}

or if you conformed the scene itself to the protocol (which again, is the more typical thing to do), you would do:

override func didMove(to view: SKView) {
    physicsWorld.delegate = self  // This is also much cleaner imo

    backgroundColor = SKColor.white
    // ...blah blah blah all the other code
}

Step 3:

Once you've added the delegate you have to configure which nodes coming in contact would trigger them. In your case its the stick and the bounds so you do so we add physics bodies to each of them:

override func didMove(to view: SKView) {
    physicsWorld.contactDelegate = self  // << from the previous step

    backgroundColor = SKColor.white
        
    // Create and position the stick
    let stickSize = CGSize(width: 160, height: 20)
    stick = SKSpriteNode(color: .brown, size: stickSize)               
    stick.position = CGPoint(x: size.width * -0.2, y: size.height * -0.1)
    addChild(stick)
        
    // Add physics body to stick
    stick.physicsBody = SKPhysicsBody(rectangleOf: stickSize)
    stick.physicsBody!.categoryBitMask = 0b0000  // Means the stick doesn't belong to any category (all zeros)
    stick.physicsBody!.collisionBitMask = 0b0000  // Means the stick collides with no category, i.e., doesn't collide with anything (all zeros)
    stick.physicsBody!.contactTestBitMask = 0b0001  // Means the stick will only collide with nodes belonging to category 0001 (in binary. This also happens to mean category "1" in decimal)
    
    stick.physicsBody!.affectedByGravity = false  // since you dont want it falling off of the screen 
    
    // Create and position the bounds box
    let boundsSize = CGSize(width: 150, height: 300)
    bounds = SKSpriteNode(color: .systemTeal, size: boundsSize)
    bounds.position = CGPoint(x: size.width * -0.4, y: size.height * -0.2)
    addChild(bounds)
        
    // add physics body to bounds
    bounds.physicsBody = SKPhysicsBody(rectangleOf: boundsSize)
    bounds.physicsBody!.collisionBitMask = 0b0000  // Means bounds collides with no category (all zeros)
    bounds.physicsBody!.categoryBitMask = 0b0001  // Means the bounds belongs to category 0001 (in binary. Which again, translates to "1" in decimal), which if you remember, is what we set the stick to collide with

    // the default value for bounds.physicsBody!.contactBitMask 
    // is already 0b0000 so no need to set it explicitly here.
    // (You could do it for consistency sake if you'd like but it 
    // wouldn't matter since you're setting it to the same value it
    // already is)
    
    bounds.physicsBody!.affectedByGravity = false  // since you dont want it falling off of the screen

    // ...blah blah blah all the other code
}

Now you should get a print saying "flag" whenever a contact begins.

Hopefully this helped! If you have questions about how the contact system works just leave a comment and I'll answer them