Player hurt logic doesn't work properly

Godot Version

Godot 4.3.stable.steam

Question

TL;DR
I have been struggling around 8 months with my hurt logic to work properly, but never have I ever been able to do so. (I’m still very beginner at programming).

So if player enters hitbox, player takes damage → invulnerability timer starts. Now finally if player stays colliding with the hitbox, player takes continuous damage every time invulnerability timer runs out, but the problem I have been having is:

if player collides with hit box → player takes damage → invulnerability timer starts → player leaves hitbox and re-enters while the invulnerability timer is still running → player stays inside hitbox after invulnerability timer stops → player can keep colliding with the hitbox as long as player wishes until leaving and re-colliding again.

Code:

var is_hurt: bool = false
var is_dead: bool = false

@export var invulnerability_time : float = 2.0

var is_inside_hitbox: bool = false

@onready var is_invulnerable_time_enabled = invulnerability_time > 0
@onready var invulnerability_timer = Timer.new()

func _ready():
##my other codes##

	if is_invulnerable_time_enabled:
		add_child(invulnerability_timer)
		invulnerability_timer.wait_time = invulnerability_time
		invulnerability_timer.one_shot = true
		invulnerability_timer.timeout.connect(_on_invulnerability_timeout)

##rest of my orther codes##

func start_invulnerability_timer():
	if is_invulnerable_time_enabled:
		invulnerability_timer.start()

func start_hurt_animation():
	if not is_invulnerable_time_enabled and not is_dead and (not animated_sprite_2d.is_playing() or animated_sprite_2d.animation != "player_hurt"):
		animated_sprite_2d.play("player_hurt")

func is_invulnerability_timer_running() -> bool:
	return not invulnerability_timer.is_stopped()

func _on_invulnerability_timeout():
	if is_inside_hitbox and !is_dead:
		currentHealth -= 1
		healthChanged.emit(currentHealth)

		if currentHealth <= 0:
			currentHealth = 0
			is_dead = true
			animated_sprite_2d.play("player_ded")
			lifeLost.emit()
		else:
			if not is_flashing:
				is_flashing = true
				flash_times = 0
				animated_sprite_2d.play("player_hurt")
			is_hurt = true
			start_hurt_animation()
		start_invulnerability_timer()

func _on_hurt_box_area_entered(area):
	if area.name == "hitBox":
		if not is_invulnerability_timer_running():
			if not is_inside_hitbox:
				is_inside_hitbox = true
			currentHealth -= 1
			healthChanged.emit(currentHealth)
			print("Current Health: ", currentHealth)
			if currentHealth <= 0:
				currentHealth = 0
				is_dead = true
				animated_sprite_2d.play("player_ded")
				lifeLost.emit()
			else:
				if not is_flashing:
					is_flashing = true
					flash_times = 0
					animated_sprite_2d.play("player_hurt")
				is_hurt = true
				start_hurt_animation()
			start_invulnerability_timer()

func _on_hurt_box_area_exited(area):
	if area.name == "hitBox":
		is_inside_hitbox = false

If you got any further questions about the issue, let me know.
I am well aware that this is possibly spagetti like, so my apologies!
It has been re-written / altered like 50 times now.

(There are more related to the code like sprite flash and respawn function, but those definitely doesn’t affect the issue so I left them off).

Thank you for checking this post and any help would be greatly appreciated!

Your current code has two main problems:

When the player is inside the hitbox and the invulnerability timer ends, you correctly apply damage in _on_invulnerability_timeout(), but you’re not handling the continuous damage cycle after that.

The is_inside_hitbox flag gets set when entering the hitbox, but if the player is already invulnerable, they don’t take damage right away (which is correct), but you’re not properly restarting the damage cycle.

In _on_hurt_box_area_entered, always set is_inside_hitbox = true when entering, regardless of invulnerability. This ensures the game knows the player is in the hitbox.

In _on_invulnerability_timeout, always restart the invulnerability timer if the player is still in the hitbox. This creates a continuous cycle of damage → invulnerability → damage as long as the player remains in the hitbox.

And boom bota pow. Should work the way you want it to? I can write out the full code for you if you need help. But I’d highly suggest writing it out on your own cause this shit really helps you get good at the nitty-gritty.

1 Like

Thank you for the help! Wouldn’t have been figured out myself.
It took me little brainstorm to figure what to do, but the solution was surprisingly tiny. :flushed:

The code seems to work properly now, player stays inside hitbox, it takes damage as soon as invulnerability timer runs out. If player takes damage leaves hitbox and re-enters while still invulnerable, player takes damage immediately after the timer runs out now! :fire:

1 Like