double spawning (& overlapping) enemies on Spritekit

Viewed 82

I'm having trouble with an endless run game, mainly with the spawning on the two kind of the enemies that I created, sometimes happens that two enemies spawns when we call the function "startDifficultyTimer". Basically when i call that function the game spawn 2 nodes in the same row so the player is forced to lose, I want to avoid that but I don't know how, I tried almost everything,I've tried to remove createsequentialenemy from didmove but the problem still persist, I think (as newbie) that the problem is in startDifficultTimer function because when the value reach the limit the problem vanish I've posted the code below, sorry for the probably huge mistake but we're new in swift development game, an huge thank you all!

func createEnemy() {
    let enemy: Enemy
    let duration: CGFloat

    switch Int(arc4random() % 100) {
        case 0...70:
            enemy = Enemy.createEnemy()
            duration =  CGFloat(Float(arc4random()%1)) + durationV
            enemy.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 55, height: 37))
            let enemyf = enemy.frame
            let fixedx = frame.width + enemy.frame.width/2.0
            let positions = [ CGPoint(x: fixedx, y: 383), CGPoint(x: fixedx, y: 447), CGPoint(x: fixedx, y: 511)]
            let position = positions[Int(arc4random_uniform(UInt32(positions.count)))]
            enemy.position = position
        case 71...100:
            enemy = Enemy.createEnemyMedium()
            duration =  CGFloat(Float(arc4random()%1)) + durationV
            enemy.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 40, height: 70))
            let enemyf = enemy.frame
            let fixedx = frame.width + enemy.frame.width/2.0
            let positions = [ CGPoint(x: fixedx, y: 415), CGPoint(x: fixedx, y: 479)]
            let position = positions[Int(arc4random_uniform(UInt32(positions.count)))]
            enemy.position = position
        default:
            enemy = Enemy.createEnemy()
            //type = .small
            duration =  CGFloat(Float(arc4random()%1)) + durationV
            let enemyf = enemy.frame
            let fixedx = frame.width + enemy.frame.width/2.0
            let positions = [ CGPoint(x: fixedx, y: 383), CGPoint(x: fixedx, y: 447), CGPoint(x: fixedx, y: 511)]
            let position = positions[Int(arc4random_uniform(UInt32(positions.count)))]
            let texture = SKTexture(imageNamed: "dronea1")
            enemy.position = position
    }

    enemy.physicsBody!.isDynamic = false
    enemy.physicsBody!.categoryBitMask = PhysicsCategory.Enemy
    addChild(enemy)

    let moveTo = SKAction.moveTo(x: 0.0, duration: TimeInterval(duration))
    enemy.run(.repeatForever(.sequence([moveTo, .removeFromParent()])))
}

func createSequentialEnemies() {
    // remove previous action if running. This way you can adjust the spawn duration property and call this method again and it will cancel previous action.
    removeAction(forKey: spawnKey)
    let spawnAction = SKAction.run(createEnemy)
    let spawnDelay = SKAction.wait(forDuration: spawnDuration)
    let spawnSequence = SKAction.sequence([spawnAction, spawnDelay])
    run(SKAction.repeatForever(spawnSequence), withKey: spawnKey)later
}

func startDifficultyTimer() {
    let difficultyTimerKey = "DifficultyTimerKey"
    let action1 = SKAction.wait(forDuration: 1)
    let action2 = SKAction.run { [unowned self] in
        guard self.spawnDuration > 0.5 else {  // set a min limit
            self.removeAction(forKey: difficultyTimerKey) // if min duration has been reached than you might as well stop running this timer.
            return
        }
        self.spawnDuration -= 0.5 // reduce by half a second
        self.createSequentialEnemies() // spawn enemies again
    }
    let sequence = SKAction.sequence([action1, action2])
    run(SKAction.repeatForever(sequence), withKey: difficultyTimerKey)
}
1 Answers

I think that the issue is that you are saying "stop this action" when the timer changes. But that statement doesn't know if an enemy has just been created or is just about to be created. Wherever it is in its loop you are stopping it and starting the loop over. So if it had just created an enemy and you stop the loop and start it over by generating a new enemy faster you will get two enemies in a row.

A way around this could be to run your sequence in reverse. Pause and then generate your enemy.

let spawnSequence = SKAction.sequence([spawnDelay, spawnAction])

you might get a slightly longer gap between enemies but you won't get two that span on top of each other.

Otherwise you could track the spawn time each time the last enemy is spawned and minus it from the next spawn time to put a custom wait action in between.

Related