Asica
Asica

Reputation: 49

SpriteKit: Having trouble updating text of a SKLabelNode during runtime (2 approaches)

I want to set the text of a label based on a User's tapping on a Node. I've tried two approaches, neither of which have worked as intended.

Approach #1: Declare the SKLabelNode globally, update sklabelnode.text at runtime. Code:

import SpriteKit

class GameScene: SKScene {

  //Level 0
  var q0Label:SKLabelNode!
  var q1Label:SKLabelNode!
  let gem = SKSpriteNode(imageNamed:"Destiny_Gem")
  let energy = SKSpriteNode(imageNamed: "Destiny_Energy")
  let money = SKSpriteNode(imageNamed: "Destiny_Money")

  override func didMoveToView(view: SKView) {
    //Level 0 - Choose your Destiny
    //Add a label to the scene
    q0Label = SKLabelNode(fontNamed: "Courier")
    q0Label.text = "Why do you want to be a Doctor?"
    q0Label.fontSize = 30
    q0Label.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
    self.addChild(q0Label)

    q1Label = SKLabelNode(fontNamed: "Courier")
    q1Label.fontSize = 25
    q1Label.position = CGPoint(x:frame.size.width * 0.5, y: frame.size.height * 0.3)

    //Position the sprites
    gem.position = CGPoint(x:frame.size.width * 0.2, y: frame.size.height * 0.4)
    gem.zPosition = 2
    [gem.setScale(0.5)]
    gem.name = "gem"
    energy.position = CGPoint(x:frame.size.width * 0.5, y: frame.size.height * 0.4)
    energy.zPosition = 2
    [energy.setScale(0.5)]
    energy.name = "energy"
    money.position = CGPoint(x:frame.size.width * 0.8, y: frame.size.height * 0.4)
    money.zPosition = 2
    [money.setScale(0.25)]
    money.name = "money"

    //Add sprites to scene
    addChild(gem)
    addChild(energy)
    addChild(money)
  }

  override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
    /* Called when a touch begins */

    for touch: AnyObject in touches {
      let location = (touch as! UITouch).locationInNode(self)
      if let theName = self.nodeAtPoint(location).name {
        if theName == "gem" {
          q1Label.text = "Like my parents before me, it is my destiny."
          self.addChild(q1Label)
        }
        else if theName == "energy" {
          q1Label.text = "The people of the world need my help."
          self.addChild(q1Label)
        }
        else if theName == "money" {
          q1Label.text = "I want to be rich and famous."
          self.addChild(q1Label)
        }
      }
    }
  }
}

Problem: This will work the first time you click a Node, however the very next click, the application will crash, with this error message: "Attemped to add a SKNode which already has a parent error"

I then read various solutions on SO about not declaring the object globally. That lead to Approach #2.

Approach #2: Declare the label conditionally. Code:

  override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
    /* Called when a touch begins */

    for touch: AnyObject in touches {
      let location = (touch as! UITouch).locationInNode(self)
      if let theName = self.nodeAtPoint(location).name {
        if theName == "gem" {
          var q1Label:SKLabelNode!
          q1Label = SKLabelNode(fontNamed: "Courier")
          q1Label.fontSize = 25
          q1Label.position = CGPoint(x:frame.size.width * 0.5, y: frame.size.height * 0.3)
          q1Label.text = "Like my parents before me, it is my destiny."
          //set destiny variable = gem
          self.addChild(q1Label)
        }
        else if theName == "energy" {
          var q1Label:SKLabelNode!
          q1Label = SKLabelNode(fontNamed: "Courier")
          q1Label.fontSize = 25
          q1Label.position = CGPoint(x:frame.size.width * 0.5, y: frame.size.height * 0.3)
          q1Label.text = "The people of the world need my help."
          //set destiny variable = energy
          self.addChild(q1Label)
        }
        else if theName == "money" {
          var q1Label:SKLabelNode!
          q1Label = SKLabelNode(fontNamed: "Courier")
          q1Label.fontSize = 25
          q1Label.position = CGPoint(x:frame.size.width * 0.5, y: frame.size.height * 0.3)
          q1Label.text = "I want to be rich and famous."
          //set destiny variable = money
          self.addChild(q1Label)
        }
      }
    }
  }
}

Problem: This just creates the labels over the top of each other; I don't know how to clean up any that were created earlier.

Upvotes: 1

Views: 162

Answers (1)

Whirlwind
Whirlwind

Reputation: 13675

Re-creating labels in this case is really unnecessary. You only want to update a label based on which button is clicked. The only thing you need to do is to create those two labels once in didMoveToView method and update them accordingly, like this (just copy/past this code to try):

import SpriteKit

class GameScene: SKScene {

    //Level 0
    var q0Label:SKLabelNode!
    var q1Label:SKLabelNode!
    let gem = SKSpriteNode(imageNamed:"Destiny_Gem")
    let energy = SKSpriteNode(imageNamed: "Destiny_Energy")
    let money = SKSpriteNode(imageNamed: "Destiny_Money")

    override func didMoveToView(view: SKView) {
        //Level 0 - Choose your Destiny
        //Add a label to the scene
        q0Label = SKLabelNode(fontNamed: "Courier")
        q0Label.text = "Why do you want to be a Doctor?"
        q0Label.fontSize = 30
        q0Label.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
        self.addChild(q0Label)

        q1Label = SKLabelNode(fontNamed: "Courier")
        q1Label.fontSize = 25
        q1Label.position = CGPoint(x:frame.size.width * 0.5, y: frame.size.height * 0.3)
        q1Label.text = ""
         self.addChild(q1Label)
        //Position the sprites
        gem.position = CGPoint(x:frame.size.width * 0.35, y: frame.size.height * 0.4)
        gem.zPosition = 2
        [gem.setScale(0.5)]
        gem.name = "gem"
        energy.position = CGPoint(x:frame.size.width * 0.3, y: frame.size.height * 0.4)
        energy.zPosition = 2
        [energy.setScale(0.5)]
        energy.name = "energy"
        money.position = CGPoint(x:frame.size.width * 0.4, y: frame.size.height * 0.4)
        money.zPosition = 2
        [money.setScale(0.25)]
        money.name = "money"

        //Add sprites to scene
        addChild(gem)
        addChild(energy)
        addChild(money)
    }

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        /* Called when a touch begins */

        for touch: AnyObject in touches {
            let location = (touch as UITouch).locationInNode(self)
            if let theName = self.nodeAtPoint(location).name {
                if theName == "gem" {
                    q1Label.text = "Like my parents before me, it is my destiny."

                }
                else if theName == "energy" {
                    q1Label.text = "The people of the world need my help."

                }
                else if theName == "money" {
                    q1Label.text = "I want to be rich and famous."

                }
            }
        }
    }
}

Just don't re-add those labels multiple times. And about the error you are getting...Keep in mind that node can have only one parent at the time, and that's why you are getting the error which saying that certain label already has a parent.

Upvotes: 1

Related Questions