derekFairholm
derekFairholm

Reputation: 285

SKLabelNode display text at specified time

I'm making a scene in which the lyrics to a song which is played in the background of a scene are displayed line by line at the bottom of a spriteKit scene. I've tried a number of different solutions, but for some reason with each of them some of the lyrics are not being displayed or are out of sync. Im attempting to use an SKLabelNode and using SKActions change the text of it at specific times. There must be an easier way of doing this!?

here's the function I came up with:

import SpriteKit

let lyrics = SKLabelNode(fontNamed: "avenirnext")

func lyricSectionCreate(lnOneTxt: String, lnOneTime: Double, lnTwoTxt: String, lnTwoTime: Double, lnThreeTxt: String, lnThreeTime: Double, lnFourTxt: String, lnFourTime: Double, lnFiveTxt: String, lnFiveTime: Double, lnSixTxt: String, lnSixTime: Double, lnSevenTxt: String, lnSevenTime: Double, lnEightTxt: String, lnEightTime: Double, lnNineTxt: String, lnNineTime: Double, lnTenTxt: String, lnTenTime: Double){

lyrics.position = CGPoint(x:250, y: 25)
lyrics.zPosition = 5

//line one
let lnOne = SKAction.runBlock { () -> Void in
    lyrics.text = lnOneTxt
}

runAfterDelay(lnOneTime){
    lyrics.runAction(lnOne)
}

//line two
let lnTwo = SKAction.runBlock { () -> Void in
    lyrics.text = lnTwoTxt
}

runAfterDelay(lnTwoTime){
    lyrics.runAction(lnTwo)
}

//line three
let lnThree = SKAction.runBlock { () -> Void in
    lyrics.text = lnThreeTxt
}

runAfterDelay(lnThreeTime){
    lyrics.runAction(lnThree)
}

//line four
let lnFour = SKAction.runBlock { () -> Void in
    lyrics.text = lnFourTxt
}

runAfterDelay(lnFourTime){
    lyrics.runAction(lnFour)
}

//line five
let lnFive = SKAction.runBlock { () -> Void in
    lyrics.text = lnFiveTxt
}

runAfterDelay(lnFiveTime){
    lyrics.runAction(lnFive)
}

//line six
let lnSix = SKAction.runBlock { () -> Void in
    lyrics.text = lnSixTxt
}

runAfterDelay(lnSixTime){
    lyrics.runAction(lnSix)
}

//line seven
let lnSeven = SKAction.runBlock { () -> Void in
    lyrics.text = lnSevenTxt
}

runAfterDelay(lnSevenTime){
    lyrics.runAction(lnSeven)
}

//line eight
let lnEight = SKAction.runBlock { () -> Void in
    lyrics.text = lnEightTxt
}

runAfterDelay(lnEightTime){
    lyrics.runAction(lnEight)
}

//line nine
let lnNine = SKAction.runBlock { () -> Void in
    lyrics.text = lnNineTxt
}

runAfterDelay(lnNineTime){
    lyrics.runAction(lnNine)
}

//line ten
let lnTen = SKAction.runBlock { () -> Void in
    lyrics.text = lnTenTxt
}

runAfterDelay(lnTenTime){
    lyrics.runAction(lnTen)
}

}

func runAfterDelay(delay: NSTimeInterval, block: dispatch_block_t) {
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay *   Double(NSEC_PER_SEC)))
dispatch_after(time, dispatch_get_main_queue(), block)
}

Upvotes: 1

Views: 303

Answers (1)

Whirlwind
Whirlwind

Reputation: 13665

Yikes! Not the way to go... Imagine what would happen if there is much more lines? You should avoid repeated code as much as possible.

Here is how you can predefine text, delay and refresh label's text based on those data (just copy and paste the code to see how it works):

import SpriteKit

class GameScene: SKScene {

let lyricsLabel = SKLabelNode(fontNamed: "ArialMT")

/*
    Create an array of tuples (text,delay). You could go with struct as well.
*/

let lines:[(text:String, delay:NSTimeInterval)] = [

    ("Line 1" ,3.05),
    ("Line 2" ,1.05),
    ("Line 3" ,1.42),
    ("Line 4" ,0.87),
    ("Line 5" ,1.21),
    ("Line 6" ,0.12),
    ("Line 7" ,1.23),
    ("Line 8" ,1.23),
    ("Line 9" ,0.52),
    ("Line 10",2.12)
]

var currentLine = 0

override func didMoveToView(view: SKView) {

    lyricsLabel.position = CGPoint(x:CGRectGetMidX(frame), y:CGRectGetMinY(frame) + 100)
    addChild(lyricsLabel)

    recursive()
}

/*
What is happening here is:

1. method recursive() is called
2. execution continues if there is more lines
3. label's text is updated (and currentLine is updated as well)
4. now we wait for a predefined duration, and recursive() is called again within itself, which means we jump to step 1 again, then go to step 2 etc...
*/

func recursive(){

    guard currentLine < lines.count
    else {

        if actionForKey("aKey") != nil {
            removeActionForKey("aKey")   
        }
        return
    }

    lyricsLabel.text = lines[currentLine].text

    let wait            = SKAction.waitForDuration(lines[currentLine].delay)
    let block           = SKAction.runBlock({[unowned self] in

        self.currentLine++
        self.recursive()
    })
    let sequence        = SKAction.sequence([wait, block])
    let action          = SKAction.repeatActionForever(sequence)

    self.runAction(action, withKey: "aKey")
    }

}

Upvotes: 2

Related Questions