Incrementing Score in Swift when SpriteNode goes off screen

Question

I have searched this a lot and couldn't find anything which could help. I am creating a game in SpriteKit using Swift 3.

SpriteNodes spawn on the right hand side of the screen and using SKActions make their way to the left hand side of the screen where this repeats.

I need a way that when the SpriteNode passes my player (in the middle of the scene) the score goes up by one.

The Following Block moves my enemy character from Right-To-Left.

func createEnemyBird() {
    // Create Enemy Bird 1
    enemyBird1 = EnemyBird(imageNamed: "enemy 1")
    enemyBird1.position.x = self.frame.width + 100
    enemyBird1.position.y = CGFloat.randomBetweenNumbers(firstNum: -(self.frame.size.height / 2.8), secondNum: (self.frame.size.height / 2.4))
    enemyBird1.name = "EnemyBird1"
    enemyBird1.initialise()

    print(enemyBird1.position.y)

    // Add Physics Bodys
    enemyBird1.physicsBody = SKPhysicsBody(circleOfRadius: self.size.height/10)
    enemyBird1.physicsBody?.allowsRotation = false
    enemyBird1.physicsBody?.affectedByGravity = false
    enemyBird1.physicsBody?.isDynamic = false
    // Add Collision Bitmasks
    enemyBird1.physicsBody?.categoryBitMask = ColliderType.Pipes

    // Animate Enemy Bird
    var enemyBirdAnim = [SKTexture]()
    for i in 1..<5 {
        let enemyName = "enemy \(i)"
        enemyBirdAnim.append(SKTexture(imageNamed: enemyName))
    }
    let animateEnemyBird = SKAction.animate(with: enemyBirdAnim, timePerFrame: 0.08, resize: false, restore: true)
    enemyBird1.run(SKAction.repeatForever(animateEnemyBird))

    // Make enemy Bird Move Left
    let destination = self.frame.width * 2;
    let move = SKAction.moveTo(x: -destination, duration: TimeInterval(6));
    let remove = SKAction.removeFromParent();


    // Run Enemy Bird Mobe
    enemyBird1.run(SKAction.sequence([move, remove]), withKey: "Move")



    // Add Children to scene
    self.addChild(enemyBird1)

}

I also have a function But i do not know when to call it

    // Score increase
func incrementScore() {
    if enemyBird1.position.x == -(self.frame.size.width/2) {
        score += 1
        scoreLabel.text = String(score)
    }
}

I tried to add an SKAction Run Block calling it in the sequence but that didn't work very well as about 6 enemy characters passed before it actually started counting (which is weird)...


Show source
| ios   | swift   | sprite-kit   | skspritenode   2017-05-11 23:05 3 Answers

Answers ( 3 )

  1. 2017-05-11 23:05

    One solution is to add your incrementScore function inside the update method on your SKScene:

    override func update(_ currentTime: TimeInterval) {
    
        if !enemyBird1.isScored {
            if enemyBird1.position.x > -self.frame.size.width / 2 {
                score += 1
                scoreLabel.text = String(score)
                enemyBird1.isScored = true
            }
        }
    
    }
    

    The update method will fire every frame and check to see if the bird has passed the center of the screen. If it has, it will be marked as having scored so it won't be counted again.

    This will require you to add an isScored property to your EnemyBird class.

    Assuming your EnemyBird is an SKSpriteNode, it will look something like this:

    class EnemyBird: SKSpriteNode {
    
        var isScored = false
    
        // other properties, inits, functions, etc
    
    }
    
  2. 2017-05-12 01:05

    You could consider overriding position in EnemyBird to call a function whenever the position of the bird changes. For example, inside the EnemyBird class, you could add something like:

     override var position: CGPoint {
        didSet {
            if self.position.x == theParentSceneLeftEdge {
            // call a function to update score
            } 
        }
     }
    

    EnemyBird would then also need to know the value of theParentSceneLeftEdge, which you could assign to equal to the value of your scene's left edge when you init EnemyBird.

    Hope this helps!

  3. 2017-05-12 02:05

    You can do the whole thing with just SKActions. Just split the move into two parts: first move to the score line, then move the rest of the way. And in between, update the score. This code replaces the "Make enemy Bird Move Left" and "Run Enemy Bird Mobe" sections in createEnemyBird():

    // first action moves to the score line
    let scoreLineDestination = -self.frame.width / 2
    let moveToScoreLine = SKAction.moveTo(x: scoreLineDestination, duration: TimeInterval(3));
    
    // second action calls your incrementScore() method
    let increment = SKAction.run(self.incrementScore)
    
    // third action moves the rest of the way
    let finalDestination = -self.frame.width * 2;
    let moveToFinal = SKAction.moveTo(x: finalDestination, duration: TimeInterval(3));
    
    // finally remove
    let remove = SKAction.removeFromParent();
    
    enemyBird1.run(SKAction.sequence([moveToScoreLine, increment, moveToFinal, remove]), withKey: "Move")
    

    You can also drop the position check from incrementScore() since you know it won't get called until a bird crosses the score line:

    func incrementScore() {
        score += 1
        scoreLabel.text = String(score)
    }
    

    That way you don't have to worry about which bird is calling it.

◀ Go back