Enemy pathfinding AI

This commit is contained in:
Marco 2025-01-31 16:45:31 +01:00
commit f0eee13e57

View file

@ -12,6 +12,8 @@ public partial class Enemy : CharacterBody2D
[Export] public float WalkSpeed = 2500f; [Export] public float WalkSpeed = 2500f;
[Export] public float PlayerDisengageRange = 500f;
[Export] public Weapon EquippedWeapon; [Export] public Weapon EquippedWeapon;
private float _currentHealth = 0f; private float _currentHealth = 0f;
@ -20,6 +22,10 @@ public partial class Enemy : CharacterBody2D
private NavigationAgent2D _navigationAgent; private NavigationAgent2D _navigationAgent;
private bool _isPlayerInRange = false;
private Vector2? _lastPlayerPosition = null;
[Export] public bool NavigationEnabled { get; set; } = false; [Export] public bool NavigationEnabled { get; set; } = false;
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
@ -29,13 +35,13 @@ public partial class Enemy : CharacterBody2D
_navigationAgent = GetNode<NavigationAgent2D>("NavigationAgent2D"); _navigationAgent = GetNode<NavigationAgent2D>("NavigationAgent2D");
CallDeferred("Setup"); //CallDeferred("Setup");
} }
private void Setup() // private void Setup()
{ // {
//
} // }
// Called every frame. 'delta' is the elapsed time since the previous frame. // Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta) public override void _Process(double delta)
@ -45,10 +51,7 @@ public partial class Enemy : CharacterBody2D
case EnemyState.Idle: case EnemyState.Idle:
break; break;
case EnemyState.Alert:
case EnemyState.Primed:
// Have the raycast follow the player and shoot if visible
//HandlePlayerDetection();
break; break;
//case EnemyState.Shooting: //case EnemyState.Shooting:
@ -61,17 +64,29 @@ public partial class Enemy : CharacterBody2D
public override void _PhysicsProcess(double delta) public override void _PhysicsProcess(double delta)
{ {
if (NavigationEnabled) switch (_currentState)
{ {
// Only do these actions if the enemy has been alerted case EnemyState.Idle:
if (_currentState is EnemyState.Primed) HandlePlayerDetection();
{
_navigationAgent.SetTargetPosition(_cachedPlayer.GlobalPosition); break;
case EnemyState.Alert:
// Update last known player position if it's in range
if (_isPlayerInRange)
{
_lastPlayerPosition = _cachedPlayer.GlobalPosition;
}
if (_lastPlayerPosition.HasValue)
{
_navigationAgent.SetTargetPosition(_lastPlayerPosition.Value);
}
var currentAgentPosition = GlobalPosition; var currentAgentPosition = GlobalPosition;
var nextPathPosition = _navigationAgent.GetNextPathPosition(); var nextPathPosition = _navigationAgent.GetNextPathPosition();
var newVelocity = currentAgentPosition.DirectionTo(nextPathPosition) * (float)(WalkSpeed * delta); var newVelocity = currentAgentPosition.DirectionTo(nextPathPosition) * (float)(WalkSpeed * delta);
@ -79,7 +94,10 @@ public partial class Enemy : CharacterBody2D
if (_navigationAgent.IsNavigationFinished()) if (_navigationAgent.IsNavigationFinished())
{ {
// Shoot player // Shoot player
HandlePlayerDetection(); Shoot();
// TODO: If player totally left the max range it should stop shooting and go back to idle
return; return;
} }
@ -95,7 +113,11 @@ public partial class Enemy : CharacterBody2D
MoveAndSlide(); MoveAndSlide();
} break;
case EnemyState.Patrolling:
break;
default:
throw new ArgumentOutOfRangeException();
} }
} }
@ -107,7 +129,7 @@ public partial class Enemy : CharacterBody2D
private void HandlePlayerDetection() private void HandlePlayerDetection()
{ {
if (_cachedPlayer == null || _currentState is not EnemyState.Primed) if (_cachedPlayer == null)
{ {
return; return;
} }
@ -119,17 +141,18 @@ public partial class Enemy : CharacterBody2D
// { // {
// _navigationAgent.SetTargetPosition(_cachedPlayer.GlobalPosition); // _navigationAgent.SetTargetPosition(_cachedPlayer.GlobalPosition);
// } // }
Shoot(); //Shoot();
_currentState = EnemyState.Alert;
} }
} }
private void Shoot() private void Shoot()
{ {
if (EquippedWeapon == null) return; if (EquippedWeapon == null || !_lastPlayerPosition.HasValue) return;
EquippedWeapon.ShootDirection = (_cachedPlayer.GlobalPosition - this.GlobalPosition).Normalized(); // Shoot at the player's last known position
EquippedWeapon.ShootDirection = (_lastPlayerPosition.Value - this.GlobalPosition).Normalized();
EquippedWeapon.Shoot(); EquippedWeapon.Shoot();
@ -176,19 +199,23 @@ public partial class Enemy : CharacterBody2D
Debug.WriteLine("Enemy detection area Entered by interaction controller"); Debug.WriteLine("Enemy detection area Entered by interaction controller");
_cachedPlayer = player; _cachedPlayer = player;
if (_currentState is EnemyState.Idle)
{ _isPlayerInRange = true;
_currentState = EnemyState.Primed; // if (_currentState is EnemyState.Idle)
} // {
// _currentState = EnemyState.Primed;
// }
} }
} }
private void _on_player_detection_area_exited(Area2D area) private void _on_player_detection_area_exited(Area2D area)
{ {
if (_currentState is EnemyState.Primed) _isPlayerInRange = false;
{
_currentState = EnemyState.Idle; // if (_currentState is EnemyState.Primed)
} // {
// _currentState = EnemyState.Idle;
// }
} }
private void _on_damage_hitbox_area_entered(Area2D area) private void _on_damage_hitbox_area_entered(Area2D area)
@ -231,7 +258,7 @@ public partial class Enemy : CharacterBody2D
private enum EnemyState private enum EnemyState
{ {
Idle, Idle,
Primed, Alert,
Patrolling Patrolling
} }
} }