Single collisions being detected multiple times - ios

I've read similar questions posted here, but none of them fully address my problem.
I've only been working with iOS for a few months.
My collisions are working 90% of the time, but occasionally I'm getting collisions with same object. This should never happen because one of the 2 objects should explode.
Sometimes this leads to fatal crashes, seemingly because it tries to run the subsequent code after its already been executed.
Basically when the ship has a shield I want the asteroid to explode rather than the ship. I have put in the "if bonus == 0" statement to accomplish this.
I've posted the relevant code, if you need more, please let me know.
Have I over complicated the code? Or should I be taking another approach?
enum PhysicsCategory : UInt32 {
case None = 0
case objectGroup = 1
case birdGroup = 2
case shieldGroup = 4
case gapGroup = 8
case boundaryGroup = 16
case bonusGroup = 32
}
....
func didBeginContact(contact: SKPhysicsContact) {
if bonus == 0 {
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
....
case PhysicsCategory.birdGroup.rawValue | PhysicsCategory.objectGroup.rawValue:
if contact.bodyA.categoryBitMask == PhysicsCategory.birdGroup.rawValue {
gameOverActions()
} else if contact.bodyB.categoryBitMask == PhysicsCategory.birdGroup.rawValue {
gameOverActions()
}
default:
fatalError("other collision: \(contactMask)")
}
} else if bonus > 0 {
println("Bonus > 0")
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
....
case PhysicsCategory.birdGroup.rawValue | PhysicsCategory.objectGroup.rawValue:
if contact.bodyA.categoryBitMask == PhysicsCategory.birdGroup.rawValue {
let secondBody = contact.bodyB.node
let asteroidExplosionNode = secondBody?.position
//asteroidExplosion()
let asteroidExplosionEmitter3 = SKEmitterNode(fileNamed: "explosionSmoke.sks")
asteroidExplosionEmitter3.position = asteroidExplosionNode!
asteroidExplosionEmitter3.name = "asteroidExplosionEmitter3"
asteroidExplosionEmitter3.zPosition = 25
asteroidExplosionEmitter3.targetNode = self
movingObjects.addChild(asteroidExplosionEmitter3)
secondBody?.removeFromParent()
bonus--
bonusUsed++
bonusIndicator.text = "x\(bonus)"
bonusIndicatorShadow.text = "x\(bonus)"
if bonus >= 1 {
shieldAnimate()
} else if bonus == 0 {
shield.removeFromParent()
}
} else if contact.bodyB.categoryBitMask == PhysicsCategory.birdGroup.rawValue {
let secondBody = contact.bodyA.node
let asteroidExplosionNode = secondBody?.position
//asteroidExplosion()
let asteroidExplosionEmitter3 = SKEmitterNode(fileNamed: "explosionSmoke.sks")
asteroidExplosionEmitter3.position = asteroidExplosionNode!
asteroidExplosionEmitter3.name = "asteroidExplosionEmitter3"
asteroidExplosionEmitter3.zPosition = 25
asteroidExplosionEmitter3.targetNode = self
movingObjects.addChild(asteroidExplosionEmitter3)
secondBody?.removeFromParent()
bonus--
bonusUsed++
bonusIndicator.text = "x\(bonus)"
bonusIndicatorShadow.text = "x\(bonus)"
if bonus >= 1 {
shieldAnimate()
} else if bonus == 0 {
shield.removeFromParent()
}
}
default:
fatalError("other collision: \(contactMask)")
}
}
}

Since you are removing the node on collision you can check if the parent is nil, before executing the rest of the code.
let secondBody = contact.bodyB.node
if secondBody?.parent != nil {
// Asteroid explosion and bonus code.
}

Related

Detecting multiple collisions in SpriteKit

I'm still a beginner in Swift and I'm having some trouble with collision detections in SpriteKit. I used this question from StackOverFlow which was great in showing my how to construct things neatly. But I'm having problems with my didBegin function, which does not even get called to at all. I'm hoping I missed something simple out that you guys can take a look at for me.
Thanks in advance.
Here is my PhysicsCatagoies struct:
import Foundation
import SpriteKit
struct PhysicsCatagories: OptionSet {
let rawValue: UInt32
init(rawValue: UInt32) { self.rawValue = rawValue }
static let None = PhysicsCatagories(rawValue: 0b00000) // Binary for 0
static let Player = PhysicsCatagories(rawValue: 0b00001) // Binary for 1
static let EnemyBullet = PhysicsCatagories(rawValue: 0b00010) // Binary for 2
static let PlayerBullet = PhysicsCatagories(rawValue: 0b00100) // Binary for 4
static let Enemy = PhysicsCatagories(rawValue: 0b01000) // Binary for 8
static let Boss = PhysicsCatagories(rawValue: 0b10000) // Binary for 16
}
extension SKPhysicsBody {
var category: PhysicsCatagories {
get {
return PhysicsCatagories(rawValue: self.categoryBitMask)
}
set(newValue) {
self.categoryBitMask = newValue.rawValue
}
}
}
And here is how I assigned my nodes in GameScene:
player.physicsBody = SKPhysicsBody(rectangleOf: player.size)
player.physicsBody!.affectedByGravity = false
player.physicsBody!.categoryBitMask = PhysicsCatagories.Player.rawValue
player.physicsBody!.collisionBitMask = PhysicsCatagories.None.rawValue
player.physicsBody!.category = [.Enemy, .EnemyBullet, .Boss]
bullet.physicsBody = SKPhysicsBody(rectangleOf: bullet.size)
bullet.physicsBody!.affectedByGravity = false
bullet.physicsBody!.categoryBitMask = PhysicsCatagories.PlayerBullet.rawValue
bullet.physicsBody!.collisionBitMask = PhysicsCatagories.None.rawValue
bullet.physicsBody!.category = [.Enemy, .Boss]
enemy.physicsBody = SKPhysicsBody(rectangleOf: enemy.size)
enemy.physicsBody!.affectedByGravity = false
enemy.physicsBody!.categoryBitMask = PhysicsCatagories.Enemy.rawValue
enemy.physicsBody!.collisionBitMask = PhysicsCatagories.None.rawValue
enemy.physicsBody!.category = [.Player, .PlayerBullet]
enemyBullet.physicsBody = SKPhysicsBody(rectangleOf: enemyBullet.size)
enemyBullet.physicsBody!.affectedByGravity = false
enemyBullet.physicsBody!.categoryBitMask = PhysicsCatagories.EnemyBullet.rawValue
enemyBullet.physicsBody!.collisionBitMask = PhysicsCatagories.None.rawValue
enemyBullet.physicsBody!.category = [.Player]
boss.physicsBody = SKPhysicsBody(rectangleOf: boss.size)
boss.physicsBody!.affectedByGravity = false
boss.physicsBody!.categoryBitMask = PhysicsCatagories.Boss.rawValue
boss.physicsBody!.collisionBitMask = PhysicsCatagories.None.rawValue
boss.physicsBody!.category = [.Player, .PlayerBullet]
bulletSpecial.physicsBody = SKPhysicsBody(rectangleOf: bulletSpecial.size)
bulletSpecial.physicsBody!.affectedByGravity = false
bulletSpecial.physicsBody!.categoryBitMask = PhysicsCatagories.PlayerBullet.rawValue
bulletSpecial.physicsBody!.collisionBitMask = PhysicsCatagories.None.rawValue
bulletSpecial.physicsBody!.category = [.Enemy, .Boss]
Finally, this is my didBegin function, which does not seem to work at all:
func didBegin(_ contact: SKPhysicsContact) {
let contactCategory: PhysicsCatagories = [contact.bodyA.category, contact.bodyB.category]
switch contactCategory {
case [.Player, .Enemy]:
print("player has hit enemy")
case [.PlayerBullet, .Enemy]:
print("player bullet has hit enemy")
case [.PlayerBullet, .Boss]:
print("player bullet has hit boss")
case [.Player, .Boss]:
print("player has hit boss")
case [.Player, .EnemyBullet]:
print("player has hit enemy bullet")
default:
preconditionFailure("Unexpected collision type: \(contactCategory)")
}
}
I've not used the OptionSet technique for cagegoryBitMasks, so here's how I'd do it:
Define unique categories, ensure your class is a SKPhysicsContactDelegate and make yourself the physics contact delegate:
//Physics categories
let PlayerCategory: UInt32 = 1 << 0 // b'00001'
let EnemyBulletCategory: UInt32 = 1 << 1 // b'00010'
let PlayerBulletCategory: UInt32 = 1 << 2 // b'00100'
let EnemyCategory: UInt32 = 1 << 3 // b'01000'
let BossCategory: UInt32 = 1 << 4 // b'10000'
class GameScene: SKScene, SKPhysicsContactDelegate {
physicsWorld.contactDelegate = self
Assign the categories (usually in didMove(to view:) :
player.physicsBody.catgeoryBitMask = PlayerCategory
bullet.physicsBody.catgeoryBitMask = BulletCategory
enemy.physicsBody.catgeoryBitMask = EnemyCategory
enemyBullet.physicsBody.catgeoryBitMask = EnemyBulletCategory
boss.physicsBody.catgeoryBitMask = BossCategory
(not sure about bulletSpecial - looks the same as bullet)
Set up contact detection:
player.physicsBody?.contactTestBitMask = EnemyCategory | EnemyBulletCategory | BossCategory
bullet.physicsBody?.contactTestBitMask = EnemyCategory | BossCategory
enemy.physicsBody?.contactTestBitMask = PlayerCategory | PlayerBulletCategory
enemyBullet.physicsBody?.contactTestBitMask = PlayerCategory
boss.physicsBody?.contactTestBitMask = PlayerCategory | PlayerBulletCategory
Turn off collisions: (on by default)
player.physicsBody?.collisionBitMask = 0
bullet.physicsBody?.collisionBitMask = 0
enemy.physicsBody?.collisionBitMask = 0
enemyBullet.physicsBody?.collisionBitMask = 0
boss.physicsBody?.collisionBitMask = 0
Implement didBegin:
func didBegin(_ contact: SKPhysicsContact) {
print("didBeginContact entered for \(String(describing: contact.bodyA.node!.name)) and \(String(describing: contact.bodyB.node!.name))")
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
case PlayerCategory | EnemyCategory:
print("player has hit enemy")
case PlayerBulletCategory | EnemyCategory:
print("player bullet has hit enemy")
case PlayerBulletCategory | BossCategory:
print("player bullet has hit boss")
case PlayerCategory | BossCategory:
print("player has hit boss")
case PlayerCategory | EnemyBulletCategory:
print("player has hit enemy bullet")
default:
print("Undetected collision occurred")
}
}
It's a bit late here, so hopefully I haven't made any stupid mistakes.
=======================
You could also include this function and then call it via checkPhysics() once you have set up all your physics bodies and collisions and contact bit masks. It will go through every node and print out what collides with what and what contacts what (it doesn't check the isDynamic property, so watch out for that):
//MARK: - Analyse the collision/contact set up.
func checkPhysics() {
// Create an array of all the nodes with physicsBodies
var physicsNodes = [SKNode]()
//Get all physics bodies
enumerateChildNodes(withName: "//.") { node, _ in
if let _ = node.physicsBody {
physicsNodes.append(node)
} else {
print("\(String(describing: node.name)) does not have a physics body so cannot collide or be involved in contacts.")
}
}
//For each node, check it's category against every other node's collion and contctTest bit mask
for node in physicsNodes {
let category = node.physicsBody!.categoryBitMask
// Identify the node by its category if the name is blank
let name = node.name != nil ? node.name : "Category \(category)"
if category == UInt32.max {print("Category for \(String(describing: name)) does not appear to be set correctly as \(category)")}
let collisionMask = node.physicsBody!.collisionBitMask
let contactMask = node.physicsBody!.contactTestBitMask
// If all bits of the collisonmask set, just say it collides with everything.
if collisionMask == UInt32.max {
print("\(name) collides with everything")
}
for otherNode in physicsNodes {
if (node != otherNode) && (node.physicsBody?.isDynamic == true) {
let otherCategory = otherNode.physicsBody!.categoryBitMask
// Identify the node by its category if the name is blank
let otherName = otherNode.name != nil ? otherNode.name : "Category \(otherCategory)"
// If the collisonmask and category match, they will collide
if ((collisionMask & otherCategory) != 0) && (collisionMask != UInt32.max) {
print("\(name) collides with \(String(describing: otherName))")
}
// If the contactMAsk and category match, they will contact
if (contactMask & otherCategory) != 0 {print("\(name) notifies when contacting \(String(describing: otherName))")}
}
}
}
}
It will produce output like:
Optional("shape_blueSquare") collides with Optional("Screen_edge")
Optional("shape_redCircle") collides with Optional("Screen_edge")
Optional("shape_redCircle") collides with Optional("shape_blueSquare")
Optional("shape_redCircle") notifies when contacting Optional("shape_purpleSquare")
Optional("shape_redCircle") collides with Optional("shape_greenRect")
Optional("shape_redCircle") notifies when contacting Optional("shape_greenRect")
Optional("shape_purpleSquare") collides with Optional("Screen_edge")
Optional("shape_purpleSquare") collides with Optional("shape_greenRect")
Category for Optional("shape_greenRect") does not appear to be set correctly as 4294967295
Optional("shape_greenRect") collides with Optional("Screen_edge")
Optional("shape_yellowTriangle") notifies when contacting Optional("shape_redCircle")
Optional("shape_yellowTriangle") collides with Optional("shape_greenRect")
Optional("shape_yellowTriangle") notifies when contacting Optional("shape_greenRect")
etc.

Sprite Kit Contact Detection

Basically I have the ground, the player (raymond), and coins.
When the player touches the ground nothing should happen, game continues as normal. When the player comes in contact with the coin, I want it to print to console "coin contact with player".
enum ColliderType: UInt32 {
case Raymond = 1
case Object = 2
case Coin = 3
}
Raymonds physics
raymond.physicsBody = SKPhysicsBody(circleOfRadius: raymondTexture.size().height/2)
raymond.physicsBody!.dynamic = true
raymond.physicsBody!.categoryBitMask = ColliderType.Raymond.rawValue
raymond.physicsBody?.contactTestBitMask = ColliderType.Object.rawValue
raymond.physicsBody?.collisionBitMask = ColliderType.Object.rawValue
Coins Physics
coin.physicsBody = SKPhysicsBody(circleOfRadius: raymondTexture.size().height/2)
coin.physicsBody!.dynamic = true
coin.physicsBody!.categoryBitMask = ColliderType.Coin.rawValue
coin.physicsBody?.contactTestBitMask = ColliderType.Raymond.rawValue
coin.physicsBody?.collisionBitMask = ColliderType.Object.rawValue
Ground Physics if you need
ground.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.frame.size.width, 1))
ground.physicsBody!.dynamic = false
ground.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
ground.physicsBody?.contactTestBitMask = ColliderType.Object.rawValue
ground.physicsBody?.collisionBitMask = ColliderType.Object.rawValue
Heres the contact function I have, I know its wrong and I need help with how to detect the coin and raymond touching.
func didBeginContact(contact: SKPhysicsContact) {
print("coin contact with player")
}
Thanks in advance.
There really is several ways of checking this, Here are 2 of the most basics ways to get you started. The first checks name for contact, and the second checks the CategoryBitMask. It is worth noting that if your PhysicsBodies bitmasks are not set properly contact may never be reported between 2 objects.
Edit Make sure the Scene conforms to SKPhysicsContactDelegate
class GameScene: SKScene, SKPhysicsContactDelegate
...
func didBegin(_ contact: SKPhysicsContact) {
let contactAName = contact.bodyA.node?.name
let contactBName = contact.bodyB.node?.name
if (contactAName == "raymond") || (contactBName == "raymond") {
if (contactAName == "coin") || (contactBName == "coin") {
print("coin contact with player")
return
}
}
//or
if contact.bodyA.categoryBitMask == ColliderType.Coin || contact.bodyB.categoryBitMask == ColliderType.Coin {
if contact.bodyA.categoryBitMask == ColliderType.Raymond || contact.bodyB.categoryBitMask == ColliderType.Raymond {
print("coin contact with player")
return
}
}
}

Collide type source error - spritekit swift game

So I have adjusted my games ball type from a shape to an image which forced me to redo the physics of my game. I am new to swift and have struggled with fixing my collisions in my swift game.
class GameScene: SKScene, GameDelegate, SKPhysicsContactDelegate {
var ball: Ball!
let ballSpeedX = CGFloat(500)
//ball = Ball(imageNamed:"colorBall.png")
enum CollisionTypes: UInt32 {
case Floor = 1
case Ball = 2
}
// board
let boards = Boards.make(CollideType.BoardStart.rawValue)
var lastBoard: SKNode?
var boardSpeedY: CGFloat { get { return CGFloat(160) * accelerate }}
let boardDistanceY = CGFloat(300)
let boardYDistanceHide = CGFloat(30)
override func didMoveToView(view: SKView) {
self.physicsWorld.gravity = CGVectorMake(0, 0)
self.physicsWorld.contactDelegate = self
let flooeBody = SKPhysicsBody.init(edgeLoopFromRect: self.frame)
self.physicsBody = floorBody
self.physicsBody!.affectedByGravity = false
self.physicsBody!.usesPreciseCollisionDetection = true
self.physicsBody!.dynamic = true
self.physicsBody!.mass = 0
self.physicsBody!.friction = 0
self.physicsBody!.linearDamping = 0
self.physicsBody!.angularDamping = 0
self.physicsBody!.restitution = 1
self.physicsBody!.categoryBitMask = CollisionTypes.Floor.rawValue
self.physicsBody!.contactTestBitMask = CollisionTypes.Ball.rawValue
// Prepare the ball - physics engine.
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.frame.width/2)
ball.physicsBody!.affectedByGravity = true
ball.physicsBody!.restitution = 0.8
ball.physicsBody!.linearDamping = 0
ball.physicsBody!.friction = 0.3
ball.physicsBody!.dynamic = true
ball.physicsBody!.mass = 0.5
ball.physicsBody!.allowsRotation = true
ball.physicsBody!.categoryBitMask = CollisionTypes.Ball.rawValue
ball.physicsBody!.contactTestBitMask = CollisionTypes.Floor.rawValue
ball.physicsBody!.collisionBitMask = CollisionTypes.Floor.rawValue
ball.hidden = false
self.addChild(ball)
// scene
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsBody!.categoryBitMask = CollideType.Scene.toMask()
self.physicsBody!.dynamic = false
self.physicsBody!.friction = 0
// ceil
let ceil = SKShapeNode(rectOfSize: CGSize(width: self.frame.width, height: 2))
ceil.position.x = CGRectGetMidX(self.frame)
ceil.position.y = self.frame.height - CGRectGetMidY(ceil.frame)
ceil.physicsBody = SKPhysicsBody(rectangleOfSize: ceil.frame.size)
ceil.physicsBody!.categoryBitMask = CollideType.Ceil.toMask()
ceil.physicsBody!.dynamic = false
ceil.alpha = 0
self.addChild(ceil)
// floor
let floor = SKShapeNode(rectOfSize: CGSize(width: self.frame.width, height: 2))
floor.position.x = CGRectGetMidX(self.frame)
floor.position.y = CGRectGetMidY(floor.frame)
floor.physicsBody = SKPhysicsBody(rectangleOfSize: floor.frame.size)
//floor.physicsBody!.categoryBitMask = CollideType.Floor.toMask() two
floor.physicsBody!.dynamic = false
floor.alpha = 0
self.addChild(floor)
}
That is the scene and physics I attempted to set up and was directed with. Below is the collide errors which cause the app to crash upon touch.
func didBeginContact(contact: SKPhysicsContact) {
let bitMaskAAndB = contact.bodyA.categoryBitMask == CollisionTypes.Floor.rawValue && contact.bodyB.categoryBitMask == CollisionTypes.Ball.rawValue
let ballAndBoardMask = CollideType.Ball.toMask() | boards.usedCollideMasks
// ball and board error: CANNOT CONVERT VALUE OF TYPE BOOL TO EXPCTD ARG TYPE UIINT32
if bitMaskAAndB | ballAndBoardMask == ballAndBoardMask {
let boardNode: SKNode! = contact.bodyA.categoryBitMask == CollideType.Ball.toMask() ? contact.bodyB.node : contact.bodyA.node
let board = boardNode.bind as! BoardDelegate
board.didBeginContact(boardNode, ball: ball, contact: contact, game: self)
}
// ball and ceil => ERROR
else if bitMaskAAndB == CollideType.toMask([.Ball, .Ceil]) {
stopGame()
}
// ball and floor => stop game
else if bitMaskAAndB == CollideType.toMask([.Ball, .Floor]) {
stopGame()
}
}
func didEndContact(contact: SKPhysicsContact) {
let ballAndBoardMask = CollideType.Ball.toMask() | boards.usedCollideMasks
// ball and board, handle it by board delegate
if contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask | ballAndBoardMask == ballAndBoardMask {
let boardNode: SKNode! = contact.bodyA.categoryBitMask == CollideType.Ball.toMask() ? contact.bodyB.node : contact.bodyA.node
let board = boardNode.bind as! BoardDelegate
board.didEndContact(boardNode, ball: ball, contact: contact, game: self)
}
}
CollideType definition as below:
enum CollideType: Int {
case Scene = 0
case Ceil = 1
case Floor = 2
case Ball = 3
case BoardStart = 4
func toMask()-> UInt32 {
return UInt32(1 << self.rawValue)
}
static func toMask(masks: [CollideType])-> UInt32 {
var toMask = UInt32(0)
for type in masks {
toMask |= type.toMask()
}
return toMask
}
So the issue now is that you have two definition for your masks:
Remove this one:
enum CollisionTypes: UInt32 {
case Floor = 1
case Ball = 2
}
Use only this one:
enum CollideType: Int {
case Scene = 0
case Ceil = 1
case Floor = 2
case Ball = 3
case BoardStart = 4
func toMask()-> UInt32 {
return UInt32(1 << self.rawValue)
}
static func toMask(masks: [CollideType])-> UInt32 {
var toMask = UInt32(0)
for type in masks {
toMask |= type.toMask()
}
return toMask
}
Correct all code to match with CollideType definitions.
Correct this line in didBeginContact:
let bitMaskAAndB = contact.bodyA.categoryBitMask == CollisionTypes.Floor.rawValue && contact.bodyB.categoryBitMask == CollisionTypes.Ball.rawValue
with:
let bitMaskAAndB = contact.bodyA.categoryBitMask == CollideType.Floor.toMask() ? contact.bodyB.categoryBitMask : CollideType.Ball.toMask()
If you have correct all you will don't have yet this error:
// ball and ceil => ERROR

didBeginContact for three sprites works for 2 sprites, but not for the third one - Swift2

I have a bird, multiple desks and a star. When the bird collides with one of the desks, the didBeginContact method works fine, but when it collides with the star, nothing happens. I will copy the code where I added some print statements so it is easier to see what is happening and then I will paste the print statements that it prints out.
Bird (just the initialization of the physicsBody part):
class Bird: SKSpriteNode {
var sc: SKScene!
init(sc: SKScene) {
let texture = SKTexture(imageNamed: "hero")
let size = texture.size()
self.sc = sc
super.init(texture: texture, color: UIColor.clearColor(), size: CGSizeMake(size.width/2, size.height/2))
self.zPosition = Layer.Bird.rawValue
self.name = "bird"
self.physicsBody = SKPhysicsBody(circleOfRadius: self.size.width/2)
self.physicsBody?.categoryBitMask = PhysicsCategory.Bird
self.physicsBody?.collisionBitMask = PhysicsCategory.Desk
self.physicsBody?.contactTestBitMask = PhysicsCategory.Desk | PhysicsCategory.StarSpecial | PhysicsCategory.Star
self.physicsBody?.allowsRotation = true
self.physicsBody?.restitution = 0
self.physicsBody?.usesPreciseCollisionDetection = true
}
// other methods and the required init
}
The star class is constructed the same way so I will just copy physicsBody related code:
self.physicsBody = SKPhysicsBody(circleOfRadius: self.size.width/2, center: self.position)
self.physicsBody?.affectedByGravity = false
self.physicsBody?.pinned = true
self.physicsBody?.categoryBitMask = PhysicsCategory.StarSpecial
self.physicsBody?.collisionBitMask = PhysicsCategory.None
self.physicsBody?.contactTestBitMask = PhysicsCategory.Bird
self.physicsBody?.usesPreciseCollisionDetection = true
self.physicsBody?.allowsRotation = false
self.physicsBody?.restitution = 0
self.physicsBody?.usesPreciseCollisionDetection = true
And the didBeginContact method in GameScene:
func didBeginContact(contact: SKPhysicsContact) {
print("something has collided")
if contact.bodyA.categoryBitMask == PhysicsCategory.Bird && contact.bodyB.categoryBitMask == PhysicsCategory.Desk {
birdSprite.BirdDidCollideWithDesk(contact.bodyA.node as! Bird, desk: contact.bodyB.node as! Desk, scoreClass: scoreClass, scoreLabel: scoreLabel)
print("bird and desk have collided")
} else if contact.bodyA.categoryBitMask == PhysicsCategory.Desk && contact.bodyB.categoryBitMask == PhysicsCategory.Bird {
print("desk and bird have collided")
birdSprite.BirdDidCollideWithDesk(contact.bodyB.node as! Bird, desk: contact.bodyA.node as! Desk, scoreClass: scoreClass, scoreLabel: scoreLabel)
}
if contact.bodyA.categoryBitMask == PhysicsCategory.Bird && contact.bodyB.categoryBitMask == PhysicsCategory.StarSpecial {
print("collided into star1")
starSpecial.removeStar(contact.bodyB.node as! Star)
} else if contact.bodyA.categoryBitMask == PhysicsCategory.StarSpecial && contact.bodyB.categoryBitMask == PhysicsCategory.Bird {
print("collided into star2")
starSpecial.removeStar(contact.bodyB.node as! Star)
}
}
If I run it, I get the following messages:
If the bird collides with a desk: "something has collided", "desk and bird have collided"
If the bird collides with the star: "something has collided"
Neither of second two conditions is met, so the message "collided into star1" or "collided into star2" is not printed out.
Any idea what could be causing this?

multiple collisions or drugging

I have two types of collision. First one is the spaceShip and asteroid (this is already handled) but I can't handle second type of collisions, where my spaceShipLazer hits enemySpaceShips. In second case I have drugging/multiple collisions or sometimes perfect collision.
My enemySpaceShip is an EnemySpaceShip class which is a subclass of SKSpriteNode.
So the question is about second collision. Is has multiple collisions that is not expected. How I can fix it?
func didBeginContact(contact: SKPhysicsContact) {
if contact.bodyA.categoryBitMask == CollisionCategories.PlayerSpaceShip && contact.bodyB.categoryBitMask == CollisionCategories.Asteroid || contact.bodyB.categoryBitMask == CollisionCategories.PlayerSpaceShip && contact.bodyA.categoryBitMask == CollisionCategories.Asteroid {
if !gameOver && !playerWasHit {
playerWasHit = true
self.pauseTheGame()
//spaceShip vs asteroid animation
let fadeOutAction = SKAction.fadeOutWithDuration(0.1)
fadeOutAction.timingMode = SKActionTimingMode.EaseOut
let fadeInAction = SKAction.fadeInWithDuration(0.1)
fadeInAction.timingMode = SKActionTimingMode.EaseOut
let blinkAction = SKAction.sequence([fadeOutAction, fadeInAction])
let blinkRepeatAction = SKAction.repeatAction(blinkAction, count: 3)
let delayAction = SKAction.waitForDuration(0.2)
let gameOverAction = SKAction.runBlock({ () -> Void in
self.gameSettings.lives--
self.gameDelegate?.gameDelegateDidUpdateLives()
if self.gameSettings.lives > 0 {
self.respawn()
} else {
self.gameSettings.recordScores(self.gameSettings.currentScore)
self.gameDelegate?.gameDelegateGameOver(self.gameSettings.currentScore)
self.gameOver = true
self.pauseTheGame()
}
})
let gameOverSequence = SKAction.sequence([blinkRepeatAction, delayAction, gameOverAction])
spaceShipLayer.runAction(gameOverSequence)
}
if soundOn {
let hitSoundAction = SKAction.playSoundFileNamed("hitSound.wav", waitForCompletion: true)
runAction(hitSoundAction)
}
//second case playerLazer vs enemySpaceShip
} else if contact.bodyA.categoryBitMask == CollisionCategories.PlayerLaser && contact.bodyB.categoryBitMask == CollisionCategories.EnemySpaceShip || contact.bodyB.categoryBitMask == CollisionCategories.PlayerLaser && contact.bodyA.categoryBitMask == CollisionCategories.EnemySpaceShip {
contact.bodyA.node?.removeFromParent()
contact.bodyB.node?.removeFromParent()
}
}
I found decision that makes everything perfect:
just second part should be like this:
if
contact.bodyA.categoryBitMask == CollisionCategories.PlayerLaser &&
contact.bodyB.categoryBitMask == CollisionCategories.EnemySpaceShip ||
contact.bodyA.categoryBitMask == CollisionCategories.EnemySpaceShip &&
contact.bodyB.categoryBitMask == CollisionCategories.PlayerLaser
{
contact.bodyA.node?.physicsBody?.categoryBitMask = CollisionCategories.None
contact.bodyB.node?.physicsBody?.categoryBitMask = CollisionCategories.None
contact.bodyA.node?.removeFromParent()
contact.bodyB.node?.removeFromParent()
self.addPoints(5)
}
}
So I've made .None categoryBitMask and applied it after collision detection so now points and spriteNode behavior is correct!

Resources