Player AI not able to find enemies even tho they are valid instances

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Viltra

So to explain a bit about my project, it is an idle dungeon crawler, 5 player nodes are spawned into a map and using their AI are able to find and navigate towards enemies and kill them, when the enemy dies they find a new one to path towards etc…

So far it works great with the first couple of enemies, but then at one point it breaks and the player node is not able to find an enemy again.

Here is the navigate() and generate_path() as well as find_enemy() functions responsible for this :

NAVIGATE FUNCTION:

func navigate() -> Vector2:
if path.size() > 0:
	while path.size() > 0 and global_position.is_equal_approx(path[0]):
		path.pop_front()
		if path.size() <= 0:
			reached_destination = true
			print(reached_destination)
			return Vector2.ZERO
	return global_position.direction_to(path[0]) * MAX_SPEED
return Vector2.ZERO

GENERATE_PATH FUNCTION :

func generate_path():
if levelNav != null && is_instance_valid(enemy) != null:
	path = levelNav.get_simple_path(global_position, enemy.global_position, true)

FIND_ENEMY FUNCTION :

func find_enemy():
enemy = null
for e in get_tree().get_nodes_in_group("enemies"):
		if not e.is_queued_for_deletion() && e.aggroed == null:
			enemy = e
			enemy.aggroed = self
			reached_destination = false
			return

VIDEO OF WHAT HAPPENS :

ALL THE PLAYER CODE IN PASTEBIN:

Thank you for your help in advance!

This looks suspect…

is_instance_valid(enemy) != null

is_instance_valid() returns a bool, so it should never be null. So, the above snippet will be true no matter what gets returned by that call. I wonder if that could be the source of your problems?

jgodfrey | 2022-11-07 20:29

I removed it, but its still the same issue

Viltra | 2022-11-07 20:36

Yeah, after watching the video and looking at the pastbin code, I think your generate_path() could be attempting to generate a path to an enemy that’s no longer valid… Drop the != null from the above snippet and see if that helps.

jgodfrey | 2022-11-07 20:38

Oh, looks like our comments overlapped… You removed what? Just the != null?

jgodfrey | 2022-11-07 20:39

func generate_path():
if levelNav != null && is_instance_valid(enemy):
	path = levelNav.get_simple_path(global_position, enemy.global_position, true)

still the same issue, btw in the map, the enemies are child nodes like this :
Imgur: The magic of the Internet

Viltra | 2022-11-07 20:43

yes, i even removed it from the LevelNav (navigation node) but it still doesn’t work

func generate_path():
if levelNav && is_instance_valid(enemy):
	path = levelNav.get_simple_path(global_position, enemy.global_position, true)

Viltra | 2022-11-07 20:52

any clue what might cause this issue besides this ?

Viltra | 2022-11-08 19:07

Is the project available for debugging?

jgodfrey | 2022-11-08 19:09

i can github it for you

Viltra | 2022-11-08 19:14

is there somewhere i can send it to you privately ?

Viltra | 2022-11-08 19:27

please tell me your discord or any other place we can communicate it so i can send you the repo

Viltra | 2022-11-08 19:37

Here’s a temporary, disposable email address you can use…

roboce1732@fgvod.com

jgodfrey | 2022-11-08 20:06

done, thank you once again for your help

Viltra | 2022-11-08 20:19

Hmmm… Seems that address has timed out. Can you send again to here:

shardae851@eqsaucege.com

jgodfrey | 2022-11-08 20:25

I’ve sent it

Viltra | 2022-11-08 20:40

Hmmm… That address works, but I haven’t received anything from you. What are you sending? I assumed just a link that I can use to download the project - you probably can’t send an attachment to that address. I’d suggest you upload the project somewhere that you can share a link with me - which should be fine to send to the posted address…

jgodfrey | 2022-11-08 21:04

i think it should work now, didn’t wanna post it publically but its fine

Viltra | 2022-11-08 21:05

you can find the player scene in scene/ then scroll down a bit, the map scene is in scenes/maps/dungeonmap1, and the enemy scene is a child of a node inside the map scene.

Viltra | 2022-11-08 21:29

Any luck with the issue ?

Viltra | 2022-11-08 22:40

Haven’t had much time to look, but I see the problem… I added this function to print the enemy state:

func print_enemies():
	print("*** Enemies ***")
	for e in get_tree().get_nodes_in_group("enemies"):
		print("%s, %s, %s" % [e.name, e.aggroed, e.is_queued_for_deletion()])
	print("----------")

And, then call it from find_enemy() like:

func find_enemy():
	enemy = null
	print_enemies()
    ....

If you do that, you’ll see the problem. Specifically, you end up a collection of enemies that already have a player targeting them (aggroed != null).

So, those enemies are never retargeted by this code:

if not e.is_queued_for_deletion() && e.aggroed == null:

It seems that a given player instance is retargeting a different enemy at some point, but leaving itself assigned to the previous enemy. So, that previous enemy can never be targeted again, because it’s already tagged as having been targeted.

Ultimately, you’ll see that the entire remaining list of enemies already has an aggroed assignment, so no additional enemies are ever targeted - even though additional enemies exist.

I assume this is being caused by the enemy_died signal that calls find_enemy, but I’m not 100% sure.

Since you’re familiar with the code, maybe that’s enough for you to find the underling cause?

jgodfrey | 2022-11-08 23:08

Thanks alot for this, this issue was driving me crazy…

I’m not much of a programmer, if you have the time could you please help me figure out a way to stop this from happening because i’m not that familiar with godot and gdscript

Thanks again!

Viltra | 2022-11-09 00:37

nvm fixed it

Viltra | 2022-11-09 01:21