How to make enemy follow several targets?

Godot Version

4.2.1

Question

Hi, I’m trying to make a 2d RTS game and I wrote some code for enemy following one target:

extends State
class_name EnemyFollow

@onready var animation = $"../../AnimationPlayer"

var target: CharacterBody2D
var rebels_arr = []


func Enter():
	target = get_tree().get_first_node_in_group("rebels")



func Physics_Update(_delta: float):
	var direction = target.global_position - enemy.global_position
	
	if direction.length() >= 40:
		nav.target_position = target.global_position
	else:
		nav.target_position = enemy.global_position
	
	if rebels_arr.size() == 0:
		Transitioned.emit(self, "idle")


func _on_see_area_body_entered(body):
	if body.is_in_group("rebels"):
		rebels_arr.append(body)
		target = rebels_arr[0]
		print(target)




func _on_see_area_body_exited(body):
	rebels_arr.erase(body)

Here I have var target, which should switch between character bodies in group “rebels”, but for some reason, my enemy only follows one target, which specified here:

func Enter():
	target = get_tree().get_first_node_in_group("rebels")
func _on_see_area_body_entered(body):
	if body.is_in_group("rebels"):
		rebels_arr.append(body)
		target = rebels_arr[0]

I wonder if there are some way to don’t specify it as first node and make it so this line would work:

func Physics_Update(_delta: float):
	var direction = target.global_position - enemy.global_position

When you append to an array (rebels_arr.append(body)), the appended element becomes the last element of the array. So if you want the newly discovered rebel to become the target, you need to do:

func _on_see_area_body_entered(body):
	if body.is_in_group("rebels"):
		rebels_arr.append(body)
		target = body

If instead you want it to pick a random target, you can do something like:

target = rebels_arr[randi() % rebels_arr.size()]

In order to use randomness, like the randi function used above, you also need to initialize the random generator. This should be done exactly once when the game starts, by calling the function randomize(), for example in the _ready function of some script that you know will only have one instance.

I think I my problem might be more stupid than I thought.

Here I have this function, so when body coming closer to enemy, target is automatically becoming first node in group again

func Enter():
	target = get_tree().get_first_node_in_group("rebels")

But without it, I can’t do this:

func Physics_Update(_delta: float):
	var direction = target.global_position - enemy.global_position

Is there any way to specify target, so it would have global position and won’t actually become target everytime when code runs? There might be some simple solution for this but I’m trying to do it for several hours and my brain refuses to work tho

So, I don’t know how your state machine setup works, but it sounds like the Enter function gets called more often that I thought? So it overwrites the target when you don’t want it to?

You could do:

if target == null:
    target = get_tree().get_first_node_in_group("rebels")

That way, it’ll only do anything if the target has never been set.

Well, it seems that my Enter function only reacting for node that is first in my
hierarchy and I started working with state machines recently, so I don’t really fully understand how it works. Thanks for helping, but it seems that I’ll have to do some more researching to make what I want to

Yeah, it’ll help if you know when the different functions get called and such.