Best way to disable hurtboxes or hitboxes after first hit (set_deferred is too slow)

Godot Version

Replace this line with your Godot version

Question

So I have this code for the hitbox and hurtbox:
Apologies for the whacky code

Hitbox:

@export var damage: int = 1 : set = set_damage, get = get_damage
@export var stagger: Stagger
@export var stagger_damage : bool = false
@export var active : bool = false : set = set_active


@export var launch : bool = false
@export var knock_back : bool = false
@export var launch_strength : float = 40.0
@export var knock_back_strength : float = 100.0

@onready var shield_hit : bool = false


func _ready():
	connect("area_entered", _on_parried)

func set_active(_value:bool)->void:
	active=_value

func set_damage(value: int):
	damage = value
	
func get_damage() -> int:
	return damage

func _on_parried(_area :Area2D) -> void:
	if _area!= null:
		if _area.is_in_group("hitbox"):
			damage = 0
			knock_back = false
			launch = false
			clashed.emit()
		elif _area.is_in_group("player_hitbox"):
			damage = 0
			knock_back = false
			launch = false
			if _area.knock_back:
				clash_knock_back.emit(_area.knock_back_strength)
			elif _area.launch:
				clash_launch.emit(40)
			else:
				clashed.emit()
		elif _area.is_in_group("ParryBox"):
			#print_debug("parried!")
			stagger.stagger -= 1
			parried.emit()
		else:
			pass

Hurtbox:

class_name HurtBox
extends Area2D


signal received_damage(damage: int)
signal received_stagger_damage(damage: int)
signal got_hit(hitbox: HitBox)
signal bullet_hit(_damage: int)

signal parried()
signal weakpoint_hit()

signal launched(launch_strength : float)
signal knockback(knock_back_strength : float)

@export var health: Health
@export var stagger: Stagger
@export var dmg_mult : int = 1
@export var weakpoint : bool = false

@onready var total_damage : int = 0

@export var shielded : bool = false


func _ready():
	connect("area_entered", _on_area_entered)
	#connect("area_entered", _on_parried)
	connect("body_entered", _bullet_hit)

func _on_area_entered(hitbox: HitBox) -> void:
	
	if not hitbox.active:
		return
		
	
	elif health.health<=0:
		return
	else:
		if hitbox != null:
			#print(hitbox.knock_back)
			if hitbox.launch:
				launched.emit(hitbox.launch_strength)
			if hitbox.knock_back:
				#print("KNOCKING BACK")
				knockback.emit(hitbox.knock_back_strength)
		
			if hitbox.is_in_group("spc_atk"):
				weakpoint_hit.emit()
			if hitbox.stagger_damage:
				stagger.stagger -= (hitbox.damage * dmg_mult)
				### Maybe add minimum health damage to stagger attacks?  
				#print_debug(hitbox.damage, " ",dmg_mult)
				#received_damage.emit(hitbox.damage)
				got_hit.emit(hitbox)
			else:
				if weakpoint:
					health.health -= (hitbox.damage * dmg_mult)
					stagger.stagger -= (hitbox.damage * dmg_mult)
					#print_debug(hitbox.damage, " ",dmg_mult)
					received_damage.emit(hitbox.damage)
					got_hit.emit(hitbox)
				else:
					if not shielded:
						health.health -= (hitbox.damage * dmg_mult)
						#print_debug(hitbox.damage, " ",dmg_mult)
						received_damage.emit(hitbox.damage)
						got_hit.emit(hitbox)

func _bullet_hit(_rigid_body : RigidBody2D) -> void:
	var _damage : int 
	if _rigid_body.has_method("get_damage"):
		_damage=_rigid_body.get_damage()
	else:
		_damage=1
	if health.health<=0:
		return

	_rigid_body.hard_impact()

	
	if stagger.stagger>0:
		stagger.stagger-=1
	else:
		health.set_health(health.health-1)
		
		print_debug(health.health)
		#received_damage.emit(_damage)
		bullet_hit.emit(_damage)
		received_damage.emit(_damage)
	_rigid_body.impact()
		


func set_damage_mulitplyer(value:int):
	dmg_mult=value

func get_damage_mulitplyer() -> int:
	return dmg_mult

When hitbox and hurtbox, the character its attached to gets damaged. To keep from taking damage due to hitbox detecting collision every frame, I use this signal:

	#print(hit_buffer.time_left)
	if not hitbox.active:
		return
	else:
		#assert(hit_buffer.is_stopped()==true)
		#assert(hit_buffer.time_left<=0)
		hit_buffer.start(1)
		hitbox.active=false
		hit_sound=hit1
		AudioStreamManager.play(hit_sound)
		#hb_collision.disabled
		hb_collision.set_deferred("disabled", true)
		hit_fx.visible=true
		hit_fx_player.stop()
		hit_fx_player.play(hit_animation)

First I used “hb_collision.disabled” (the collisionshape for the hitbox) but was getting errors, so I switch to "“hb_collision.set_deferred(“disabled”, true)”, which got rid of the errors, but the hitbox still collided many times before disabling, instead of disabling on first collision.

Right now I’m trying to use an “active” flag on the hitbox, but it still sometimes hits a couple times before disabling. Anyone got any tips on the best ways of disabling hitbox on first collision?

Why not make a boolean for has attacked then when the hitbox collides wrap the logic in a if statement checking for that eg:

Signal →!is on cooldown → hurt logic

Then add to the hurt logic setting the cooldown variable to true

2 Likes

That’s the plan with the “active” flag: On collision, set active to false, code doesn’t run. Reactivate when starting new attack. Problem is it still detects more than one collisions before active is set to false.

It wouldn’t matter what it detects if you don’t act on it.

2 Likes

The issue is that it does act on it, the hurtbox still takes damage couple of times before hitbox becomes inactive.

But if you use a boolean yourself, and wrap your entire “act” code in it, then it doesn’t matter if the hitbox is still active, if your code ignores it. I think that’s what @normalized meant.

2 Likes

To clarify, “active” is a custom boolean I use, the code only actually functions if active is true. Otherwise, it’s entirely ignored.

Or atleast it should. Because, as you all said, wrapping my code in it should ignore it when “active” is false.

Problem is that is still activates a couple of times before “active” actually becomes false, and I’m trying to figure out why that is

Print hurtbox and hitbox in that signal handler to see which pair of them is interacting.

1 Like

Did some of that in the signal handler and hurtbox and hitbox codes, and might have revealed the issue. Looks like hurtbox’s code was completing before hitbox did, so it ran a couple times before hitbox got to the “active=false” part.

Made hurtbox deactive hitbox right at the beginning and that seemed to solve the issue. Not sure how stable that is yet but looking good so far