My explosive projectile is only half working

Godot Version

4.4.1

Question

Hello to all!

Still making my vampire survivor clone and i’m trying to add a projectile that explodes on contact or when the projectile has travelled a certain distance.

I used 2 Area2D for this, the 1st one being the hit detector, the 2nd one being the explosion and is a child of the first, this one is disabled by default and is enabled when it is time to explode.

Here comes the issue, when the projectile reach max range and explodes, it deals damage as expected when an enemy enters the explosion radius; but when the projectile hits an enemy and explodes, it does not deal any damage even when other enemies enter the explosion radius.

I think it’s because i’m using the “area_entered” to damage my enemy but i am confused as to why it must reach max range to deal any damage at all

anyway here is the relevant code of my projectile’s hit detector

const EXPLOSION_DURATION := 0.5
const SPEED := 1000
const RANGE := 500

func _physics_process(delta: float) -> void:
	var direction = Vector2.RIGHT.rotated(rotation)
	position += direction * SPEED * delta

	travelled_distance += SPEED * delta
	if travelled_distance > RANGE:
		attack()

func attack() -> void:
	set_physics_process(false)
	hit_detector.disabled = true
	hit_box.disabled = false
	explosion_timer.wait_time = EXPLOSION_DURATION
	explosion_timer.start()

func _on_area_entered(area: Area2D) -> void:
	if area.is_in_group("enemy"):
		attack()

here is the intitial code that handles the hit detection in my enemy:

func _on_area_entered(area: Area2D) -> void:
	if area.is_in_group("attack"):
		var damage = area.get_damage()
		stats.health -= damage
		if stats.health < 0:
			queue_free()
			enemy_death.emit(stats, position)

i tried moving the hit detection in my “_process” function and using the overlapping areas like this but nothing changed:

	if hurt_box.has_overlapping_areas():
		var overlaps = hurt_box.get_overlapping_areas()
		for area in overlaps:
			if area.is_in_group("attack"):
				var damage = area.get_damage()
				stats.health -= damage
				if stats.health < 0:
					queue_free()
					enemy_death.emit(stats, position)

(edit) here is a screenshot of my fireball scene

and here is a video of my fireball in action

https://youtu.be/TYGnm0J6Hx0

I can’t really know the cause without seeing the scene tree, the definitions of some variables (like damage or health), and maybe a video of what is happening.

With what I can see, I suspect some signal isn’t connected right, or maybe the health variable isn’t going below zero (maybe use stats.health <= 0?).

If you can provide some more information, maybe I can help further. Thanks!

A few thoughts:

  1. Disabling the Area2D could be your problem. Your code makes it unclear what you’re disabling. You need to be disabling the CollisionShape2D. This is likely your problem. Re-enabling the CollisionShape2D allows it to run the detection. re-enabling the Area2D does not.
  2. There’s no real reason to make one Area2D the parent of the other. It can have unintended effects.
  3. Unless the enemies are Area2D nodes, you should be using the body_entered Signal, not area_entered.
1 Like

I think it could be because the area2d does not trigger on enemies that are already within the area it covers when instantiated.
When the area spawns, have it check which other enemy areas are within it.

thank you all for your answers, i’ll look into what you suggested, in the meantime i edited my post to provide a screenshot of my fireball scene and a short video displaying the issue.

Now that i’m writing this, i’ll provide more info about how i did my damage detection here so bear with me please.

In short, it happens in my enemy script, if have an “area_entered” signal that looks for areas in the “attack” group, if it detects one, it calls the “get_damage” function of the area to then lower the enemy health by this amount and killing it if its health drops at or lower than 0 and emiting a signal to spawn the experience for the player to collect.

I’m 90% sure that it is because the area doesn’t count something as entering if it’s already there and that you can solve it by adding a _ready() for an initial check and do damage to whatever is there, and adding any print statement to the on_area_entered just to see if it triggers or not on whatever is already there.

1 Like

that’s what i thought too but that doesn’t explain why the enemies that enters the area after it explodes don’t take damage if the explosion was triggered by hitting an enemy

You’re supposed to change the disabled value via set_deferred(), like this:

	hit_detector.set_deferred("disabled", true)
	hit_box.set_deferred("disabled", false)

Since the only obvious difference between both attack() calls seems to be the timing (one is in _physics_process(), the other in response to a signal), I guess that could be the issue?

From my understanding, it looks like you are trying to have one thing complete two tasks with a single set of instructions. That’s difficult by nature. I would suggest that you create a separate explode() function.

If the fireball position exceeds the range, just explode()

If the fireball hits a target within range, attack() the target, then explode()

I don’t think the enemy should be checking itself for encountering a fireball or an explosion.

The fireball should check if it hits an enemy and then deal_damage_to_enemy(area : enemy, fireball_damage)

After it deals damage, it should explode, so maybe have the fireball be in a State.EXPLODED and when it enters an area it only deal_explosion_damage(area : enemy, explosion damage)

May I ask why are you toggling the collision detection? It seems counter intuitive to me. Edit: wew lad I re-read the second paragraph. I would also suggest you just have one fireball area and change the sprites texture and the collision radius when the fireball is State.EXPLODED

Baba might be right but I know I had something setup where a power up would spawn every 3 seconds in a small map that I made and when it spawned with its huge radius and multiple characters were in that radius they all got the power up simultaneously. I don’t know if that’s tied to ready() or not though.