Explosive bullets not working

Godot Version

Godot 4

extends Node2D

const RANGE = 1200
const SPEED: int = 300
var travelled_distance = 0
var health = 100
var explosion_damage: int = 3
var explosion_radius: float = 100

func _physics_process(delta: float) -> void:
	position += transform.x * SPEED * delta



	###queue_free()


func _on_area_2d_body_entered(body: Node2D) -> void:
	if body.is_in_group("enemies"):
		explode()



func explode():
	var explosion_area = Area2D.new()
	var collision_shape = CollisionShape2D.new()
	collision_shape.shape = CircleShape2D.new()
	collision_shape.shape.radius = explosion_radius
	add_child(explosion_area)
	explosion_area.add_child(collision_shape)
	

	for body in explosion_area.get_overlapping_bodies():
		if body.has_method("take_damage"):
			body.take_damage(explosion_damage)  

Question

I am trying to make my bullets change into explosions on impact, I get no errors yet its still not working, for some reason the explode() function doesn’t even get activated because no Area2D has been created after touching the enemy

btw I use a Node2D and have Area2D as a child

Do your bullets have collision detection? If so, do your bullets stop around the impact area or go straight through the enemy?

func _on_area_2d_body_entered(body: Node2D) -> void:
	if body.is_in_group("enemies"):
		explode()

Isn’t this a collision detector?
it goes thru

I mean does your bullet mesh have a collisionshape associated with it and have you checked to see if it works e.g. when bullet hits enemy, print something?

oh… well yeah I have a CollisionShape2D as a Child of Area2D

func _on_area_2d_body_entered(body: Node2D) -> void:
        print("ok")
	if body.is_in_group("enemies"):
		explode()

for some reason that didn’t print anything

A couple of points for you to check:

  1. Is your enemy a CharacterBody2D? Because the body_entered signal works only on the inherited classes from PhysicsBody2D, which is CharacterBody2D, RigidBody2D or StaticBody2D.
  2. Did you connect the body_entered signal of the Area2D in the inspector? Your method should have this green arrow symbol next to it.
    obraz
  3. The monitoring of the Area2D should be turned on
    obraz
  4. The collision_mask of your Area2D should be the same as collision_layer of your enemy
    obraz
1 Like

Thanks, the body entered signal wasn’t connected, now it prints ok. Also I just found out that I had a spelling problem with my groups so thats why the explode func wouldn’t work…

the explosion still doesn’t do any damage

	for body in explosion_area.get_overlapping_bodies():
		if body.has_method("take_damage"):
			body.take_damage(explosion_damage)  

my enemy has the take damage func so it should work

Show your enemy class script please.
What happens if you add these 2 print statements? Which of these get printed (if any)?

	for body in explosion_area.get_overlapping_bodies():
		print("should be damaged")
		if body.has_method("take_damage"):
			print("has take_damage method")
			body.take_damage(explosion_damage)  
1 Like

this is all my enemy script

extends CharacterBody2D

var health = 3
@onready var player = get_node("/root/game/player")


func _physics_process(delta: float) -> void:
	var direction = global_position.direction_to(player.global_position)
	velocity = direction * 200
	move_and_slide()
	

func take_damage():
	health -= 1
	
	if health == 0 :
		queue_free()

for some reason I get an error and no prints when the bullets touch the enemy

E 0:00:02:0766 bomb.gd:30 @ explode(): Can’t change this state while flushing queries. Use call_deferred() or set_deferred() to change monitoring state instead.
<C++ Error> Condition “area->get_space() && flushing_queries” is true.
<C++ Source> servers/physics_2d/godot_physics_server_2d.cpp:355 @ area_set_shape_disabled()
bomb.gd:30 @ explode()
bomb.gd:20 @ _on_area_2d_body_entered()

Can we see your other scripts too?

You call your take_damage method with the explosion_damage parameter:

body.take_damage(explosion_damage)

But your take_damage method doesn’t take any parameter:

func take_damage():
	health -= 1
	
	if health == 0 :
		queue_free()

Update your method to accept the damage amount.

func take_damage(amount: float):
	health -= amount
	
	if health <= 0 :
		queue_free()
1 Like

oh damn thats what I get for copying code from my other games, thanks, I still have some issues but I think I can figure it out and post the solution, if not I will ask soon

2 Likes

because I got the “Can’t change this state while flushing queries. Use call_deferred()”
I added call_deferred() here

func explode():
	var explosion_area = Area2D.new()
	var collision_shape = CollisionShape2D.new()
	collision_shape.shape = CircleShape2D.new()
	collision_shape.shape.radius = explosion_radius
	add_child(explosion_area)
	explosion_area.call_deferred("add_child", collision_shape)

now the game straight up crashes and just says breakpoint

the breakpoint happens here in the first line

for body in explosion_area.get_overlapping_bodies():
		print("should take damage")
		if body.has_method("take_damage"):
			print("dmg")
			body.take_damage(explosion_damage)