Benjamin Rosenfeld
Benjamin Rosenfeld

Reputation: 41

Add a delay to a function in SpriteKit

I would like to be able to only allow the player to shoot a missile every 0.7 seconds. How do I do this? Here is my code. I have tried other methods I found on this site but they do not work.

func fireTorpedo() {
    if !self.player.isPaused
    {
        if isSoundEffect == true {
            self.run(SKAction.playSoundFileNamed("Rocket", waitForCompletion: false))
        }

        let torpedoNode = SKSpriteNode(imageNamed: "Rocket")

        torpedoNode.position = player.position
        torpedoNode.position.y += 5
        torpedoNode.physicsBody = SKPhysicsBody(circleOfRadius: torpedoNode.size.width / 2)
        torpedoNode.physicsBody?.isDynamic = true
        torpedoNode.physicsBody?.categoryBitMask = photonTorpedoCategory
        torpedoNode.physicsBody?.contactTestBitMask = alienCategory
        torpedoNode.physicsBody?.collisionBitMask = 0
        torpedoNode.physicsBody?.usesPreciseCollisionDetection = true

        self.addChild(torpedoNode)

        let animationDuration:TimeInterval = 0.5
        var actionArray = [SKAction]()
        actionArray.append(SKAction.move(to: CGPoint(x: player.position.x, y: self.frame.size.height + 10), duration: animationDuration))
        actionArray.append(SKAction.removeFromParent())
        torpedoNode.run(SKAction.sequence(actionArray))
    }
}

Upvotes: 4

Views: 2257

Answers (3)

Whirlwind
Whirlwind

Reputation: 13675

I like to do that like this:

class GameScene:SKScene {

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)

        if action(forKey: "shooting") == nil {

            let wait = SKAction.wait(forDuration: 0.7)
            run(wait, withKey: "shooting")

            print("shot fired")
        }
    }
}

While there is a key "shooting" found on a scene, you can't shoot anymore. In real game, this would be likely the part of a node (an action should be run on a node).

Upvotes: 2

Alessandro Ornano
Alessandro Ornano

Reputation: 35402

To launch every 0.7 seconds you can do:

let actionKey = "repeatLaunchMethod"

let launchMethod = SKAction.afterDelay(0.7, runBlock: {
       [weak self] in
       guard let strongSelf = self else { return }
       strongSelf.fireTorpedo()
})
// do this if you want to repeat this action forever
self.player.run(SKAction.repeatForever(launchMethod), withKey: actionKey)
// do this if you want to repeat this action only n times
// self.player.run(SKAction.repeat(launchMethod, count: 5), withKey: actionKey)

To stop this action you can do:

if self.player.action(forKey: actionKey) != nil {
       self.player.removeAction(forKey: actionKey)
}

Extension used to render the code more readable:

extension SKAction {
    /**
     * Performs an action after the specified delay.
     */
    class func afterDelay(_ delay: TimeInterval, performAction action: SKAction) -> SKAction {
        return SKAction.sequence([SKAction.wait(forDuration: delay), action])
    }
    /**
     * Performs a block after the specified delay.
     */
    class func afterDelay(_ delay: TimeInterval, runBlock block: @escaping () -> Void) -> SKAction {
        return SKAction.afterDelay(delay, performAction: SKAction.run(block))
    }
}

Upvotes: 1

Sweeper
Sweeper

Reputation: 274480

First, add a flag at class-level that indicates whether the player can fire a torpedo.

var canFireTorpedo = true

Then, at the end of the fireTorpedo method, set canFireTorpedo to false, then set it to true again after 0.7 seconds:

canFireTorpedo = false
player.run(SKAction.sequence([
    SKAction.wait(forDuration: 0.7), 
    SKAction.run { [weak self] in self?.canFireTorpedo = true }]))

After that, check canFireTorpedo at the start of the method:

func fireTorpedo() {
    if canFireTorpedo {
        // put everything in the method here, including the parts I added
    }
}

Hopefully this code is self-explanatory.

Upvotes: 1

Related Questions