How to get closest enemy in array ?

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

I’m trying to get sort my enemies and get the closest enemy to the player node but i am unable to (not much of a programmer)

func generate_path():
var enemy_path = []
var distances
var shortest_distance = INF
if levelNav != null and is_instance_valid(enemy) != null:
	for e in get_tree().get_nodes_in_group("enemies"):
		var distance = 0
		for i in path.size():
			if i > 0:
				distance += path[i-1].distance_to(path[i])
			else:
				distance = position.distance_to(path[i])
		if distance < shortest_distance:
			shortest_distance = distance
			e = enemy
			enemy_path = path

this is what i was able to write with my limited knowledge of godot, but it does not work, thank you in advance!

:bust_in_silhouette: Reply From: Wakatta

Error

The error in your code stems from this line e = enemy assigning the loop var e.

Answer

Array has a custom_sort function you can use to do just that.

func sort_closest(a, b):
    return a.position < b.position

func get_closest_enemy():
    var enemies = get_tree().get_nodes_in_group("enemies")
    enemies.sort_custom(self, "sort_closest")
    return enemies.front()

Edit:
Answer to actual intention

func get_enemy():
    var closest_enemy = null
    var shortest_distance = INF
    for enemy in get_tree().get_nodes_in_group("enemies"):
        if not is_instance_valid(enemy):
            continue
        var distance = position.distance_to(enemy.position)
        if distance < shortest_distance:
            shortest_distance = distance
            closest_enemy = enemy
    return closest_enemy

func generate_path():
    var enemy = get_enemy()
    if levelNav != null and is_instance_valid(enemy):
        return levelNav.get_simple_path(position, enemy.position)
    return Array()
        

Then call the following whenever you need a path

path = generate_path()

Sorry im not quite sure how to incorporate this code into my generate_path() function, i’m not sure how to assign the closest path to the variable “path”, because i have this other function that uses the path variable :

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

I tried this but it doesn’t work

func generate_path():

var closest_enemy = null
var shortest_distance = INF
if levelNav != null and is_instance_valid(enemy) != null:
    for enemy in get_tree().get_nodes_in_group("enemies"):
        if not is_instance_valid(e):
            continue
        var distance = position.distance_to(enemy.position)
        if distance < shortest_distance:
            shortest_distance = distance
            closest_enemy = enemy

Also i am not sure about the bottom bit of code. thank you in advance, you’ve been a huge help already

Viltra | 2022-11-06 16:29

Sorry did not pay attention to your function names and only answered the question as you’d asked.

However suspect that your true question is How to generate a path to the closest enemy in an array? which is something else entirely.

Wakatta | 2022-11-06 17:01

yes exactly, i’d like to put all enemies into array and get the closest enemy and assign it to path which the navigate function can use

Viltra | 2022-11-06 17:07

Which navigation system are you using?

Astar?
Navigation Node?
Purely by code?

Wakatta | 2022-11-06 17:23

Navigation node with navigationpolygoninstance

Viltra | 2022-11-06 17:29

Adjusted to answer to fit your query.

Wakatta | 2022-11-06 18:42

Okay it works perfectly thanks!!!

but now there is a different problem, to elaborate more on my project : i have 5 Player nodes controlled by AI, and i have a function ignore_enemy() to make it so only one player focuses each enemy and if an enemy is aggroed, get the next enemy.

func ignore_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

but i don’t think it works anymore because all instanced player nodes follow the closest enemy now and don’t ignore it if already aggroed

Viltra | 2022-11-06 20:45