Stats affecting other stats; connecting two ProgressBars

Question

I have worked for three days without being able to create a custom signal. The idea is that once a stamina bar is depleated, it also reduces your defence bysome amount. As much as i have tried to read, i cannot seem to understand the logic behind custom signals, nor how to send them correctly. As far as i understand, i should somehow tell StaminaBar to send a signal to the Node2D, then the Node2D calls the DefenceBar in some way? I have read the documentation, seen tutorials, and have made working buttoms and a little moving guy… but a stat making another stat go upp or down? Please, am i missing something really basic here. Sorry if this is silly or simplistic but some part of this logic my brain is not vibing with, and i whould like to understand the “whys”, not only the practical aspect. Thank you.

Here is my terrible code that wont work:

extends Node2D
@onready var health_bar: ProgressBar = $HealthBar
@onready var defence_bar: ProgressBar = $DefenceBar
#@onready var stamina_bar: ProgressBar = $StaminaBar
@onready var stamina_bar = get_node("StaminaBar")
#i assume some sort of call or send or emit is souposed to go here?
#code for StaminaBar (progress bar)
func Tired():
	if STAMINA.CurrentStamina == 0:
		StaminaZero.emit()
#code for DefenceBar (also a progress bar)
func _on_stamina_bar_stamina_zero():
	DEFENCE.CurrentDef -= 18.4
	DefMinimum()
	defence_bar.value = DEFENCE.CurrentDef

the two progress bars are siblings, and the Node2D is their parent if this is usefull.

In your DefenceBar you need to connect to StaminaZero. Judging by your code snippets here you would need:

func _ready() → void:
 STAMINA.StaminaZero.connect(_on_stamina_bar_stamina_zero)

(I assume STAMINA is somehow a global reference?)

In cases like these it helps to look at the Debugger and read the errors and warnings it prints. Also, you can help yourself understand what is happening (and what is not) by using the print()function everywhere.

Try adding print(STAMINA.CurrentStamina) in your DefenceBar_on_stamina_bar_stamina_zero()to see if it correctly fires.

Do you even need a custom signal for this? It looks like you could just use the already built-in Progressbar signals, like value_changed and connect those to your Node2D script.

Example:

func _on_stamina_bar_value_changed(value: float) -> void:
	if value == 0:
      DEFENCE.CurrentDef -= 18.4
      DefMinimum()
	  defence_bar.value = DEFENCE.CurrentDef

in hindsigth… yes, this indeed is the much better way. Whould still like to know the “why” of custom signals as ill need to tackle them eventually, but that is irrelevant for my current problem. Thanks

It seems like your code should work, assuming StaminaZero is a signal declared in the same script i.e.

# stamina bar.gd
extends ProgressBar

signal StaminaZero # does this exist within the script?

func Tired() -> void:
	if STAMINA.CurrentStamina == 0:
		StaminaZero.emit()

Then you can connect this signal through the editor.

If signal is declared inside the same script, the it should work, maybe your STAMINA.CurrentStamina isn’t equal to zero when you call Tired?

Yes. STAMINA is global, should have made this clearer. The prints are not working; i have a few scattered about here and there to see things are working, but they never fire. Im getting Missing connected method ‘_on_battle_screen_stamina_empty’ for signal ‘StaminaEmpty’ from node ‘BattleScreen’ to node ‘BattleScreen/DefenceBar’. as the error message if this helps?

There error message is very relevant, can you share your scene tree? Do you have a function named _on_battle_screen_stamina_empty?

i do not have a function like that as of yet. Sharing my entire tree whould be a little bit of a mess, but there are as of yet no other nodes other then a few buttons and a health bar. All of these are siblings currently.

To avoid getting troubles with node paths you can export a node.

In this case something like this in the defence bar script:

@export var StaminaBar: ProgressBar

func _ready():
     StaminaBar.StaminaZero.connect("_on_stamina_bar_value_changed")

Then in the inspector, just click the export and select the stamina bar. That way you are guaranteed to have access to the stamina bar even if you rearrange your scene or whatever.

I find it a bit easier and more reliable to work with than nodepaths, get_node and those things.

this is what i tried to do. Yes, i have signal StaminaZeroin the same script, and that is what i used to connect the two progress bars together.

tried your solution to, and it gave me this error: Invalid access to property or key ‘StaminaZero’ on a base object of type ‘Nil’. I have no clue what Godot is doing, as there is no undefined thing in the programming.

It means that you have not assigned an object. The object is nil means that it doesn’t exist.

Are you sure you have assigned the node as the export variable?

if this is done through @export or @ready, then i have at least tried with @export var stamina_bar : ProgressBar. Yet it does not seem to be working. Do both nodes need to be exported to eachother?

i finnaly tried this solution after trying to get the custom signal to work. It also did not work, even though the in built on_button_pressed signals worked. Either im missing the one major obvious peice that will make everything fit, or, as im starting to suspect perhaps superstisiously, that my copy of Godot4 is glitched in some detrimental way

Just to double-check: In the editor click on the node that is supposed to send the signal, then check the Signals tab to the right to see if the signal actually is connected (I assume you did that, but I’ll mention it just in case).

If the signal is connected and the debugger doesn’t show an error, it is indeed connected and working. If still nothing is happening, then maybe the issue is the function itself and not the signal.

Can you post the function that the signal is triggering?

And in regards to the custom signal not working - have you checked if the signal is actually connected to the function before it is emitted?

You can use methods like .has_connections() and .is_connected() to track what is happening with your signals more precisely.

I hope this helps:

extends Node2D

#declare a custom test signal
signal test_signal

func _ready():
    #connect test signal to a function
	test_signal.connect(say_hello)

    #check if test signal actually is connected
	if test_signal.has_connections():
		print("has connections")

    #check if test signal is connected to a specific function
	if test_signal.is_connected(say_hello):
		print("signal connected to SAY HELLO")

    #emit the signal
	test_signal.emit()

func say_hello() -> void:
    print("hi")

this migth be something to do in a new thread, but my prints are not working. Im not getting any messages in the debug menu. Yes, i have them enabled, still nothing. I do have a few other

if STAMINA.CurrentStamina <= n

print("OHNO, i need to tell you that you are tired!")

and they dont work.

Then it may be that CurrentStamina either has no value assigned to it or it is not updated?

I don’t know how you’ve set things up, but I’ll just assume that your StaminaBar is intended to update your CurrentStamina. Maybe check the numbers you are comparing in that if-statement, because if CurrentStamina is never lower or equal to n , nothing will get printed.