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:
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
How to Get the Correct Current Time in iOS
How to Auto Call an @Ibaction Function
Adding Custom Game Logic to Scene Kit (Swift)
How to Use a Generic Class Without the Type Argument in Swift
Custom Uitoolbar Too Close to the Home Indicator on iPhone X
How to Get Alexa Working on My iOS App
Moya/Alamofire - Url Encoded Params with Same Keys
How to Create a Rounded Rectangle Label in Xcode 7 and Swift 2
Cannot Set Color of Button's Label Inside Menu in Swiftui
How to Get the Original Filename of a Sprite/Texture in Swift
iOS Keyboard Active But Invisible When Uisearchbar Is Tapped
How to Get the User "Name" Using Swift
iOS Media Playback Controls Notification
Avcapturestillimageoutput VS Avcapturephotooutput in Swift 3