Multiple cover area for enemies AI to take cover

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

I recently making AI behaviour for my FPS project, and I managed to make my enemy to chase my player character if player got too far away from enemy AI, and enemy AI will take cover by moving to the specific cover area if my player character move close enough to any of those enemies.

But I only managed to make them to use one cover area by moving toward it. I can make more individual cover area in the same scene and refer them separately in enemy AI script, but this will be inconvenient and may not work as I intended, as I want my enemy only choose to move to nearby cover.

Is there a way to group cover area and refers that group in enemy AI script?

Btw, here is my enemy AI code:

enum {IDLE, ALERT, CHASE, COVER}
var state = IDLE
var target = null
onready var cover_area = get_node("../CoverPoint") # get node called coverpoint

func _physics_process(delta: float) -> void:
    ...
	change_states(delta)

func _on_DetectionArea_body_entered(body):
    if body.name == "Player":
		target = body
		state = COVER

func _on_DetectionArea_body_exited(body):
	if body.name == "Player":
		if body.is_queued_for_deletion():
			target = null
			state = IDLE

func change_states(delta : float) -> void:
match state:
	...
	CHASE:
		Chase(delta)
	COVER:
		Covering(delta)

func Chase(delta : float) -> void: # Chase player once get far away from enemy
	look_at(target.global_transform.origin, Vector3.UP)
    direction = (target.global_transform.origin - global_transform.origin).normalized()
    Gravity_velocity(delta)

func Covering(delta : float) -> void: # Will seek for cover and shoot at player if close enough
	if global_transform.origin.distance_to(cover_area.global_transform.origin) > 1:
		direction = (cover_area.global_transform.origin - global_transform.origin).normalized()
	else:
		direction = Vector3()
	look_at(target.global_transform.origin, Vector3.UP)
	Gravity_velocity(delta)

func Gravity_velocity(delta : float) -> void:
...
	elif state == CHASE:
	velocity = velocity.linear_interpolate(direction * acrobatics.mov_spd,
	acrobatics.accel * delta)
	if !attack_timer.is_stopped():
		attack_timer.stop()
	if global_transform.origin.distance_to(target.global_transform.origin) < rush_dist:
		state = COVER

elif state == COVER:
	velocity = velocity.linear_interpolate(direction * acrobatics.mov_spd,
	acrobatics.accel * delta) # direction towards nearby cover instead of target
	if attack_timer.is_stopped():
		attack_timer.start()
	if global_transform.origin.distance_to(target.global_transform.origin) > rush_dist:
		state = CHASE
:bust_in_silhouette: Reply From: Wakatta

If your cover_area nodes are instanced through code

func ready():
    add_to_group("COVER")

if not for each cover_area node In the Node Dock on the groups tab type COVER then add

In your code
remove

onready var cover_area = get_node("../CoverPoint") # get node called coverpoint

change covering func to

# Will seek for cover and shoot at player if close enough
func Covering(delta : float) -> void: 
    var cover_areas = get_tree().get_nodes_in_group("COVER")
    var nearest_cover = cover_areas[0] #select first cover_area
    var nearest_pos = nearest_cover.global_transform.origin
    var position = global_transform.origin
	
	#find the nearest cover to this Ai's position
	for cover in cover_areas:
		var cover_pos = cover.global_transform.origin
		if position.distance_to(cover_pos) < position.distance_to(nearest_pos):
			nearest_cover = cover
			nearest_pos = cover_pos
			
	if position.distance_to(nearest_pos) > 1:
		direction = (nearest_pos - position).normalized()
	else:
		direction = Vector3()
	look_at(target.global_transform.origin, Vector3.UP)
	Gravity_velocity(delta)

Good, now my enemy AI will always seek for a nearest cover from multiple cover points, thanks for responding with such a working method!

Ngong8 | 2021-02-27 03:22