Godot Version
v4.3
Question
I am trying to make a simple tower defense game and thought it would be pretty simple to make. It has been so far, but I have a glaring issue that I can’t seem to solve.
(FYI I call the enemies ‘mobs’ because why not)
Currently, each tower has it’s own ‘mob queue’ that keeps track of all mobs that enter and exit the tower’s radius. When the shoot timer finishes, the towers check if the mob queue is empty, and if it isn’t, they fire a projectile at the first mob in the queue. This works great for small amounts of mobs, but with even a slightly larger amount it starts to break.
If the mob is valid when checking if the queue is empty, and then dies (is freed) when the checks for the mob position happen, I get the error:
Invalid access to property or key ‘global_position’ on base object of type ‘previously_freed’
The error makes sense for the situation, but I don’t know how to solve it. Doing a check of is_instance_valid()
works to not give this error, but will cause the towers to not fire at all whenever the head of the queue is invalid, which lasts until that mob leaves queue. Attempts to remove the mob from the queue when invalid haven’t worked either.
The only other fix I could think of was reassembling the mob queue every time a mob entered or exited, making sure that the mob queue was fresh each check. This didn’t work and the towers would start firing at either the last mobs in the queue or the first. I think this might be because the mob queue doesn’t finish building before the shoot check happens.
My question is: what do I do now?
I’m fine using a completely different method for everything if the method I came up with is just fundamentally flawed, would only be a little butt hurt about it. If anyone has any tips or advice, it would be greatly appreciated. All the relevant code and attachments are below:
Tower Code
extends Node2D
# DEBUG
var id = 0
@onready var arrow_scene = preload("res://Projectiles/arrow.tscn")
# A queue of entities that are currently in the fire radius
# A mob will be added when they enter and removed when they leave
var mob_queue: Array[Area2D]
func _on_timer_timeout() -> void:
fire()
func fire():
if !mob_queue.is_empty():
var mob = mob_queue.front()
if is_instance_valid(mob): # This bypasses the error, but creates the bug
var arrow = arrow_scene.instantiate()
arrow.rotation = global_position.angle_to_point(mob.global_position) # This is normally where the error happens
add_child(arrow)
else:
print("Invalid instance of mob left in mob queue for ranger ", id)
print("Mob queue: \n", mob_queue)
func _on_area_2d_area_entered(area: Area2D) -> void:
if area.is_in_group("mob"):
mob_queue.append(area)
func _on_area_2d_area_exited(area: Area2D) -> void:
if area.is_in_group("mob"):
mob_queue.pop_front()