Why is my code never detecting any contact with my SKPhysicsBody? - swift

The problem I am running in to is that the code I created to detect contact is not working. I wanted it to work when the ball category touched the obstacle category. What is wrong with my code? Thank you so much, any help is appreciated!
Heres where I define the categories:
let ballCategory : UInt32 = 0x1 << 1
let obstacleCategory : UInt32 = 0x1 << 2
Heres where I create the physics aspect of my ball:
leftBall.physicsBody = SKPhysicsBody(rectangleOf: leftBall.size)
leftBall.physicsBody?.categoryBitMask = ballCategory
leftBall.physicsBody?.contactTestBitMask = obstacleCategory
leftBall.physicsBody?.affectedByGravity = false
leftBall.physicsBody?.isDynamic = false
leftBall.physicsBody?.collisionBitMask = 0
Heres where I create the physics aspect of an obstacle:
obstacleSquare.physicsBody = SKPhysicsBody(rectangleOf: obstacleSquare.size)
obstacleSquare.physicsBody?.categoryBitMask = obstacleCategory
obstacleSquare.physicsBody?.contactTestBitMask = ballCategory
obstacleSquare.physicsBody?.affectedByGravity = false
obstacleSquare.physicsBody?.isDynamic = false
obstacleSquare.physicsBody?.collisionBitMask = 1
And heres my collision detection function:
func didBegin(_ contact: SKPhysicsContact) {
print("CONTACT")
if contact.bodyA.categoryBitMask == obstacleCategory {
print("CONTACT")
gameOver()
}
if contact.bodyB.categoryBitMask == obstacleCategory {
print("CONTACT")
gameOver()
}
if contact.bodyA.categoryBitMask == ballCategory {
print("CONTACT")
gameOver()
}
if contact.bodyB.categoryBitMask == ballCategory {
print("CONTACT")
gameOver()
}
}

Both of your object’s physics bodies have their isDynamic Property set to false. At least one of them needs to have this property set to true for any contacts to be registered.

Related

collision detection between two nodes in swift 4

I wanted to have a collision detection between the avatar and the obstacle, so whenever something collides, it should print "collision", but even that doesn't work, so the problem is, that it doesn't detect any collision. And if it would collide, it should differentiate between the player and the obstacle.
class GameScene: SKScene, SKPhysicsContactDelegate {
let avatar = SKShapeNode(circleOfRadius: 20)
let avatarCategory: UInt32 = 0*1 << 0
let obstacleCategory: UInt32 = 0*1 << 1
override func didMove(to view: SKView) {
physicsWorld.contactDelegate = self
createAvatar()
spawnObstacles()
}
func createAvatar() {
avatar.name = "avatarNode"
avatar.physicsBody = SKPhysicsBody()
avatar.physicsBody?.categoryBitMask = avatarCategory
avatar.physicsBody?.contactTestBitMask = avatarCategory
avatar.physicsBody?.collisionBitMask = 0
avatar.physicsBody?.usesPreciseCollisionDetection = true
avatar.physicsBody?.affectedByGravity = false
avatar.zPosition = 2
addChild(avatar)
}
func createRandomObstacle() {
let obstacle = SKShapeNode()
obstacle.name = "obstacleNode"
obstacle.physicsBody = SKPhysicsBody()
obstacle.physicsBody?.categoryBitMask = obstacleCategory
obstacle.physicsBody?.contactTestBitMask = obstacleCategory
obstacle.physicsBody?.collisionBitMask = 0
obstacle.physicsBody?.usesPreciseCollisionDetection = true
obstacle.physicsBody?.affectedByGravity = false
obstacle.zPosition = 2
addChild(obstacle)
}
func didBegin(_ contact: SKPhysicsContact) {
print("collision")
}
To start with, both avatarCategory and obstacleCategory are 0, because : UInt32 = 0*1 << 1 = 0, so let’s fix that:
let avatarCategory: UInt32 = 1 << 0
let obstacleCategory: UInt32 = 1 << 1
Now contactTestBitMask represents the object(s) you want to be notified about contacts with, so you need to change that:
avatar.physicsBody?.contactTestBitMask = obstacleCategory
and
obstacle.physicsBody?.contactTestBitMask = avatarCategor
Try that for now 😀
Edit: my step-by-step guide for collisions and contacts:
https://stackoverflow.com/a/51041474/1430420
And a guide to collision and contactTest bit masks:
https://stackoverflow.com/a/40596890/1430420

getting the sprite attached to a SKPhysicsBody

I'm animating a sprite, and when it touches another one, a physics contact function is called. Than, in this function, I'm trying to get the sprite with touched the other. Its body is bodyA but it's a SKPhysicsBody, and it can not be converted as a SKSpriteNode. Do you have any ideas?
The function is correctly called when contact, I just try to get the sprite which body makes the contact. The final idea is getting the actions attached to the sprite with I'm looking for but I suppose when you have the sprite it's easy.
let shootCategory: UInt32 = 0x1 << 0
let enemyCategory: UInt32 = 0x1 << 1
// Declaration of the SKPhysicsBody of the sprite wich will touch the other one
sprite.physicsBody = SKPhysicsBody(circleOfRadius: (20))
sprite.physicsBody?.usesPreciseCollisionDetection = true
sprite.physicsBody?.categoryBitMask = shootCategory
sprite.physicsBody?.collisionBitMask = shootCategory | enemyCategory
sprite.physicsBody?.contactTestBitMask = shootCategory | enemyCategory
sprite.physicsBody?.affectedByGravity = false
// Declaration of the SKPhysicsBody of the sprite wich will be touched
sprite.run(SKAction.group([moveAction, fadeInAction]))
sprite.physicsBody = SKPhysicsBody(circleOfRadius: (20))
sprite.physicsBody?.usesPreciseCollisionDetection = true
sprite.physicsBody?.categoryBitMask = enemyCategory
sprite.physicsBody?.collisionBitMask = shootCategory | enemyCategory
sprite.physicsBody?.contactTestBitMask = shootCategory | enemyCategory
sprite.physicsBody?.affectedByGravity = false
func didBegin(_ contact: SKPhysicsContact) {
if (contact.bodyA.categoryBitMask == enemyCategory) && (contact.bodyB.categoryBitMask == shootCategory){
// I tried this to get the sprite wich the bodyB is attached to but it doesn't even build
let sprite: SKSpriteNode = sprite.contact.bodyB
contact.bodyA.removeAction(forKey: "moveToAction")
}
}
I finally found the solution with is really simple:
let Node = contact.bodyB.node as! SKSpriteNode

Easy SpriteKit Contact Detection (Space Shooter Game) Not Wotking Properly

I'm trying to make a simple Space Shooter game. The contact should happen either between the torpedo and the alien or the shuttle and the alien. The problem is that this second contact (shuttle vs. alien) only happens after the first kind of contact has happend (torpedo vs. alien) and further more they're not always precise. This is a struct created outside the class
struct PhysicsCategory {
static let alien : UInt32 = 1
static let torpedo : UInt32 = 2
static let shuttle : UInt32 = 3 }
Shuttle:
shuttle.physicsBody = SKPhysicsBody(rectangleOfSize: shuttle.size)
shuttle.physicsBody?.categoryBitMask = PhysicsCategory.shuttle
shuttle.physicsBody?.contactTestBitMask = PhysicsCategory.alien
shuttle.physicsBody?.dynamic = false
shuttle.physicsBody?.affectedByGravity = false
Torpedo:
torpedo.physicsBody = SKPhysicsBody(rectangleOfSize: torpedo.size)
torpedo.physicsBody?.categoryBitMask = PhysicsCategory.torpedo
torpedo.physicsBody?.contactTestBitMask = PhysicsCategory.alien
torpedo.physicsBody?.affectedByGravity = false
torpedo.physicsBody?.dynamic = false
Alien:
alien.physicsBody = SKPhysicsBody(rectangleOfSize: torpedo.size)
alien.physicsBody?.categoryBitMask = PhysicsCategory.alien
alien.physicsBody?.contactTestBitMask = PhysicsCategory.torpedo
alien.physicsBody?.affectedByGravity = false
alien.physicsBody?.dynamic = true
Finally, here's my contact code:
func didBeginContact(contact: SKPhysicsContact) {
var firstBody : SKPhysicsBody = contact.bodyA
var secondBody : SKPhysicsBody = contact.bodyB
if ((firstBody.categoryBitMask == PhysicsCategory.alien) && (secondBody.categoryBitMask == PhysicsCategory.torpedo)) ||
((firstBody.categoryBitMask == PhysicsCategory.torpedo) && (secondBody.categoryBitMask == PhysicsCategory.alien)) {
self.contactWithTorpedo(firstBody.node as! SKSpriteNode, torpedo: secondBody.node as! SKSpriteNode)
} else if ((firstBody.categoryBitMask == PhysicsCategory.shuttle) && (secondBody.categoryBitMask == PhysicsCategory.alien)) {
self.contactWithShuttle(firstBody.node as! SKSpriteNode, shuttle: secondBody.node as! SKSpriteNode)
}
}
func contactWithTorpedo (alien: SKSpriteNode, torpedo : SKSpriteNode) {
alien.removeFromParent()
torpedo.removeFromParent()
score++
scoreLabel.text = "score: " + "\(score)"
}
func contactWithShuttle (alien:SKSpriteNode, shuttle:SKSpriteNode) {
alien.removeFromParent()
shuttle.removeFromParent()
self.view?.presentScene(EndScene())
}
I'm not really sure where the problem is, plus I've seen a couple of tutorials do the same. I don't know if it's relevant by the way, but this is not an iOS game but an OSX. Thank you in advance!
You might find it less confusing to restructure your didBeginContact as follows, as this avoids the firstBody/secondbody stuff and the complicated if...then conditions to see what has contacted what:
func didBeginContact(contact: SKPhysicsContact) {
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
case PhysicsCategory.alien | PhysicsCategory.torpedo:
// alien and torpedo have contacted
contact.bodyA.removeFromParent()
contact.bodyB.removeFromParent()
score += 1
scoreLabel.text = "score: " + "\(score)"
case PhysicsCategory.alien | PhysicsCategory.shuttle:
// alien and shuttle have contacted
contact.bodyA.removeFromParent()
contact.bodyB.removeFromParent()
self.view?.presentScene(EndScene())
default :
//Some other contact has occurred
print("Some other contact")
}
}
You can just add as many PhysicsCategory.enemy | PhysicsCategory.player cases as you need for all the contacts that you have to take action for in your game. Code each potential contact individually and you won't loose yourself in if...then...else.
if you do need to reference only one of the nodes involved in a contact, (e.g. to remove the player after an enemy hits it), you can do it like this:
let playerNode = contact.bodyA.categoryBitMask == PhysicsCategory.player ? contact.bodyA.node! : contact.bodyB.node!
playernode.removefromParent
I would recommend you to read the the docs about SKPhysicsBody.
Every physics body in a scene can be assigned to up to 32 different categories, each corresponding to a bit in the bit mask. You define the mask values used in your game. In conjunction with the collisionBitMask and contactTestBitMask properties, you define which physics bodies interact with each other and when your game is notified of these interactions
First of all I would change the PhysicsCategory to
struct PhysicsCategory {
static let alien : UInt32 = 0x1 << 1
static let torpedo : UInt32 = 0x1 << 2
static let shuttle : UInt32 = 0x1 << 3
}
Then
alien.physicsBody?.contactTestBitMask = PhysicsCategory.torpedo | PhysicsCategory.shuttle
Hope this helps.
So I've actually managed to solve my problem yesterday. I'm posting the updated code in case it could help someone.
Outside the class:
struct PhysicsCategory {
static let player : UInt32 = 0x1 << 0
static let bullet : UInt32 = 0x1 << 1
static let enemy : UInt32 = 0x1 << 2}
And then, after applying the phyics to each sprite as i wrote before, inside the class:
//Contact with bullet
func contactWithBullet(enemy : SKSpriteNode, bullet: SKSpriteNode) {
enemy.removeFromParent()
bullet.removeFromParent()
score += 1
updateLabels()
}
//contact with player
func contactWithPlayer(player : SKSpriteNode, enemy : SKSpriteNode) {
enemy.removeFromParent()
lives -= 1
updateLabels() //another function that changes the score and lives labels
}
//CONTACT DETECTION
func didBeginContact(contact: SKPhysicsContact) {
let firstBody : SKPhysicsBody = contact.bodyA
let secondBody : SKPhysicsBody = contact.bodyB
if (firstBody.categoryBitMask == PhysicsCategory.enemy && secondBody.categoryBitMask == PhysicsCategory.bullet || firstBody.categoryBitMask == PhysicsCategory.bullet && secondBody.categoryBitMask == PhysicsCategory.enemy) {
contactWithBullet(firstBody.node as! SKSpriteNode, bullet: secondBody.node as! SKSpriteNode)
checkScore()
enemiesInWave -= 1
} else if (firstBody.categoryBitMask == PhysicsCategory.enemy && secondBody.categoryBitMask == PhysicsCategory.player || firstBody.categoryBitMask == PhysicsCategory.player && secondBody.categoryBitMask == PhysicsCategory.enemy) {
contactWithPlayer(firstBody.node as! SKSpriteNode, enemy: secondBody.node as! SKSpriteNode)
checkLives()
enemiesInWave -= 1
}
}

didBeginContact not called : this instance is unique to me

So, I am still experimenting with Sprite Kit for my first time ever, and I would like to test for collision. So, I searched around a bit in Apple's documentation, around Stack Overflow, online tutorials, and other forums. However, I was unable to find something a tip or code that makes what I am doing work. So, here are the relevant pieces of code:
This is the code for an obstacle:
func createObstacle(){
var ball = SKShapeNode(circleOfRadius: 20)
var width = UInt32(self.frame.width)
var random_number = arc4random_uniform(width)
ball.position = CGPointMake(CGFloat(random_number), frame.height+20)
ball.strokeColor = SKColor.blackColor()
ball.glowWidth = 1.0
ball.fillColor = SKColor.darkGrayColor()
ball.physicsBody = SKPhysicsBody(circleOfRadius: 20)
ball.physicsBody!.affectedByGravity = true
ball.physicsBody?.categoryBitMask = 6
ball.physicsBody?.dynamic = true
self.addChild(ball)
}
This is relevant code for the thing that it would collide with:
let circle = SKShapeNode(circleOfRadius: 20)
circle.physicsBody = SKPhysicsBody(circleOfRadius: 20)
circle.fillColor = SKColor.blueColor()
circle.strokeColor = SKColor.blueColor()
circle.glowWidth = 1.0
circle.physicsBody?.categoryBitMask = 4
circle.physicsBody?.dynamic = true
circle.physicsBody?.affectedByGravity = false
And this is the code for contact:
func didBeginContact(contact: SKPhysicsContact) {
var firstBody: SKPhysicsBody!
var secondBody: SKPhysicsBody!
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
}
else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if ((firstBody.categoryBitMask == 4 && secondBody.categoryBitMask == 6) || (firstBody.categoryBitMask == 6 && secondBody.categoryBitMask == 4)){
println("HI")
}else{println("NO")}
}
Sadly, nothing is being printed at all, so something's wrong. Any idea why this doesn't work?
Your class should have delegate SKPhysicsContactDelegate.
class GameScene: SKScene, SKPhysicsContactDelegate {
In didMoveToView write this:
physicsWorld.contactDelegate = self
EDIT
Define CategoryBitMask like this
struct PhysicsCategory {
static let circleCategory : UInt32 = 0b1 // 1
static let ballCategory : UInt32 = 0b10 // 2
}
Give CategoryBitMask to circle and ball
circle.physicsBody?.categoryBitMask = PhysicsCategory.circleCategory
ball.physicsBody?.categoryBitMask = PhysicsCategory.ballCategory
Then check contact like this:
(func didBeginContact(contact: SKPhysicsContact) {
if ((contact.bodyA.categoryBitMask == 0b1 && contact.bodyB.categoryBitMask == 0b10 ) || ( contact.bodyA.categoryBitMask == 0b1 && contact.BodyB.categoryBitMask == 0b1 ))
println("Contact")
}
}
Sorry for typos didnt used editor

iOS Swift didBeginContact not being called

I have been struggling for the past two days to get two SKSpriteNodes to register a collision and evoke didBegin#contact.
I've set their bit masks 'categoryBitMask', 'contactTestBitMask' and 'collisionTestBitMask' for both objects.
I've also set the 'dynamic' property for both to 'true'
initPhysics() seems to set up the physicsWorld okay.
All I'm expecting is that didBegin#Contact is called, but it is not
//Set up Physicsbody bit masks
let playerCarBitMask: UInt32 = 0x1 << 1
let slowCarBitMask: UInt32 = 0x1 << 2
//initPhysics
func initPhysics() {
println("(((((((((((((( Initiating Physicsbody ))))))))))))))")
self.physicsWorld.contactDelegate = self
self.physicsWorld.gravity = CGVector.zeroVector
println("self.physicsWorld.contactDelegate = \(self.physicsWorld.contactDelegate)")
}
//setupPlayer
func setupPlayer() {
car = SKSpriteNode(imageNamed: "redCarUp")
car.setScale(2.0)
car.position = CGPoint(x: 800, y: 400)
car.zPosition = 100
car.name = "car"
gameNode.addChild(car)
let carBody = SKPhysicsBody(
rectangleOfSize: car.frame.size, center: car.position)
carBody.dynamic = true
carBody.categoryBitMask = playerCarBitMask
carBody.contactTestBitMask = slowCarBitMask
carBody.mass = 5
carBody.collisionBitMask = slowCarBitMask
car.physicsBody = carBody
println("carBody = \(carBody)")
println("carBody.dynamic = \(carBody.dynamic)")
println("carBody.mass = \(carBody.mass)")
println("carBody.categoryBitMask = \(carBody.categoryBitMask)")
println("carBody.contactTestBitMask = \(carBody.contactTestBitMask)")
println("carBody.collisionBitMask = \(carBody.contactTestBitMask)")
slowCar = SKSpriteNode(imageNamed: "blueCarUp")
slowCar.setScale(2.0)
let slowCarScenePos = CGPoint(
x: 680,
y: 2048)
slowCar.position = gameNode.convertPoint(slowCarScenePos, fromNode: self)
println("slowCar.position = \(slowCar.position) ****")
slowCar.zPosition = 80
slowCar.name = "slowCar"
let slowCarBody = SKPhysicsBody(
rectangleOfSize: slowCar.frame.size, center: slowCar.position)
println("slowCar = \(slowCar) ****")
slowCarBody.dynamic = true
slowCarBody.categoryBitMask = slowCarBitMask
slowCarBody.contactTestBitMask = playerCarBitMask
slowCarBody.mass = 5
slowCarBody.collisionBitMask = playerCarBitMask
slowCar.physicsBody = slowCarBody
gameNode.addChild(slowCar)
}
func didBeginContact(contact: SKPhysicsContact!) {
println("*******************PhysicsContact********************")
}
'didBeginContact' has been changed to 'didBegin' in swift 3
func didBegin(_ contact: SKPhysicsContact) {
//stuff
}
I had a code from swift 2 and 'didBeginContact' was sitting there but wasn't being called. After quite a white I figured out that the function was changed. So, I thought my answer could help someone.
If you want make a contact between car and slowCar you have to init the categoryBitMask of both physicsBodies (I think you did). See the code below to get contact between two physicsBodies. When there is a contact it returns your display function :
//init your categoryBitMask :
let carCategory:UInt32 = 0x1 << 0
let SlowCarCategory:UInt32 = 0x1 << 1
//init car
car.physicsBody?.categoryBitMask = carCategory
car.physicsBody?.contactTestBitMask = slowCarCategory
//init slowCar
slowCar.physicsBody?.categoryBitMask = slowCarCategory
slowCar.physicsBody?.contactTestBitMask = CarCategory
// set your contact function
func didBeginContact(contact: SKPhysicsContact!)
{
var firstBody: SKPhysicsBody
var secondBody: SKPhysicsBody
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA
secondBody = contact.bodyB
}
else
{
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if ((firstBody.categoryBitMask & carCategory) != 0 && (secondBody.categoryBitMask & slowCarCategory) != 0)
{
displayfunction(firstBody.node as SKSpriteNode, car: secondBody.node as SKSpriteNode)
}
}
func displayFunction (slowCar : SKSpriteNode, car : SKSpriteNode)
It turned out to be a simple problem. In my original code I was setting parameters for the SKPhysicsBody detection frame like so:
let carBody = SKPhysicsBody(
rectangleOfSize: car.frame.size, center: car.position)
Similarly I was doing the same for the second node that I was testing physics collisions for.
Simply removing the 'centre:' parameters like so:
let carBody = SKPhysicsBody(rectangleOfSize: car.frame.size)
for the two sprite nodes solved the problem and the nodes now crash into each other and push themselves aside as expected.
Please Note that contact will not be detected between two static bodies
(node.physicsBody?.isDynamic = false)