I quite frankly don’t understand why this situation keeps happening. I don’t know if it is with the Area3D or the functions themselves. It’s probably a simple solution, but I just can’t seem to get over it yet. (Sorry, I am a beginner in Godot).
The way I have it setup is, once the bomb explodes, there is an explosion part that is separate and checks for the group “bombs” in its Area3D if it is colliding with it. The explosion parts get spawned by a spawner on top of where the bomb was. I don’t get why after a certain iteration of its chain explosion, it just would not work entirely. There are no errors showing up in the debugger, but I can see that it is not accessing the function in which it explodes via chain (as seen by the “NORMAL EXPLOSION” and “EXPLODED VIA CHAIN” prints in the output).
I feel as though it will be an overall issue once I add in the explosions for the other directions, as I’m trying out the explosion for one direction right now. And I’d be glad to share each script regarding the problem:
Bomb script:
class_name UniversalBombStats
extends Node
@onready var timer = $Timer # Used to call upon the timer
@onready var Collision = get_node("Bomb_PH/StaticBody3D/CollisionShape3D")
@onready var SpawnScene = preload("res://scenes/objects/spawn_node.tscn")
@onready var ExplosionScene = preload("res://scenes/objects/explosion.tscn")
@onready var player = preload("res://scenes/characters/placeholderChar.tscn")
@export var BombTimer : float = 5
var out = false
var rng = RandomNumberGenerator.new()
func _ready():
timer.wait_time = BombTimer # Set the timer
timer.start()
wait_for_bomb(0.05) # How long to wait til bomb is visible again
# Clever wait function, will use in future projects
func wait_for_bomb(seconds: float) -> void:
await get_tree().create_timer(seconds).timeout
self.visible = true
func _on_timer_timeout() -> void:
PlayerStats.BombCount += 1
normal_explode()
func normal_explode():
# Bom explodes via time
# TL:DR will spawn the bomb as a new Node3D
var spawner = SpawnScene.instantiate()
# Any adjustments to be made not connected to the scene should be put here
# Select the parent for the node
var parent = self.get_parent() # Parents to the level node
# Change stuff regarding the scene structure within here
queue_free() # + 6
spawner.name = "spawnNode"
spawner.position = Vector3(self.global_position.x, 5, self.global_position.z)
parent.add_child(spawner)
print("NORMAL EXPLOSION")
#print(parent.get_children())
# Bomb explodes by chain explosion
func chain_explode():
if PlayerStats.BombCount < PlayerStats.BombLimit:
# TL:DR will spawn the bomb as a new Node3D
timer.stop()
var spawner = SpawnScene.instantiate()
# Any adjustments to be made not connected to the scene should be put here
# Select the parent for the node
var parent = self.get_parent() # Parents to the level node
# Change stuff regarding the scene structure within here
queue_free()
spawner.position = Vector3(self.global_position.x, 5, self.global_position.z)
parent.add_child(spawner)
PlayerStats.BombCount += 1
print("EXPLODED VIA CHAINING")
Explosion Script:
extends Node3D
@onready var timer = $Timer # Used to call upon the timer
@onready var raycast = $ExplosionBox/RayCast3D
@export var ExplosionTimer : float = 0.5
var rng = RandomNumberGenerator.new()
# TODO: Make the explosion not go through both border and stage walls.
func _ready() -> void:
timer.wait_time = ExplosionTimer
timer.start()
func _process(delta: float) -> void:
pass
func _on_timer_timeout() -> void:
queue_free()
func _on_area_3d_area_entered(area: Area3D) -> void:
var objectHit = area.get_parent()
print(raycast.get_collider())
if objectHit.is_in_group("bkWalls"):
objectHit.queue_free()
# Used for chain exploding
if objectHit.is_in_group("bomb"):
objectHit.chain_explode()
func _on_area_3d_body_entered(body: Node3D) -> void:
if body is CharacterBody3D:
print(body.name, " got hit!")
From further testing though, I think it’s a problem with my setup for the Area3Ds(?), in the current video I haven’t figured out a way how to despawn the overlapping explosions, as it should keep only one explosion cell for a specific tile. I tested it earlier with the explosion range of 2 cells and everything seemed to worked out fine, further leading me to believe it might be due to the Area3Ds when they overlap with each other a bunch (just speculation though, might be wrong about it entirely).
How are your collision layers and masks set up for the bombs? I would recommend the explosions only mask for bombs and other destructables. Could you also move the explosion area3D to be part of the initial bomb, checking get_overlapping_areas() when it explodes instead of the area_entered signal when it spawns?
I just started Godot, so with collision layers and masks, I’m gonna be completely honest, I have no idea what I’m doing with them. I mean the most that I can do is match the masks of the bombs and the explosions themselves, and with your suggestion, onto other breakable objects as well.
As for the explosion Area3D, could you elaborate further on what you mean? Because I have a setup right now where an Area3D is in the initial bomb, and also have a separate one for the explosion.
I still don’t know how get_overlapping_areas() work, but I’m currently testing out what you’ve laid out. So far for it to work, it should be that the Area3D of the generated explosions would check to see if their is a bomb overlapping them, does it already check that with the collision masks already setup? And if so, I am hoping it does not break the function for chain exploding.
I would try out this layer/mask combo, I selected 3 because I figure it would be out of the way, I like to keep 1 for world collision, of course you can add more things to layer 3 if they are to be hit by the bombs, like your “bkWalls” should added to layer 3, and your Players.
For the script, try this? I would have to make a lot of assumptions on your scene tree so I will use my own example scene.
extends Node3D
@onready var chain_hit_box: Area3D = $ChainHitBox
@onready var explosion_box: Area3D = $ExplosionBox
@export var bomb_timer: float = 5.0
func _ready() -> void:
chain_hit_box.add_to_group("Bomb")
await get_tree().create_timer(bomb_timer).timeout
normal_explosion()
func normal_explosion() -> void:
if is_queued_for_deletion():# overlapping hits cause recursion
return
queue_free()
for hit in explosion_box.get_overlapping_areas():
if hit.is_in_group("Bomb"):
hit.get_parent().normal_explosion()
elif hit.is_in_group("bkWalls"):
hit.queue_free()
for hit in explosion_box.get_overlapping_bodies():
if hit is CharacterBody3D:
print(hit.name, " got hit!")
I want to know more about what this does, and why it fixes my whole thing. For the longest time I thought it was because of some kind of overflow from all of the overlapping areas and queue_freeing all of them (which I think it probably is?), but I would have never known the solution would be this:
As I implemented these lines onto both my normal and chain explosion functions, voila, it worked! Thanks for the suggestions! I need to look into more of the collision layers and masks though as I think they’re gonna be super helpful down the line. I’ll try and experiment with your setup with the collision masks.
Also I get what you mean by this now:
I didn’t know you could call and use the Area3D itself instead of signals for checking:
I’ll make note of that instead of using signals. Well that begs the question, what differentiates them from using signals? Is it because the entered and exited signals only account for their specific actions and the overlapping_area() code accounts for anything that collides with them overtime?
I would also like to say (again), thank you kindly .
It’s a way to stop the function if the bomb has already exploded. A has_exploded variable may be more useful when animating the bomb exploding; but for now checking if the bomb is queued for deletion works to prevent double-triple-infinite explosions. The recursion part is because I used the same normal_explosion function even for chained explosions, so bomb A triggers bomb B, but bomb B’s hitbox also triggers bomb A so they would be stuck triggering chain explosions between the two. Checking if bomb A has_exploded prevents that recursion.
Use the signal body_entered when you want a function to run the exact moment something enters the area. get_overlapping_bodies is great for checking multiple bodies within the area when something else happens. Since you are waiting for the bomb to explode instead of waiting for something to walk into the bomb, get_overlapping_bodies fits better, the action depends on the timer going off.