swiftsprite-kitcollision-detectionskspritenodeskphysicsbody

Swift SpriteKit basic Contact / Collision


The problem: When the coin spawns, it's physicsBody appears right underneath the spriteNode. Also, when the player comes in contact with the coin's physicsBody, the player bounces off of the physicsBody and the game ends.

What the output should be: The coin's physisBody should be aligned properly with the coin spriteNode. When the player comes in contact with the coin, the coin should disappear and +1 should be added to the proper label.

The current code:

struct ColliderType {

static let playerCategory: UInt32 = 0x1 << 0

static let boundary: UInt32 = 0x1 << 1
​  
​static let coinCategory: UInt32 = 0x1 << 2
​
​static let bodyA: UInt32 = 0x1 << 4
​
​static let bodyB: UInt32 = 0x1 << 8

}

override func didMoveToView(view: SKView) {

var coinInt = 0
​
​
​
​self.physicsWorld.gravity = CGVectorMake(0.0, -7.0)
physicsWorld.contactDelegate = self

player = SKSpriteNode(imageNamed: "player")
player.zPosition = 1
player.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
player.physicsBody = SKPhysicsBody(circleOfRadius: player.size.width / 5.12)
player.physicsBody?.dynamic = true
player.physicsBody?.allowsRotation = false

self.addChild(player)

generateCoins()

​
​
coin = SKSpriteNode( imageNamed: "coin")
coin.physicsBody? = SKPhysicsBody(circleOfRadius: coin.size.height / 10)
coin.physicsBody?.dynamic = false
coin.physicsBody?.allowsRotation = false
coin.zPosition = 1
​self.addChild(coin)
​
player.physicsBody?.categoryBitMask = ColliderType.playerCategory
​player.physicsBody?.contactTestBitMask = ColliderType.boundary
player.physicsBody?.collisionBitMask = ColliderType.coinCategory | ColliderType.boundary

coin.physicsBody?.categoryBitMask = ColliderType.coinCategory
coin.physicsBody?.contactTestBitMask = ColliderType.playerCategory
coin.physicsBody?.collisionBitMask = ColliderType.playerCategory

func didPlayerCollideWithCoin(player: SKSpriteNode, coin: SKSpriteNode) {



self.coin.removeFromParent()

self.coin += 1

coinLabel.text = "\(coinInt)"

}

​
​    
​
​func generateCoins()  {

if(self.actionForKey("spawning") != nil){return}

let coinTimer = SKAction.waitForDuration(7, withRange: 2)

let spawnCoin = SKAction.runBlock {

self.coin = SKSpriteNode( imageNamed: "coin")

self.coin.physicsBody = SKPhysicsBody(circleOfRadius: self.coin.size.height / 10)

self.coin.name = "coin"

self.coin.physicsBody?.dynamic = false

self.coin.physicsBody?.allowsRotation = false

var coinPosition = Array<CGPoint>()

coinPosition.append((CGPoint(x:340, y:103)))

coinPosition.append((CGPoint(x:340, y:148)))

coinPosition.append((CGPoint(x:340, y:218)))

coinPosition.append((CGPoint(x: 340, y:343)))

let spawnLocation = coinPosition[Int(arc4random_uniform(UInt32(coinPosition.count)))]

let action = SKAction.repeatActionForever(SKAction.moveToX(+self.xScale, duration: 4.4))

self.coin.runAction(action)

self.coin.position = spawnLocation

self.addChild(self.coin)

print(spawnLocation)

}

let sequence = SKAction.sequence([coinTimer, spawnCoin])

self.runAction(SKAction.repeatActionForever(sequence), withKey: "spawning")

}
​​
func didBeginContact(contact:SKPhysicsContact)  {

let bodyA: SKPhysicsBody = contact.bodyA

let bodyB: SKPhysicsBody = contact.bodyB

if ((bodyA.categoryBitMask == ColliderType.playerCategory) &&     (bodyB.categoryBitMask == ColliderType.coinCategory)){

didPlayerCollideWithCoin(bodyA.node as! SKSpriteNode, coin: bodyB.node as! SKSpriteNode)

}

}

Solution

  • I would recommend that you read these documentation first!

    contactTestBitMask - A mask that defines which categories of bodies cause intersection notifications with a current physics body.

    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.

    collisionBitmask - A mask that defines which categories of physics bodies can collide with this physics body.

    When two physics bodies contact each other, a collision may occur. This body’s collision mask is compared to the other body’s category mask by performing a logical AND operation. If the result is a nonzero value, this body is affected by the collision. Each body independently chooses whether it wants to be affected by the other body. For example, you might use this to avoid collision calculations that would make negligible changes to a body’s velocity.

    Remove this code to solve the colliding issue

    coin.physicsBody?.collisionBitMask = ColliderType.playerCategory
    

    Try to see if this solves your SpriteNode alignment issue

     var coinTexture = SKTexture(imageNamed: "coin")
     coin = SKSpriteNode(texture:coinTexture)