I consider myself a beginner in Godot, and it's been a week that I've been trying to fix this, which is updating the health in the player script to the label script.
I've seen and searched for many tutorials and other people's questions to fix this, but I still didn't find a way.

In Player script

extends CharacterBody2D
@export var health = 200

Game script (where all the nodes are)

extends Node
@onready var text = $ui/Health_panel/text_hp2
@onready var health = global.health
func _ready():
	text.text = "Player Health: " + str(health)

global script - for the global variables

extends Node
@export var health = 200

label script - the text itself

extends Label
func update_health():
	var health = global.health

After many tests, these were the only things left. What is better? Try to emit a signal from the player script directly to the label script, or connect variables with the global, or game scripts?
It's my first time asking about coding in a forum, so I still have no experience in this. So I'm gonna be extremelly grateful if someone could help me with this :')

  • BroTa replied to this.
  • HeyLavenderBee What is better? Try to emit a signal from the player script directly to the label script, or connect variables with the global, or game scripts?

    As stated by el_malo , you are not updating the Label constantly and it won't change text. Your question is how you do it, right?

    I generally try to avoid globals, when there is another solution. And use them when only neccessary, or when their use makes sense.

    Generally the rule is signal up (signal to parents and siblings) call down (functions of children)

    Setter function is a very neat way to do this, they are called whenever their variables change, and you can do anything inside them

    In the player script you can do this:

    # Beware that, with export vars, the set function is also called first thing, 
    # even on the default value, so try not to access other nodes, 
    # as they will not be ready/instantiated yet.
    # A workaround can be to have @export var default_health,
    # and another var health with setter function 
    # Then inside _ready() you do health = default_health
    # That was only a solution to problem you might face someday, in a different context
    @export var health = 200:
      set(value):
        # Whenever you try to change the health value, 
        # this gets called and the below code is executed
        health = value
        # A signal can carry information too, such as health
        emit_signal("health_changed", health)
    
    signal health_changed(health)

    In Game script:

    @onready var text = $ui/Health_panel/text_hp2
    @onready var player = $Player # or wherever you Player node is in the Game node tree
    
    # The neat thing about _ready() is that it is called AFTER every child node is ready
    # Put that in mind and you can traverse the mine field called scene tree :')
    func _ ready():
      # connect signals, you can do it in the editor too via signals tab
      # connect the health_changed signal, from the player, to the update_health(new_health) function in the health label
      player.health_changed.connect(text.update_health)
      # Beware that since the children nodes were ready first,
      # if the player emitted a signal( which it did in the set function of the export var),
      # nothing happens because the signal was not connected yet
      text.update_health(player.health) # Neat way to update it first thing and show the health at the start
      # A scum elitist would do player.health = player.health 
      # knowing the set function will be called and emit the already connected signal xD

    Now change the update_health function in the label script to something like this

    func update_health(new_health):
      text = " Player Health: " + String.num(new_health) # Both sides need to be String type

    Tada, you have a label that updates whenever the player's health change

    • Note: The neat thing about signals, is that it separates code logically. So you can cannect the signal to the gui that holds a label and health bar for example, and update both. Or you can connect the signal to another function in the game scene which check the health, and accordingly alert some types of enemies like blood seekers for example. You can go wild with signals!

    • Also notice that you are only updating the label when the health changes, and not ever frame. Much optimization.

    I hope that was helpful with the explanations 😉

    Edit: Correct here and there, add the last notice.

    In the function _physics_process(delta) there should be the life bar because if you put it in ready, only the data that was there when the script was executed will appear

      HeyLavenderBee What is better? Try to emit a signal from the player script directly to the label script, or connect variables with the global, or game scripts?

      As stated by el_malo , you are not updating the Label constantly and it won't change text. Your question is how you do it, right?

      I generally try to avoid globals, when there is another solution. And use them when only neccessary, or when their use makes sense.

      Generally the rule is signal up (signal to parents and siblings) call down (functions of children)

      Setter function is a very neat way to do this, they are called whenever their variables change, and you can do anything inside them

      In the player script you can do this:

      # Beware that, with export vars, the set function is also called first thing, 
      # even on the default value, so try not to access other nodes, 
      # as they will not be ready/instantiated yet.
      # A workaround can be to have @export var default_health,
      # and another var health with setter function 
      # Then inside _ready() you do health = default_health
      # That was only a solution to problem you might face someday, in a different context
      @export var health = 200:
        set(value):
          # Whenever you try to change the health value, 
          # this gets called and the below code is executed
          health = value
          # A signal can carry information too, such as health
          emit_signal("health_changed", health)
      
      signal health_changed(health)

      In Game script:

      @onready var text = $ui/Health_panel/text_hp2
      @onready var player = $Player # or wherever you Player node is in the Game node tree
      
      # The neat thing about _ready() is that it is called AFTER every child node is ready
      # Put that in mind and you can traverse the mine field called scene tree :')
      func _ ready():
        # connect signals, you can do it in the editor too via signals tab
        # connect the health_changed signal, from the player, to the update_health(new_health) function in the health label
        player.health_changed.connect(text.update_health)
        # Beware that since the children nodes were ready first,
        # if the player emitted a signal( which it did in the set function of the export var),
        # nothing happens because the signal was not connected yet
        text.update_health(player.health) # Neat way to update it first thing and show the health at the start
        # A scum elitist would do player.health = player.health 
        # knowing the set function will be called and emit the already connected signal xD

      Now change the update_health function in the label script to something like this

      func update_health(new_health):
        text = " Player Health: " + String.num(new_health) # Both sides need to be String type

      Tada, you have a label that updates whenever the player's health change

      • Note: The neat thing about signals, is that it separates code logically. So you can cannect the signal to the gui that holds a label and health bar for example, and update both. Or you can connect the signal to another function in the game scene which check the health, and accordingly alert some types of enemies like blood seekers for example. You can go wild with signals!

      • Also notice that you are only updating the label when the health changes, and not ever frame. Much optimization.

      I hope that was helpful with the explanations 😉

      Edit: Correct here and there, add the last notice.

        BroTa Ohhh, that worked right as I tested it, thank you so much!
        And thanks you two for reminding me of updating the Label constantly, I think that was the main reason it was not working.
        But thank you again 🙂