When two of my SKSpriteNodes touch the didBeginContact function isn't called:
func didBegin(_ contact: SKPhysicsContact) {
let bodyA = contact.bodyA
let bodyB = contact.bodyB
print("Called")
if bodyA.categoryBitMask == 1 && bodyB.categoryBitMask == 2 || bodyA.categoryBitMask == 2 && bodyB.categoryBitMask == 1{
//End
print("contact")
_ = GameScene4(fileNamed: "GameScene4.sks")
If you are using Xcode8.0 Swift3.0 you should use this.
func didBegin(_ contact: SKPhysicsContact) {}
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")
}
The main question is: How to determine only one contact?
part of code:
extension GameScene : SKPhysicsContactDelegate {
func didBegin(_ contact: SKPhysicsContact) {
let bodyA = contact.bodyA.categoryBitMask
let bodyB = contact.bodyB.categoryBitMask
let ball = BitmaskCategory.ball
let bucket = BitmaskCategory.bucket
if bodyA == ball && bodyB == bucket || bodyA == bucket && bodyB == ball {
print("contact")
// block.run(SKAction.repeatForever(blockInstanse.rotateBlock(block: block)))
}
}}
When I put ball in bucket, i have that output because ball has bouncing effect.
Want to write some logic in that func but I can't because have several contacts.
Tried to change ball.physicsBody?.categoryBitMask in "if" condition but without success too.
Please help...
Your ball or bucket can set a flag on the first contact, you can check inside the didBegin and run action if is true, like:
var isFirstContact = true
func didBegin(_ contact: SKPhysicsContact) {
let bodyA = contact.bodyA.categoryBitMask
let bodyB = contact.bodyB.categoryBitMask
let ball = BitmaskCategory.ball
let bucket = BitmaskCategory.bucket
if bodyA == ball && bodyB == bucket || bodyA == bucket && bodyB == ball {
if isFirstContact {
isFirstContact = false
// block.run(SKAction.repeatForever(blockInstanse.rotateBlock(block: block)))
}
}
}}
I am trying to delete an enemy after 2 shots.Here is my didBegin
func didBegin(_ contact: SKPhysicsContact) {
var body1:SKPhysicsBody
var body2:SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
body1 = contact.bodyA
body2 = contact.bodyB
}else{
body1 = contact.bodyB
body2 = contact.bodyA
}
if body1.categoryBitMask == PhysicsCatagory.Bullet && body2.categoryBitMask == PhysicsCatagory.Enemy{
//if the bullet has hit the enemy
if body2.node != nil {
spawnSplatter(spawnPosition: body2.node!.position)
body1.node?.removeFromParent()
body2.node?.removeFromParent()
}
This deletes an enemy after being hit once, can someone tell me how I can delete an enemy after 2 hits?
Usually, try to remove nodes to didBegin(_ contact: it's not a good idea because you could have multiple contacts with nodes that become nil after the first contact and this could cause a crash.
var bulletCounter : Int = 0
var nodesToRemove = [SKNode]()
func didBegin(_ contact: SKPhysicsContact) {
var body1:SKPhysicsBody
var body2:SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
body1 = contact.bodyA
body2 = contact.bodyB
}else{
body1 = contact.bodyB
body2 = contact.bodyA
}
if body1.categoryBitMask == PhysicsCatagory.Bullet && body2.categoryBitMask == PhysicsCatagory.Enemy{
//if the bullet has hit the enemy
bulletCounter += 1
nodesToRemove.append(body1.node!) // remove always the bullet
switch bulletCounter {
case 2:
nodesToRemove.append(body2.node!) // remove enemy
bulletCounter = 0
default:break
}
}
}
override func didFinishUpdate()
{
nodesToRemove.forEach(){$0.removeFromParent()}
nodesToRemove = [SKNode]()
}
This example can be useful if you have 1 enemy.
If you have more enemies you can create a bulletCounter property to your Enemy class or store a bulletCounter value inside the SKNode userData property:
Create the dictionary first:
enemy.userData = NSMutableDictionary()
enemy.userData?.setObject(0, forKey: "bulletCounter")
Get/assign your value during the game:
if let bulletCounter = self.userData?.object(forKey: "bulletCounter") {
var counter = bulletCounter as! Int
counter += 1
self.userData?.setObject(counter, forKey: "bulletCounter" as NSCopying)
}
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