0

I am trying to fix an issue I am having with the SKPhysicsContactDelegate collision detection. I have two nodes, nodeA and nodeB, nodeA is stationary on the screen while nodeB is able to be dragged around the screen by the users finger. nodeA needs to be able to detect if nodeB is overlapping it. The didBeginContact and didEndContact methods are being called multiple times which through research I have found to be an expected behavior. To get around this issue I simply set an integer variable to 0 and increment it each time there was a contact and decrement it each time a contact ended. If the value is greater than 0 then the two nodes are overlapping and if the value is equal to 0 then they are not. This works fine until the user drags nodeB over nodeA too fast. When this happens the contact methods are not always called the correct amount of times. For example, there may be 3 contacts detected but only two end contacts (or even none), which makes the program think that the two nodes are still overlapping even when they are not. I am assuming that this is happening because the user is dragging the node faster than the program can update. Is there anything I can do to get around this? Basically I just need to know exactly when the two nodes are overlapped and when they are not. Also note that the nodes are convex shapes. Below are my contact methods:

func didBeginContact(contact: SKPhysicsContact)
{
    let contactMask = contact.bodyA.categoryBitMask + contact.bodyB.categoryBitMask

    if contactMask == 3
    {
        startContact++
        timerStart = true
    }
}

func didEndContact(contact: SKPhysicsContact)
{
    let contactMask = contact.bodyA.categoryBitMask + contact.bodyB.categoryBitMask

    if contactMask == 3
    {
        startContact--
        if startContact == 0
        {
            timerStart = false
        }
    }
} 
BCRwar3
  • 45
  • 1
  • 11
  • This is expected behaviour if you are using physics engine to detect contacts. You can try with node.physicsBody.[usesPreciseCollisionDetection](https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKPhysicsBody_Ref/#//apple_ref/occ/instp/SKPhysicsBody/usesPreciseCollisionDetection) to see if there is a change. But still, if you move nodes too fast, then yeah, contact might not be detected every time. – Whirlwind Dec 13 '15 at 23:12
  • I have used the usesPreciseCollisionDetection and it doesn't help any. And yeah I kind of figured that was the case. Do you possibly know of any alternate way to get the results I need? I've been thinking of something to put in the update() method that is constantly checking to see if the nodes are overlapping but I am not sure what I would need to do. – BCRwar3 Dec 13 '15 at 23:18
  • You might try with SKNode's [intersectsNode:](https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKNode_Ref/#//apple_ref/occ/instm/SKNode/intersectsNode:) method. – Whirlwind Dec 13 '15 at 23:21

1 Answers1

1

You can check if one node intersect the other by using intersectsNode: method. From the docs about this method:

Returns a Boolean value that indicates whether this node intersects the specified node.

Also important part to keep in mind is:

The two nodes are considered to intersect if their frames intersect. The children of both nodes are ignored in this test.

import SpriteKit

class GameScene: SKScene {


    var stationaryNode :SKSpriteNode = SKSpriteNode(color: SKColor.grayColor(), size: CGSize(width: 100, height: 100))

    var moveableNode   :SKSpriteNode = SKSpriteNode(color: SKColor.purpleColor(), size: CGSize(width: 100, height: 100))

    let debugLabel     :SKLabelNode  = SKLabelNode(fontNamed: "ArialMT")

    override func didMoveToView(view: SKView) {


        setupScene()

    }

    func setupScene(){

        stationaryNode.name = "stationaryNode"
        stationaryNode.zRotation = 0.2
        stationaryNode.zPosition = 1
        stationaryNode.position = CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidY(frame))
        addChild(stationaryNode)

        moveableNode.name = "moveableNode"
        moveableNode.zRotation = 0.4
        moveableNode.zPosition = 2
        moveableNode.position = CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidY(frame)-200)
        addChild(moveableNode)

        debugLabel.fontSize = 18
        debugLabel.fontColor = SKColor.yellowColor()
        debugLabel.position = CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidY(frame)+200)
        addChild(debugLabel)
        updateDebugLabel()


    }

    func updateDebugLabel(){

        let intersectionDetected:String = stationaryNode.intersectsNode(moveableNode) ? "YES" : "NO"

        debugLabel.text = "Overlapping : " + intersectionDetected

    }

    override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {


        for touch in touches {

            let location = touch.locationInNode(self)

            let previousPosition = touch.previousLocationInNode(self)

            let node: SKNode? = nodeAtPoint(location)

            if let nodeName = node?.name{

                if nodeName == "moveableNode" {

                    let translation = CGPoint(x: location.x - previousPosition.x , y: location.y - previousPosition.y )

                    node!.position = CGPoint(x: node!.position.x + translation.x, y: node!.position.y + translation.y)

                }
            }

        }
    }

    override func update(currentTime: CFTimeInterval) {
        /* Called before each frame is rendered */


        updateDebugLabel()

    }
}

I guess that this solution works a bit better than when using physics engine for detecting contacts for such a fast moving objects. Still, moving the object extremely fast might have an unpredictable result.

Whirlwind
  • 14,286
  • 11
  • 68
  • 157
  • This works except I need to know when one shape intersects another. Since this only detects frames intersecting, the shape would have to be a rectangle, I am dealing with concave shapes in my program. – BCRwar3 Dec 14 '15 at 00:08
  • @BCRwar3 Yup, I am aware of that, but you didn't mentioned that in your original question ;). You can check [this](http://stackoverflow.com/questions/24412487/spritekit-detect-complete-node-overlap) to see if there is something helpful in there. – Whirlwind Dec 14 '15 at 00:18
  • Yeah I'm sorry at the time I didn't think about the type of shape being a problem, I'll update the question now – BCRwar3 Dec 14 '15 at 00:20
  • Probably a dumb question but there's no way to change the shape of a frame is there? The reason I'm asking is because I've read a little about overriding the calculateAccumulatedFrame method for an SKNode – BCRwar3 Dec 14 '15 at 00:32
  • @BCRwar3 Nope. The frame property is a CGRect and it represents the smallest rectangle which contains node's content. Also the frame property, for example, might be implicitly changed when xScale, yScale or zRotation properties are changed. But it can't be changed in the way you ask :) – Whirlwind Dec 14 '15 at 00:48