I am getting the node attached to a contact body (SKPhysicsBody), in a function called while detecting a contact between to SKSpriteNode. Sometimes, I get an error because it can not get the node attached to the contact body, so I test if there's a node to avoid that type of error. But a warning tells me it will never return false. I've not had anymore error like this since I test that but I'm not sure if it works well or if it can still happen. Do you have any idea?
//The actual code
func didBegin(_ contact: SKPhysicsContact) {
if (contact.bodyA.categoryBitMask == enemyCategory) && (contact.bodyB.categoryBitMask == shootCategory){ //test if it is the contact with I want to catch
let shootNode = contact.bodyB.node, shootNode != nil { // I test if there's a node attached to the enemyShootNode but it's supposed never to return false
let enemyNode = contact.bodyA.node, enemyNode != nil { // I test if there's a node attached to the shootNode but it's supposed never to return false
let enemySKNode = enemyNode as? SKSpriteNode
let shootSKNode = shootNode as? SKSpriteNode
// code
}
}
}
}
// The code with error
func didBegin(_ contact: SKPhysicsContact) {
if (contact.bodyA.categoryBitMask == enemyCategory) && (contact.bodyB.categoryBitMask == shootCategory){
let shootNode = contact.bodyB.node!
let enemyNode = contact.bodyA.node! // The error occurs here : "fatal error: unexpectedly found nil while unwrapping an Optional value"
let enemySKNode = enemyNode as? SKSpriteNode
let shootSKNode = shootNode as? SKSpriteNode
}
}
Thanks for your advices, the error indeed comes from the SKPhysics property (the didBegin function is called several times) but I didn't manage to fix.
Although I corrected the way I was checking if a sprite was here, and there are no more warnings or errors so I suppose it works well :
func didBegin(_ contact: SKPhysicsContact) {
if (contact.bodyA.categoryBitMask == enemyCategory) && (contact.bodyB.categoryBitMask == shootCategory) || (contact.bodyA.categoryBitMask == shootCategory) && (contact.bodyB.categoryBitMask == enemyCategory){
if var shootNode = contact.bodyB.node{
if var enemyNode = contact.bodyA.node{
// If the enemyNode and the shootNode don't respectively correspond to
if contact.bodyA.categoryBitMask == shootCategory {
shootNode = contact.bodyA.node!
enemyNode = contact.bodyB.node!
}
let enemySKNode = enemyNode as? SKSpriteNode
let shootSKNode = shootNode as? SKSpriteNode
}
}
}
}
Related
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 making a sprite based game. While applying physics to my coin sprites, I have begun to encounter this error. Any suggestions on how to avoid it ?
#objc func spawnEnemy(){
enemy = childNode(withName: "enemy") as? SKSpriteNode
coin = childNode(withName: "coin") as? SKSpriteNode
self.physicsWorld.contactDelegate = self
and later
for coin in sprites{
if coin.name == "coin"{
coin.physicsBody = SKPhysicsBody(rectangleOf: enemy.size)
coin.physicsBody?.categoryBitMask = PhysicsCategory.coin
coin.physicsBody?.contactTestBitMask = PhysicsCategory.player
coin.physicsBody?.affectedByGravity = false
coin.physicsBody?.isDynamic = true
}
}
You should always safely unwrap optional values by using "if let" or "guard let"
by using if let :
if let coin = coin , let name = coin.name {
// you can use "coin" or "name" safely here
}else{
// "coin" or "name" is nil
}
by using guard let :
guard let coin = coin , let name = coin.name else {
// "coin" or "name" is nil
return
}
//you can use "coin" or "name" safely here
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 been stuck in this part, I have been trying to removeFromParent but it doesnt work. In my game when an enemy colide 3 times with the player the game is over, the problem is that the enemies continue coming for some seconds and then dissapear but continue makeing damage to the player.
func didBeginContact(contact: SKPhysicsContact) {
let body1 = contact.bodyA.node as! SKSpriteNode
let body2 = contact.bodyB.node as! SKSpriteNode
if ((body1.name == "circuloPrincipal") && (body2.name == "enemigo")) {
colisionPrincipal(body2)
}else {
((body1.name == "enemigo") && (body2.name == "circuloPrincipal"))
colisionPrincipal(body1)
}
}
func colisionPrincipal(enemigo: SKSpriteNode) {
if hits < 2 && circuloPrincipal.color != enemigo.color{
shakeFrame(scene!)
circuloPrincipal.runAction(SKAction.scaleBy(1.5, duration:0.5))
enemigo.removeFromParent()
let particula = SKEmitterNode(fileNamed: "particulas.sks")
particula?.position = enemigo.position
particula?.hidden = false
particula?.runAction(SKAction.fadeOutWithDuration(0.8))
self.addChild(particula!)
hits += 1
}else if circuloPrincipal.color == enemigo.color {
enemigo.physicsBody?.affectedByGravity = false
enemigo.physicsBody?.dynamic = true
enemigo.removeFromParent()
score += 1
scoreLabel.text = "\(score)"
}else {
shakeFrame(scene!)
gameStarted = false
enemigo.removeFromParent()
enemigoTimer.invalidate()
highscoreLabel.runAction(SKAction.fadeInWithDuration(0.5))
if score > highscore {
let highscoreDefault = NSUserDefaults.standardUserDefaults()
highscore = score
highscoreDefault.setInteger(highscore, forKey: "highscore")
highscoreLabel.text = "Best: \(highscore)"
}
}
}
OK, so I don't have all that much information about your game to go by, but if you don't want the enemies to keep damaging the player the quick and dirty fix would be to add a guard to the top of your didBeginContact function:
func didBeginContact(contact: SKPhysicsContact) {
guard hits < 3 else {
return
}
let body1 = contact.bodyA.node as! SKSpriteNode
let body2 = contact.bodyB.node as! SKSpriteNode
if ((body1.name == "circuloPrincipal") && (body2.name == "enemigo")) {
colisionPrincipal(body2)
}else {
((body1.name == "enemigo") && (body2.name == "circuloPrincipal"))
colisionPrincipal(body1)
}
}
This should at least prevent anything to get called based on contacts after the required number of hits have occurred. As for cleaning up the sprites, this sounds rather simple (removeFromParent at the appropriate time) but I haven't got a clue about how you handle your game-states so it's hard to comment any further.
Personally I'd trigger it from the update() function if the required state has happened...
I have different colored bars on the right side of my screen with other bars of the same colors generated randomly coming from the left side that are to be matched with the static ones on the right. When matching colors collide, they are incrementing my score label and removing from the scene perfectly fine, but when the wrong ones match up, nothing happens. Ive even set up a print("") statement and its not being called. Here is my code for didBeginContact:
func didBeginContact(contact: SKPhysicsContact) {
if let firstBody = ((contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask) ? contact.bodyA.node : contact.bodyB.node) as! SKSpriteNode? {
if let secondBody = ((contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask) ? contact.bodyB.node : contact.bodyA.node) as! SKSpriteNode? {
if firstBody.color == secondBody.color {
label.text = "\(points)"
points++
firstBody.removeFromParent()
}
if firstBody.color != secondBody.color {
gameEnd()
print("didn't match")
}
}
}
Your code seems to be an unorganized mess, give this a try:
func didBeginContact(contact: SKPhysicsContact) {
if let firstBody = ((contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask) ? contact.bodyA.node : contact.bodyB.node) as! SKSpriteNode?, secondBody = ((contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask) ? contact.bodyB.node : contact.bodyA.node) as! SKSpriteNode? {
if firstBody.color == secondBody.color {
label.text = "\(points)"
points++
firstBody.removeFromParent()
}
else{
print("didn't match")
gameEnd()
}
}
}