Do you guys know to make any a cover system for an enemy AI in a first person shooter?
I've been trying to figure it but, I keep failing. I ask ChatGPT for help and it doesn't work out for me. I've search Youtube high and low for a tutorial but, all I got was a video titled "Godot 4. 3D Enemy Shooting and Cover system" by Youtuber Raku Nana; I'll link it right here:
I assume the video has good infromation in it but, it seems unscripted and difficult to follow.
It's easier to come up with code that tells the enemy where to find the nearest cover and I'll even share with you what I've got right here:
func nearest_available_cover():
var covers = get_tree().get_nodes_in_group("Cover")
var closest_cover = null
var min_distance_squared = INF # Use squared distance for efficiency
var my_position = global_transform.origin
for cover : Cover_Area in covers: # Check if the cover is valid
if not is_instance_valid(cover) or cover == self:
continue
var their_position = cover.global_transform.origin
var distance_squared = my_position.distance_squared_to(their_position)
# Check if this is the new closest object
if distance_squared < min_distance_squared:
min_distance_squared = distance_squared
closest_cover = cover
return closest_cover
I've even had some code in the cover to helps it to communicate to the enemy that it's safe and available( though it's function isn't being called in the current iteration of the nearest_available_cover() function). So here's what I find challenging.
- Getting the enemy To move to available cover that's away from the player.
- Getting the enemy to avoid moving to cover that other enemies are moving towards or talking.
I don't even know where to start when it comes to solving number 2. I've only showed one function from my enemy's code but, everything is a bit more complex on my end; I'm working with a node based finite state machine I'm trying to take advantage of it as much as possible.
The cover points I use are Area3d. I'm not sure if you anyone here needs to see the script for it but, here it is anyway:
extends Area3D
class_name Cover_Area
@onready var mesh = $%MeshInstance3D
@onready var raycast : RayCast3D = $%RayCast3D
@export var cover_point_model_visibility = true
var safe = true
var player : CharacterBody3D = null
var available = true
func _ready():
body_entered.connect(entered_cover)
body_exited.connect(leave_cover)
if cover_point_model_visibility == false:
mesh.visible = false
if get_tree().get_first_node_in_group("Player") != null:
player = get_tree().get_first_node_in_group("Player")
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
pass
func safety():
if player != null and is_instance_valid(player):
raycast.look_at(player.global_transform.origin,Vector3.UP)
if raycast.is_colliding():
var target = raycast.get_collider()
if target.is_in_group("Player"):
safe = false
elif target.is_in_group("Enemy"):
raycast.add_exception(target)
else:safe = true
else:safe = true
func safe_and_available():
if safe == true and available == true:
return true
else:
return false
func entered_cover(body):
if body.is_in_group("Enemy"):
pass
func leave_cover(body):
if body.is_in_group("Enemy"):
pass
So can you help me to come up with any idea for the cover system? Any pseudo-code or links to helpful articles would be greatly appreciated. You can also go to ChatGPT, ask it for help and then share what it showed you if it's worth showing; perhaps it would be more helpful to you. Old games like Perfect Dark for the n64 had a solution to this problem; I wonder what it's like and how complex it was. 🤔