Search code examples
sprite-kitskspritenodeskactionsklabelnode

run an SKAction based on the character count of an SKLabel node's text


I have a game scene that consists of animations that occur in sync with the content of a song. One of the actions I'm trying to set up is in relation to the lyrics of the song which are displayed using an SKLabelNode. What I want to do is is toggle between two animations based on whether or not there are characters in the LabelNode (the singer stops "singing" and waits). I have this in the update method:

var singerActive = false
var lyricsLabel = SKLabelNode()

override func update(currentTime: CFTimeInterval) {
        /* Called before each frame is rendered */
        if lyricsLabel.text?.characters.count == 0{
            singerActive = false
        }else{
            singerActive = true
        }

        let sings = SKAction.runBlock { () -> Void in
            self.singer.runAction(SKAction.fadeAlphaTo(1, duration: 0.1))
            self.singerIdle.runAction(SKAction.fadeAlphaTo(0, duration: 0.1))
        }
        let singerInactive = SKAction.runBlock { () -> Void in
            self.singer.runAction(SKAction.fadeAlphaTo(0, duration: 0.1))
            self.singerIdle.runAction(SKAction.fadeAlphaTo(1, duration: 0.1))
        }

        var stopper = true
        if singerActive && stopper{
            stopper = false
            singer.runAction(sings)
        }

        if singerActive == false && stopper == false{

            stopper = true
            singer.runAction(singerInactive)
        }

    }

Unfortunately, this doesn't seem to work. The active singer state works, but the inactive state fails to trigger when the character count == 0. I put the "stopper" bool in there in order to ensure that the SKAction doesn't get called continuously as it's in the update method. I'm fairly new to Swift and SpriteKit so any help would be greatly appreciated.


Solution

  • Your stopper code is breaking everything, you want to do something like this:

        override func update(currentTime: CFTimeInterval) {
        /* Called before each frame is rendered */
        if lyricsLabel.text?.characters.count == 0{
            singerActive = false
        }else{
            singerActive = true
        }
    
        let sings = SKAction.runBlock { () -> Void in
            self.singer.runAction(SKAction.fadeAlphaTo(1, duration: 0.1))
            self.singerIdle.runAction(SKAction.fadeAlphaTo(0, duration: 0.1))
        }
        let singerInactive = SKAction.runBlock { () -> Void in
            self.singer.runAction(SKAction.fadeAlphaTo(0, duration: 0.1))
            self.singerIdle.runAction(SKAction.fadeAlphaTo(1, duration: 0.1))
        }
    
        if singerActive{
            singer.removeAllActions()
            singer.runAction(sings)
        }
        else{
            singer.removeAllActions()
            singer.runAction(singerInactive)
        }
    
    }
    

    But this is also bad because this means that you are always running an action on every update. You may want to rework your code so this isn't happening.

    So you need to do something like this:

        override func update(currentTime: CFTimeInterval) {
        /* Called before each frame is rendered */
        if lyricsLabel.text?.characters.count == 0{
            singerActive = false
        }else{
            singerActive = true
        }
    
        let sings = SKAction.runBlock { () -> Void in
            self.singer.runAction(SKAction.fadeAlphaTo(1, duration: 0.1))
            self.singerIdle.runAction(SKAction.fadeAlphaTo(0, duration: 0.1))
        }
        let singerInactive = SKAction.runBlock { () -> Void in
            self.singer.runAction(SKAction.fadeAlphaTo(0, duration: 0.1))
            self.singerIdle.runAction(SKAction.fadeAlphaTo(1, duration: 0.1))
        }
    
        if singerActive && singerIdle.alpha = 1{
            singer.removeAllActions()
            singerIdle.alpha = 0.99;
            singer.runAction(sings)
        }
        else if !singerActive && singer.alpha = 1{
            singer.removeAllActions()
            singer.alpha = 0.99;
            singer.runAction(singerInactive)
        }
    
    }
    

    Now we only call it when our alpha states are at 1