0
votes

So I have two objects that should lose health points at an collision.

func addPlayer(xPos: CGFloat, yPos: CGFloat){
    playerNode = SKSpriteNode(imageNamed: "player")

    playerNode.physicsBody = SKPhysicsBody(circleOfRadius: width/2)
    playerNode.physicsBody!.affectedByGravity = false
    playerNode.physicsBody!.categoryBitMask = PhysicsCategory.Player
    playerNode.physicsBody!.contactTestBitMask = PhysicsCategory.Wall | PhysicsCategory.Zombie
    playerNode.physicsBody!.collisionBitMask = PhysicsCategory.Wall | PhysicsCategory.Zombie
    playerNode.name = "Player"

    player = Player(node: playerNode, healthPoints: 100, attack: 10)
    playerNode.position.x = xPos
    playerNode.position.y = yPos
    playerNode.size = CGSize(width: width, height: width)
    addChild(playerNode)
}

func addZombie(xPos: CGFloat, yPos: CGFloat){

    zombieNode = SKSpriteNode(imageNamed: "zombie")

    zombieNode.physicsBody = SKPhysicsBody(circleOfRadius: width/2)
    zombieNode.physicsBody!.affectedByGravity = false
    zombieNode.physicsBody!.categoryBitMask = PhysicsCategory.Zombie
    zombieNode.physicsBody!.contactTestBitMask = PhysicsCategory.Zombie | PhysicsCategory.Player | PhysicsCategory.Wall
    zombieNode.physicsBody!.collisionBitMask = PhysicsCategory.Zombie | PhysicsCategory.Player | PhysicsCategory.Wall
    zombieNode.name = "Zombie"

    zombie = Zombie(node: zombieNode, healthPoints: 50, attack: 5)
    Zombies.append(zombie!)
    zombieNode.position.x = xPos
    zombieNode.position.y = yPos
    zombieNode.size = CGSize(width: width, height: width)
    addChild(zombieNode)
}

When a collision appears this function get activated:

func didBeginContact(contact: SKPhysicsContact) {
    let firstBody = contact.bodyA.node as! SKSpriteNode
    let secondBody = contact.bodyB.node as! SKSpriteNode
    if(firstBody.name == "Player" && secondBody.name == "Zombie"){
        changeHealthPointsForZombieWithNode(secondBody, points: player!.attack)
    } else if(firstBody.name == "Zombie" && secondBody.name == "Player"){
        changeHealthPointsForPlayer(secondBody, points: zombie!.attack)
        print(player!.healthPoints)
    }
}

func changeHealthPointsForZombieWithNode(node: SKSpriteNode, points: Int) {
    for zombie in Zombies {
        if zombie.node == node {
            zombie.healthPoints -= points
            print(zombie.healthPoints)
            if(zombie.healthPoints <= 0){
                zombieNode.removeFromParent()
            }
            return
        }
    }
}

func changeHealthPointsForPlayer(node: SKSpriteNode, points: Int) {
    player!.healthPoints -= points
    if(player!.healthPoints <= 0){
        playerNode.removeFromParent()
        gameOver = true
    }
}

I want to subtract the health points of the zombie depending on the attack of the player and other way around. When the player hits the zombie the zombie should lose life points. When the zombie hits the player the player should lose life points. Every player/zombie got health points and an attack value. The Problem is that some zombies are killable and lose health and other (normally 1-3) are not able to lose health. These zombies who aren't able to lose health are the only one able to kill the player. Zombies that lose health can't deal damage(why?)? So there only able to do one thing(attack or lose health) although they should be able to do two things(attack and lose health).

1
im a little confused, are we saying if player hits zombie, zombie loses life, if zombie hits player, player loses life, but at no time a player and zombie can collide at the same time?Knight0fDragon
@Knight0fDragon Why should they collide at the same time? There is always someone colliding with somehow. If the player is the one running in the zombie the zombie should lose life. If the zombie is the one running in the player the player should lose life.Lukas Köhl
well you need to think like a machine here, I do not think sprite kit is smart enough to determine who is colliding with who, just that they both have collided, so you may be missing calls because you the user see it as Player hit Zombie, but the code sees it as Zombie hit Player when processingKnight0fDragon
@Knight0fDragon It could be that there is some problems with the collision and who collided with whom, but it still doesn't explain the problem. There are always at least one zombie who isn't able to get damage and just attack the player. All other zombies are able to lose health but doesn't attack the player. This whole game was precoded in Greenfoot. There you couldn't clearly see who attacked whom so the player lost life when attack due to random movement. If you are interested I can give you my current project to experience the bug.Lukas Köhl
well I was right about spritekit not being smart, it has no way of knowing who is hitting who, so you are going to have to add a check to determine who is the one doing the attacking. How do I know what a super zombie is? I do not see any spot for that. Also, you should make a zombie a subclass of SKSpriteNode, then you can elinate the node variable, and eliminate that dreaded for loop you do on every checkKnight0fDragon

1 Answers

0
votes

Keep in mind that the two physics bodies described in the contact parameter are not passed in a guaranteed order. Inside the delegate function there is not such a thing as "A is the one colliding with B, not the other way around". There is only "A and B are colliding". That said, you have two options at least, depending on your game mechanics:

  • Make player AND zombie deal damage to each other every time they collide.
  • Make for player and zombie a subclass of SKSpriteNode with a property isAttacking, a boolean to determine when the entity should deal damage when detecting a contact with it. For the player this boolean might get "activated" every button/tap press. For the zombie, every once in a while, if close enough to the player.

Additionally, the way you implemented you will be getting doubled contact notifications. SpriteKit will detect that the player is in contact with the zombie, by the player contact mask and zombie category mask, and that the zombie is in contact with the player, by the zombie contact mask and the player category mask. You usually don't want this to happen. You should make only one of them detect it. I would suggest the player. This way there is no need to set the zombie contact mask. As long as you set zombie in the player's contact mask and the zombie's category mask, you will already have the detection (and once).