Help with if statement working when it should not

Godot Version

4.6.2

Question

So in short I am trying to make a regenerating shield for a ship, the shield health is part of my stats component.

here’s the issue, this is part of my code is supposed to check if my shield health is 0 then run the regen func to wait 5.0 sec then set my shield health to 100 then put my hurtbox back to active state then play the shield animation reforming then print for me my current shield health.

this all happens, but for some reason regen wont stop … it just keeps printing my shield health over and over like 600 lines a second, in game the shield looks like it works … i break it and in about 5 sec it reforms but after a bit it gets wonky and seems to take longer to break the shield, pointing at the possibility that its constantly being reset to 100 health over and over.

what confuses me is the if statement is checking for the health to be 0 and I know after regen the shield health is 100 (the print out tells me this 600 times a sec) so I don’t understand why regen is firing off over and over.

TLDR:

after the if statement fires and runs regen, regen wont stop running even when the if statement cant be triggered

func _physics_process(_delta: float) -> void:
	if stats_component.health == 0:
		regen()
	

func regen() -> void:
		await get_tree().create_timer(5.0).timeout
		stats_component.health = 100
		hurtbox_component.is_invincible = false
		animated_sprite.play("activate")
		print(stats_component.health)

just changed it, tested … still not working

I think even better would be to have a separate timer node that has its timeout signal connected to the regen() function though. And then just start it where now you instantiate it.

Like

func _physics_process(_delta: float) -> void:

	if stats_component.health == 0:

		timer.start()



func regen() -> void: 

		stats_component.health = 100

		hurtbox_component.is_invincible = false

		animated_sprite.play("activate")

		print(stats_component.health)

Physics process happens X times per second. And for each physics process, you run regen. But one instance of regen function waits 5 seconds to continue. So during that 5 seconds you run regen function over and over again. (5 * X times)

Add a variable to check if regen is started and use it in your if statement.

var is_regen: bool = false #add this variable

func _physics_process(_delta: float) -> void:
	if stats_component.health == 0 and !is_regen: #make sure this only happens if not regenerating
		regen()
	

func regen() -> void:
		is_regen = true # waiting for regen
		await get_tree().create_timer(5.0).timeout
		stats_component.health = 100
		hurtbox_component.is_invincible = false
		animated_sprite.play("activate")
		print(stats_component.health)
		is_regen = false # regen finished.


2 Likes

ok yes ok … ``await get_tree().create_timer(5.0).timeout is the issue … removing it make it work but instantly … Ill look into timers.

it always seems to be a hit or miss with await with me.

I am now working with what lastbender said,

thanks very much, this gives me a direction to head into.

Oops I didn’t even see that it was in process.:sweat_smile:

1 Like

Yeah i missed that it was in process. Think i initially misread the entire post tbh.

To avoid spreading your health code out, you could do the check where you change life total. So when you do life -= X add

if life <= 0:
     timer.start()

Then you avoid setting it several times and the need of tracking if regen has already started or not.

You would need to add a timer node and add it as an autoload variable at the top of the script.

ok so I did your changes and wow things got weird, first the shield reformed instantly when I destroyed it, then it took 10 sec to reform then it did not reform at all.

but at least the print out stopped spamming my shield health.

EDIT:

I made some mistakes in using your code so that’s why it did not work before, I forgot to put in ``is_regen = false at the end and to replace my timer code.

in short your code works and my shield now regens, thank you very much :smiley:

1 Like

I am not too sure how to write why things are the way they are…..

things end up much like the code you see now, I know my components are tied into many things but they are very versatile.

My issues always seem to pop up when I am trying to do something simple.. like this is mind boggling simple on what I want to do.

Anyways, thanks… there’s enough here for me to find a solution.

1 Like

Your initial code await get_tree().create_timer(5.0).timeout added a new SceneTreeTimer object every time it was called (most likely 60 times a second), i.e. _physics_process is still being even when your timer is being awaited on.

What would be better is to reuse the same timer. To do this you can use a Timer node (either added in the scene or in code).

Here it is in your example:

var regen_timer: Timer


func _init() -> void:
    regen_timer = Timer.new()
    regen_timer.one_shot = true
    regen_timer.wait_time = 5
    regen_timer.timeout.connect(_on_regen_timer_timeout)
    add_child(regen_timer)


func _physics_process(_delta: float) -> void:
    if stats_component.health == 0:
        regen_timer.start()


func _on_regen_timer_timeout() -> void:
    stats_component.health = 100
    hurtbox_component.is_invincible = false
    animated_sprite.play("activate")
    print(stats_component.health)

I recommend you read Timer and SceneTreeTimer docs.

lastbender solved my code with his bool var,

but all so learning on how timers work is invaluable to me. Its madding on how many ways one can do one particular task in code. like which one is better? timers or bool? personally I like the bool, its less code, clean and it works.