I have 4 physics bodies that are all detecting collisions well. However, there is two physics bodies that will not detect when they collide with each other. They do detect when they collide with other physics bodies though. I have contacttestbitmasks for all the them so I do not understand why there is a problem. Here is some code:
Here is where I setup my physics bodies:
struct PhysicsCategory{
static let None : UInt32 = 0
static let All : UInt32 = UInt32.max
static let player : UInt32 = 0b1
static let bounce : UInt32 = 0b10
static let blueball : UInt32 = 0b100
static let redball : UInt32 = 0b1000
static let coin : UInt32 = 0b10000
}
Here is the code where I used for setting up the player physics body which is one of the problem physic bodies:
player.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: player.size.width-40, height: player.size.height-40))
player.physicsBody?.isDynamic = true
player.physicsBody?.categoryBitMask = PhysicsCategory.player
player.physicsBody?.contactTestBitMask = PhysicsCategory.blueball
player.physicsBody?.contactTestBitMask = PhysicsCategory.redball
player.physicsBody?.collisionBitMask = PhysicsCategory.None
Here is the func for detecting collisions:
func didBegin(_ 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 & PhysicsCategory.player != 0) &&
(secondBody.categoryBitMask & PhysicsCategory.redball != 0)) {
RedballDidCollideWithPlayer(player: firstBody.node as! SKSpriteNode, redball: secondBody.node as! SKSpriteNode)
}
if ((firstBody.categoryBitMask & PhysicsCategory.player != 0) &&
(secondBody.categoryBitMask & PhysicsCategory.blueball != 0)) {
BlueballDidCollideWithPlayer(player: firstBody.node as! SKSpriteNode, blueball: secondBody.node as! SKSpriteNode)
}
if ((firstBody.categoryBitMask & PhysicsCategory.bounce != 0) &&
(secondBody.categoryBitMask & PhysicsCategory.redball != 0)){
RedballDidCollideWithBounce(bounce: firstBody.node as! SKSpriteNode, redball: secondBody.node as! SKSpriteNode)
}
if ((firstBody.categoryBitMask & PhysicsCategory.bounce != 0) &&
(secondBody.categoryBitMask & PhysicsCategory.blueball != 0)) {
BlueballDidCollideWithBounce(bounce: firstBody.node as! SKSpriteNode, blueball: secondBody.node as! SKSpriteNode)
}
if ((firstBody.categoryBitMask & PhysicsCategory.bounce != 0) &&
(secondBody.categoryBitMask & PhysicsCategory.coin != 0)) {
coinDidCollideWithplayer(player: firstBody.node as! SKSpriteNode, coin: secondBody.node as! SKSpriteNode)
}
if ((firstBody.categoryBitMask & PhysicsCategory.player != 0) &&
(secondBody.categoryBitMask & PhysicsCategory.coin != 0)) {
coinDidCollideWithplayer(player: firstBody.node as! SKSpriteNode, coin: secondBody.node as! SKSpriteNode)
}
}
Here is the code I used for setting up the blueball. This is the other physics body having problems:
let blueball = SKSpriteNode(imageNamed: "blueball")
blueball.position = enemyb.position
blueball.physicsBody = SKPhysicsBody(circleOfRadius: blueball.size.width/2)
blueball.physicsBody?.isDynamic = true
blueball.physicsBody?.categoryBitMask = PhysicsCategory.blueball
blueball.physicsBody?.contactTestBitMask = PhysicsCategory.player
blueball.physicsBody?.contactTestBitMask = PhysicsCategory.bounce
blueball.physicsBody?.collisionBitMask = PhysicsCategory.None
blueball.physicsBody?.usesPreciseCollisionDetection = true
addChild(blueball)
let actionMove = SKAction.move(to: player.position, duration: 2)
Any ideas here would be helpful. I have been trying to find the problem for a few days with no luck.
When setting multiple categories you have to OR the values together. Your code
player.physicsBody?.contactTestBitMask = PhysicsCategory.blueball
player.physicsBody?.contactTestBitMask = PhysicsCategory.redball
Just sets the category twice, the second one overwriting the first. Change this to:
player.physicsBody?.contactTestBitMask = PhysicsCategory.blueball | PhysicsCategory.redball
Related
func didBegin(_ 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 & CollisionTypes.enemy.rawValue != 0) && (secondBody.categoryBitMask & CollisionTypes.bullet.rawValue != 0)) {
if let enemy = firstBody.node as? SKSpriteNode,
let bullet = secondBody.node as? SKSpriteNode {
bulletCollidedWithEnemy(bulletNode: bullet, enemyNode: enemy)
}
}
else if ((firstBody.categoryBitMask & CollisionTypes.enemy.rawValue != 0) && (secondBody.categoryBitMask & CollisionTypes.player.rawValue != 0)) {
if let enemy = firstBody.node as? SKSpriteNode,
let player = secondBody.node as? SKSpriteNode {
enemyCollidedWithPlayer(enemyNode: enemy, playerNode: player)
}
}
}
My enemy and bullet both get removed after contact and update my score but my player isnt. I dont know if that last bit of code with player is wrong or its something else.
func bulletCollidedWithEnemy( bulletNode:SKSpriteNode, enemyNode:SKSpriteNode) {
bulletNode.removeFromParent();
enemyNode.removeFromParent();
score += 1;
}
func enemyCollidedWithPlayer( enemyNode:SKSpriteNode, playerNode:SKNode) {
enemyNode.removeFromParent();
playerNode.removeFromParent();
self.updateHighestScore(score: self.score);
// #todo: Better pause handling
pause();
score = 0;
}
It points to enemy and missile but I had to add the second part because i need player. Its inside didbegin so i thought this was the only way for it work. Did i write it wrong or should I just remove the whole player else if code
I already have my categorybitmasks setup hopefully correctly but my code below should have my bullet and enemy contact and then remove each other. Nothing happens, they just collide and still stay on the screen
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 & bulletCategory) != 0 &&
(secondBody.categoryBitMask & enemyCategory) != 0 {
destroyEnemy(bullet: firstBody.node as! SKSpriteNode, enemy: secondBody.node as! SKSpriteNode)
}
}
func destroyEnemy(bullet: SKSpriteNode, enemy: SKSpriteNode) {
bullet.removeFromParent()
enemy.removeFromParent()
}
enemy.physicsBody?.categoryBitMask = CollisionTypes.enemy.rawValue
enemy.physicsBody?.contactTestBitMask = CollisionTypes.bullet.rawValue
enemy.physicsBody?.collisionBitMask = CollisionTypes.bullet.rawValue
bullet.physicsBody?.categoryBitMask = CollisionTypes.bullet.rawValue
bullet.physicsBody?.contactTestBitMask = CollisionTypes.enemy.rawValue
bullet.physicsBody?.collisionBitMask = CollisionTypes.enemy.rawValue
This isn't an answer per se, but is a bit long for a comment:
In your didBegin you have:
if (firstBody.categoryBitMask & bulletCategory) != 0 &&
(secondBody.categoryBitMask & enemyCategory) != 0 {
destroyEnemy(bullet: firstBody.node as! SKSpriteNode, enemy: secondBody.node as! SKSpriteNode)
What happens if firstBody is the enemy and secondBody is the bullet? It would appear that the code to be run when the contact occurs won't fire. Without seeing your categoryBitMask definitions we won't know because you appear to be assigning first and second body in ascending order of the categoryBitMask value.
You could confirm if this is working with another print inside the if block.
I've never really liked this form of didBegin and I personally find this style more readable:
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 bulletCategory | enemyCategory:
print("bullet and enemy have contacted.")
let bulletNode = contact.bodyA.categoryBitMask == bulletCategory ? contact.bodyA.node : contact.bodyB.node
let enemyNode = contact.bodyA.categoryBitMask == enemyCategory ? contact.bodyA.node : contact.bodyB.node
destroyEnemy(bullet: bulletNode, enemy: enemyNode)
case playerCategory | enemyCategory:
// Handle player/Enemy collisions
default:
print("Some other contact occurred")
}
I've managed to make contact between the bullets and enemies but for some reason the main character won't make contact with the enemies.
func didBegin(_ 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 & Bullet) != 0 && (secondBody.categoryBitMask & Enemy) != 0{
bulletDidCollideWithEnemy(bulletNode: firstBody.node as! SKSpriteNode, Zombie: secondBody.node as! SKSpriteNode)
}else{
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if (firstBody.categoryBitMask & Player) != 0 && (secondBody.categoryBitMask & Enemy) != 0{
playerCollideWithEnemy(playerNode: firstBody.node as! SKSpriteNode, Zombie: secondBody.node as! SKSpriteNode)
}
}
For anyone who is having this same problem as I did, here is the solution to the problem. I just had to make sure that "Enemy" was visible to other objects. Huge importance! Hope this helps somebody out.
Enemy.physicsBody?.affectedByGravity = false
Enemy.physicsBody?.isDynamic = true
When I run this code the first CollisionWithplayer line gives me a bad instruction error. The error doesn't appear every time, only every once in a while with no similar conditions to identify what is causing it.
func didBeginContact(contact: SKPhysicsContact) {
let firstBody : SKPhysicsBody = contact.bodyA
let secondBody : SKPhysicsBody = contact.bodyB
if ((firstBody.categoryBitMask == PhysicsCategory.Goblin) && (secondBody.categoryBitMask == PhysicsCategory.Bullet) ||
(firstBody.categoryBitMask == PhysicsCategory.Bullet) && (secondBody.categoryBitMask == PhysicsCategory.Goblin))
{
CollisionWithBullet(firstBody.node as! SKSpriteNode, Bullet: secondBody.node as! SKSpriteNode)
}
else if ((firstBody.categoryBitMask == PhysicsCategory.Goblin) && (secondBody.categoryBitMask == PhysicsCategory.player) ||
(firstBody.categoryBitMask == PhysicsCategory.player) && (secondBody.categoryBitMask == PhysicsCategory.Goblin)){
CollisionWithplayer(firstBody.node as! SKSpriteNode, player: secondBody.node as! SKSpriteNode)
}
func CollisionWithBullet(Goblin: SKSpriteNode, Bullet:SKSpriteNode){
Goblin.removeFromParent()
Bullet.removeFromParent()
score += 1
ScoreLbl.text = "\(score)"
var explosion = SKEmitterNode(fileNamed: "Goblin Death Animation.sks")
explosion!.particlePosition = Goblin.position
self.addChild(explosion!)
var fire = SKEmitterNode(fileNamed: "Goblin Death Animation 2.sks")
fire!.particlePosition = Goblin.position
self.addChild(fire!)
}
func CollisionWithplayer(Goblin: SKSpriteNode, player: SKSpriteNode){
let ScoreDefault = NSUserDefaults.standardUserDefaults()
ScoreDefault.setValue(score, forKey: "Score")
ScoreDefault.synchronize()
if (score > Highscore){
let HighscoreDefault = NSUserDefaults.standardUserDefaults()
HighscoreDefault.setValue(score, forKey: "Highscore")
}
Goblin.removeFromParent()
player.removeFromParent()
self.view?.presentScene(EndScene())
ScoreLbl.removeFromSuperview()
}
I assume you get an error because your code doesn't treat the case where 1 collision causes the didBeginContact method to fire more than once (collision happened at 2 points of same node)
I would rewrite your code like this to avoid such a case (using optionals). Furthermore I slightly rewrote it so you don't have to write 2 if statements for each collision.
func didBeginContact(contact: SKPhysicsContact) {
let firstBody: SKPhysicsBody
let 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 == PhysicsCategory.Goblin) && (secondBody.categoryBitMask == PhysicsCategory.Bullet) {
collisionWithBullet(firstBody.node as? SKSpriteNode, bullet: secondBody.node as? SKSpriteNode)
}
if (firstBody.categoryBitMask == PhysicsCategory.Goblin) && (secondBody.categoryBitMask == PhysicsCategory.player) {
collisionWithPlayer(firstBody.node as? SKSpriteNode, player: secondBody.node as? SKSpriteNode)
}
}
func collisionWithBullet(goblin: SKSpriteNode?, bullet:SKSpriteNode?){
guard let goblin = goblin, bullet = bullet else { return }
goblin.removeFromParent()
bullet.removeFromParent()
score += 1
scoreLbl.text = "\(score)"
if let explosion = SKEmitterNode(fileNamed: "Goblin Death Animation.sks") {
explosion.particlePosition = goblin.position
self.addChild(explosion)
}
if let fire = SKEmitterNode(fileNamed: "Goblin Death Animation 2.sks") {
fire.particlePosition = goblin.position
self.addChild(fire)
}
}
func collisionWithPlayer(goblin: SKSpriteNode?, player: SKSpriteNode?){
guard let goblin = goblin, player = player else { return }
let scoreDefault = NSUserDefaults.standardUserDefaults()
scoreDefault.setValue(score, forKey: "Score")
// synchronised not needed anymore
if (score > highscore){
let highscoreDefault = NSUserDefaults.standardUserDefaults()
highscoreDefault.setValue(score, forKey: "Highscore")
}
goblin.removeFromParent()
player.removeFromParent()
self.view?.presentScene(EndScene())
scoreLbl.removeFromSuperview()
}
Please also follow the swift guidlines, your methods and properties should start will small letters not will capital letters.
Hope this helps
I have a problem with my func contact because the second contact don't work (contact with Bonus and Vaisseau) but the two others contact don't work.
So this is my code :
The function :
func didBeginContact(contact: SKPhysicsContact) {
let PremierBody : SKPhysicsBody = contact.bodyA
let SecondBody : SKPhysicsBody = contact.bodyB
if ((PremierBody.categoryBitMask == PhysicsCategories.Meteorites) && (SecondBody.categoryBitMask == PhysicsCategories.Meteorites)) {
contactEntreMeteorites(PremierBody.node as! SKSpriteNode, Meteorites2: SecondBody.node as! SKSpriteNode)
}
else if ((PremierBody.categoryBitMask == PhysicsCategories.Bonus) && (SecondBody.categoryBitMask == PhysicsCategories.Vaisseau) ||
(PremierBody.categoryBitMask == PhysicsCategories.Vaisseau) && (SecondBody.categoryBitMask == PhysicsCategories.Bonus)){
gameOver(PremierBody.node as! SKSpriteNode, Vaisseau: SecondBody.node as! SKSpriteNode)
print("CONTACT")
}
else if ((PremierBody.categoryBitMask == PhysicsCategories.Meteorites) && (SecondBody.categoryBitMask == PhysicsCategories.Vaisseau) ||
(PremierBody.categoryBitMask == PhysicsCategories.Vaisseau) && (SecondBody.categoryBitMask == PhysicsCategories.Meteorites)){
gameOver(PremierBody.node as! SKSpriteNode, Vaisseau: SecondBody.node as! SKSpriteNode)
print("Couco")
}
}
And this is my Physics categories :
struct PhysicsCategories {
static let Meteorites : UInt32 = 1
static let Bonus : UInt32 = 2
static let Vaisseau : UInt32 = 5
}
This in my func DidMoveToView :
Vaisseau = SKSpriteNode(texture: Vaisseau1)
Vaisseau.position = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2)
Vaisseau.physicsBody = SKPhysicsBody(rectangleOfSize: Vaisseau.size)
Vaisseau.physicsBody?.affectedByGravity = false
Vaisseau.physicsBody?.categoryBitMask = PhysicsCategories.Vaisseau
Vaisseau.physicsBody?.contactTestBitMask = PhysicsCategories.Bonus
Vaisseau.physicsBody?.contactTestBitMask = PhysicsCategories.Meteorites
Vaisseau.physicsBody?.dynamic = false
Vaisseau.setScale(0.08)
self.addChild(Vaisseau)
And i create my SpriteNode Bonus in this function :
func ApparitionBonus() {
let BonusSprite = SKSpriteNode(imageNamed: "Bonus.png")
var BonusApparitionX = UInt32(self.frame.size.width)
var BonusApparitionY = UInt32(self.frame.size.height)
BonusApparitionX = arc4random() % BonusApparitionX
BonusApparitionY = arc4random() % BonusApparitionY
BonusSprite.position = CGPointMake(CGFloat(BonusApparitionX),CGFloat(BonusApparitionY))
BonusSprite.setScale(0.8)
BonusSprite.physicsBody = SKPhysicsBody(circleOfRadius: 20)
BonusSprite.physicsBody?.affectedByGravity = false
BonusSprite.physicsBody?.categoryBitMask = PhysicsCategories.Bonus
BonusSprite.physicsBody?.contactTestBitMask = PhysicsCategories.Vaisseau
BonusSprite.physicsBody?.dynamic = false
self.addChild(BonusSprite)
let RotationBonus = SKAction.rotateByAngle(CGFloat(M_PI), duration: 3)
let wait = SKAction.waitForDuration(3)
let actionFini = SKAction.removeFromParent()
BonusSprite.runAction(SKAction.sequence([RotationBonus, wait, actionFini]))
BonusSprite.runAction(SKAction.repeatActionForever(RotationBonus))
}
In your didMoveToView, when you call:
Vaisseau.physicsBody?.contactTestBitMask = PhysicsCategories.Bonus
Vaisseau.physicsBody?.contactTestBitMask = PhysicsCategories.Meteorites
You set the contactTestBitMask to one category, and then another. You need to bitwise or them together. Something like:
Vaisseau.physicsBody?.contactTestBitMask = PhysicsCategories.Bonus | PhysicsCategories.Meteorites
This will combine the two bit masks, which will allow it to contact both the Bonus and Meteorites.
If you don't want your sprites to collide, set the collisionBitMask to 0x0 (or however you want to represent 0):
Vaisseau.physicsBody?.collisionBitMask = 0x0