Regine
Regine

Reputation: 11

Node not being removed from parent (spritekit)

Create enemy

touchesBegan and didBegin contact function

My enemy node is not being removed from the scene every time my sword node touches it. I'm just wondering if anyone could explain to me what I'm doing wrong?

(UPDATE BELOW)

import SpriteKit
import GameplayKit
import AVFoundation

class LevelTwo: SKScene, SKPhysicsContactDelegate{
    var levelBg = SKSpriteNode(imageNamed: "level2")
    var hero = SKSpriteNode()
    var enemy = SKSpriteNode()
    var sword = SKSpriteNode()
    var health1 = SKSpriteNode(imageNamed: "playerhplv2")
    var health2 = SKSpriteNode(imageNamed: "playerhplv2")
    var health3 = SKSpriteNode(imageNamed: "playerhplv2")
    var musicPath = URL(fileURLWithPath: Bundle.main.path(forResource: "gameMusic", ofType: "mp3")!)
    var musicGamePlayer = AVAudioPlayer()
    var runMonster =  SKAction()
    var waitMonster =  SKAction()
    var sequenceMonster =  SKAction()
    var repeatMonster = SKAction()

    enum CollisionNum: UInt32{
        case swordNum = 1
        case enemyNum = 2
        case playerNum = 4
    }
    override func didMove(to view: SKView) {
         self.physicsWorld.contactDelegate = self

    ///music
    do{
        musicGamePlayer = try AVAudioPlayer(contentsOf: musicPath)
        musicGamePlayer.prepareToPlay()
        musicGamePlayer.numberOfLoops = -1
        musicGamePlayer.play()
    }
    catch{
        print(error)
    }
    //bg
    levelBg.position = CGPoint(x: 0, y: 0)
    levelBg.zPosition = 1
    levelBg.size = levelBg.texture!.size()
    levelBg.setScale(1.25)
    self.addChild(levelBg)

    //hero
    let playerTexture = SKTexture(imageNamed: "main")
    hero = SKSpriteNode(texture: playerTexture)
    hero.position  = CGPoint(x: 0, y: 0)
    hero.zPosition = 2
    hero.setScale(0.6)
    hero.physicsBody = SKPhysicsBody(texture: playerTexture, size: CGSize(width: hero.size.width, height: hero.size.height))
    hero.physicsBody!.categoryBitMask = CollisionNum.playerNum.rawValue
    hero.physicsBody!.collisionBitMask = CollisionNum.enemyNum.rawValue  //player is allowed to bump into rocks and skulls
    hero.physicsBody!.contactTestBitMask = CollisionNum.enemyNum.rawValue // same as collisions
    hero.physicsBody!.isDynamic = false
    self.addChild(hero)


    //health1
    health1.position = CGPoint(x: 130, y: 150)
    health1.zPosition = 3
    health1.setScale(0.75)
    self.addChild(health1)

    //health2
    health2.position = CGPoint(x: 230, y: 150)
    health2.zPosition = 3
    health2.setScale(0.75)
    self.addChild(health2)

    //health3
    health3.position = CGPoint(x: 320, y: 150)
    health3.zPosition = 3
    health3.setScale(0.75)
    self.addChild(health3)

    runMonster = SKAction.run(addMonster)
    waitMonster = SKAction.wait(forDuration: 0.3)
    sequenceMonster = SKAction.sequence([runMonster,waitMonster])
    repeatMonster = SKAction.repeatForever(sequenceMonster)
    run(repeatMonster)


}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    for touch in touches{
        let locale = touch.location(in: self)
        hero.position.x = locale.x
        hero.position.y = locale.y
    }
}
func addMonster(){
    //random position based off the bg size
    let monsterHigherX = Int(levelBg.size.width)
    let monsterHigherY = Int(levelBg.size.height)
    let monsterLowerX = monsterHigherX * -1
    let monsterLowerY = monsterHigherY * -1

    let randomLocaleX = Int(arc4random_uniform(UInt32(monsterHigherX - monsterLowerX))) + monsterLowerX
    let randomLocaleY = Int(arc4random_uniform(UInt32(monsterHigherY - monsterLowerY))) + monsterLowerY
    let movementEnemy = SKAction.moveBy(x: -5, y: -5, duration: 0.2)
    let movementForever = SKAction.repeatForever(movementEnemy)

    let enemyTexture = SKTexture(imageNamed: "boss0")
    enemy = SKSpriteNode(texture: enemyTexture)
    enemy.zPosition = 2
    enemy.setScale(0.5)
    enemy.position = CGPoint(x: randomLocaleX, y: randomLocaleY)
    enemy.physicsBody = SKPhysicsBody(texture: enemyTexture, size: CGSize(width: enemy.size.width, height: enemy.size.height))
    enemy.physicsBody!.isDynamic = true
    enemy.physicsBody!.affectedByGravity = false
    enemy.physicsBody!.categoryBitMask = CollisionNum.enemyNum.rawValue
    enemy.physicsBody!.collisionBitMask = CollisionNum.swordNum.rawValue
    enemy.physicsBody!.contactTestBitMask = CollisionNum.swordNum.rawValue
    enemy.run(movementForever)
    self.addChild(enemy)

}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let swordTexture = SKTexture(imageNamed: "blade-0")
        sword = SKSpriteNode(texture: swordTexture)
        sword.setScale(0.50)
        sword.zPosition = 2
        sword.position = hero.position
        sword.physicsBody = SKPhysicsBody(texture: swordTexture, size: CGSize(width: sword.size.width, height: sword.size.height))
        sword.physicsBody!.velocity = CGVector(dx: 1200, dy:0)
        sword.physicsBody!.isDynamic = true
        sword.physicsBody!.affectedByGravity = true
        sword.physicsBody!.usesPreciseCollisionDetection = true
        sword.physicsBody!.categoryBitMask = CollisionNum.swordNum.rawValue
        sword.physicsBody!.collisionBitMask = CollisionNum.enemyNum.rawValue
        sword.physicsBody!.contactTestBitMask = CollisionNum.enemyNum.rawValue
        self.addChild(sword)

}
func didBegin(_ contact: SKPhysicsContact) {
    let collision: UInt32 = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
    if  collision == CollisionNum.swordNum.rawValue | CollisionNum.enemyNum.rawValue {
        enemy.removeFromParent()
    }
}
override func update(_ currentTime: TimeInterval) {
}

}

(Ive attached my whole level2 class) Thank you so much for the suggestion; however, when I tried implementing this I still run into the same problem (im running this on the iphone simulator) Im wondering whether the error is with my enum or my implementation of my physics with my nodes

Upvotes: 0

Views: 112

Answers (2)

Knight0fDragon
Knight0fDragon

Reputation: 16847

You do not want to remove "enemy" because "enemy" is always the last monster you added. You need to check which contactBody is the enemy so you can remove it. You can do that by guaranteeing which node you want to associate as A, and which you want to associate as B by looking at the categoryBitMask value:

func didBegin(_ contact: SKPhysicsContact) {
    //This guarantees the lower categoryBitMask (Providing you are only using one) is in A
    let bodyA = contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask ? contact.bodyA : contact.bodyB
    let bodyB = contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask  ? contact.bodyB : contact.bodyA   


    if  bodyA.categoryBitMask == CollisionNum.swordNum.rawValue && bodyB.categoryBitMask == CollisionNum.enemyNum.rawValue {
        bodyB.node.removeFromParent()
    }
}

Of course this will lead to problems with multiple collisions, so instead you may want to do:

var removeNodes = SKNode()
func didBegin(_ contact: SKPhysicsContact) {
    //This guarantees the lower categoryBitMask (Providing you are only using one) is in A
    let bodyA = contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask ? contact.bodyA : contact.bodyB
    let bodyB = contact.bodyA.categoryBitMask > contact.bodyB.categoryBitMask  ? contact.bodyB : contact.bodyA   


    if  bodyA.categoryBitMask == CollisionNum.swordNum.rawValue && bodyB.categoryBitMask == CollisionNum.enemyNum.rawValue {
        bodyB.node.moveToParent(removeNodes)
    }
}

func didFinishUpdate(){
   removeNodes.removeAllChildren()
}

Upvotes: 0

George
George

Reputation: 30501

Try this:

func didBegin(_ contact: SKPhysicsContact) {
    let collision: UInt32 = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask

    if collision == CollisionNum.swordNum.rawValue | CollisionNum.enemyNum.rawValue {
        enemy.removeFromParent()
    }
}

You were only testing if bodyA is equal to the enemy, however, bodyA may be equal to the sword instead.

Upvotes: 0

Related Questions