Spritekit/Swift - How to Check Contact of Two Nodes When They Are Already in Contact

Check if two nodes are the same color when they touch each other

You haven't set contactBitMask appropriately so no contacts were detected... By default, due to performance reasons a default value of this mask is zero:

When two bodies share the same space, each body’s category mask is
tested against the other body’s contact mask by performing a logical
AND operation. If either comparison results in a nonzero value, an
SKPhysicsContact object is created and passed to the physics world’s
delegate. For best performance, only set bits in the contacts mask for
interactions you are interested in.

The default value is 0x00000000 (all bits cleared).

To fix this, set both contact and category bit masks to an appropriate values, like this:

class GameScene: SKScene,SKPhysicsContactDelegate {

override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self

physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
physicsWorld.contactDelegate = self

}

func didBegin(_ contact: SKPhysicsContact) {

if let bodyA = contact.bodyA.node as? SKSpriteNode,
let bodyB = contact.bodyB.node as? SKSpriteNode{
//Of course this is simple example and you will have to do some "filtering" to determine what type of objects are collided.
// But the point is , when appropriate objects have collided, you compare their color properties.
if bodyA.color == bodyB.color {
bodyA.run(SKAction.removeFromParent())
bodyB.run(SKAction.removeFromParent())
}
}
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
let box = SKSpriteNode(color: UIColor.red, size: CGSize(width: 64, height: 64))
box.color = Color.randomColor

box.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 64, height: 64))
box.physicsBody?.contactTestBitMask = 0b1
box.physicsBody?.categoryBitMask = 0b1
box.position = location
addChild(box)
}
}
}

Now when a contact happen between two bodies, as said in docs, each body's category bit mask is tested agains the other body's contact mask, by performing logical AND operation. If a result is non-zero, a contact notification occurs. In this case this would be 1 & 1 = 1.

Sprite-Kit registering multiple collisions for single contact

OK - it would appear that a simple:

        if bomb == nil {return}

is all that's required. This should be added as follows:

        let bomb = contact.bodyA.categoryBitMask == category.bomb.rawValue ? contact.bodyA.node : contact.bodyB.node
if bomb == nil {return}

This works to prevent multiple collisions for a node that you removeFromParent in didBeginContact. If you don;t remove the node but are still registering multiple collisions, then use the node's userData property to set some sort of flag indicating that the node i s'anactive' Alternately, subclass SKSPriteNode and add a custom 'isActive' property, which is what I did to solve my problem of bullets passing up the side of an invader and taking out that invader and the one above it. This never happens on a 'direct hit'.

It doesn't answer the underlying question as to why SK is registering multiple collisions, but it does mean that I can remove all the extra code concerning setting contactTestBitMasks to 0 and then back to what they should be later etc.

Edit: So it appears that if you delete a node in didBeginContact, the node is removed but the physics body isn't. So you have to be careful as Sprite-Kit appears to build an array of physicsContacts that have occurred and then calls dBC multiple times, once for each contact. So if you are manipulating nodes and also removing them in dBC, be aware that you might run into an issue if you force unwrap a node's properties.

Check for contact before sprite enters another sprite

I suggest adding an SKConstraint to the player (or to the boxes), so that after simulating physics your character will always be at least 1 pixel away from the boxes (so as to not trigger another velocity = 0).

Since constraints are handled AFTER physics in spritekit, this should work fairly well:

Sample Image

Something like this should work:

 let boxes: [SKNode] = [leftBox, middleBox, rightBox]
var constraints = [SKConstraint]()
for box in boxes {
constraints.append(SKConstraint.distance(SKRange(lowerLimit: 5),
to: box)
}

player.constraints = constraints

SKConstraint documentation

note: you may need adjust the distance of the lowerLimit in the constraint to get the desired effect, or add more .distance constraints to your main player.. say, one for each corner of each box.



Related Topics



Leave a reply



Submit