mirror of
https://gitlab.com/MaddoScientisto/cirnogodot.git
synced 2026-07-06 03:41:16 +00:00
Enemy strafing
This commit is contained in:
parent
a18dc11eb3
commit
b35772f519
6 changed files with 112 additions and 16 deletions
|
|
@ -108,11 +108,12 @@ DamageReceiver = NodePath("../../DamageReceiver")
|
||||||
NavigationModule = NodePath("../../NavigationModule")
|
NavigationModule = NodePath("../../NavigationModule")
|
||||||
_moduleNodes = [NodePath("../../AnimationModule")]
|
_moduleNodes = [NodePath("../../AnimationModule")]
|
||||||
|
|
||||||
[node name="Shooting" type="Node2D" parent="StateMachine" node_paths=PackedStringArray("StorageModule", "PlayerDetection", "DamageReceiver", "EquippedWeapon", "_moduleNodes")]
|
[node name="Shooting" type="Node2D" parent="StateMachine" node_paths=PackedStringArray("StorageModule", "PlayerDetection", "DamageReceiver", "NavigationModule", "EquippedWeapon", "_moduleNodes")]
|
||||||
script = ExtResource("7_br0mr")
|
script = ExtResource("7_br0mr")
|
||||||
StorageModule = NodePath("../../Storage")
|
StorageModule = NodePath("../../Storage")
|
||||||
PlayerDetection = NodePath("../../PlayerDetection")
|
PlayerDetection = NodePath("../../PlayerDetection")
|
||||||
DamageReceiver = NodePath("../../DamageReceiver")
|
DamageReceiver = NodePath("../../DamageReceiver")
|
||||||
|
NavigationModule = NodePath("../../NavigationModule")
|
||||||
EquippedWeapon = NodePath("../../EnemyWeapon")
|
EquippedWeapon = NodePath("../../EnemyWeapon")
|
||||||
_moduleNodes = [NodePath("../../AnimationModule")]
|
_moduleNodes = [NodePath("../../AnimationModule")]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,11 +108,12 @@ DamageReceiver = NodePath("../../DamageReceiver")
|
||||||
NavigationModule = NodePath("../../NavigationModule")
|
NavigationModule = NodePath("../../NavigationModule")
|
||||||
_moduleNodes = [NodePath("../../AnimationModule")]
|
_moduleNodes = [NodePath("../../AnimationModule")]
|
||||||
|
|
||||||
[node name="Shooting" type="Node2D" parent="StateMachine" node_paths=PackedStringArray("StorageModule", "PlayerDetection", "DamageReceiver", "EquippedWeapon", "_moduleNodes")]
|
[node name="Shooting" type="Node2D" parent="StateMachine" node_paths=PackedStringArray("StorageModule", "PlayerDetection", "DamageReceiver", "NavigationModule", "EquippedWeapon", "_moduleNodes")]
|
||||||
script = ExtResource("7_w6ssf")
|
script = ExtResource("7_w6ssf")
|
||||||
StorageModule = NodePath("../../Storage")
|
StorageModule = NodePath("../../Storage")
|
||||||
PlayerDetection = NodePath("../../PlayerDetection")
|
PlayerDetection = NodePath("../../PlayerDetection")
|
||||||
DamageReceiver = NodePath("../../DamageReceiver")
|
DamageReceiver = NodePath("../../DamageReceiver")
|
||||||
|
NavigationModule = NodePath("../../NavigationModule")
|
||||||
EquippedWeapon = NodePath("../../EnemyWeapon")
|
EquippedWeapon = NodePath("../../EnemyWeapon")
|
||||||
_moduleNodes = [NodePath("../../AnimationModule")]
|
_moduleNodes = [NodePath("../../AnimationModule")]
|
||||||
|
|
||||||
|
|
@ -173,6 +174,7 @@ target_desired_distance = 8.0
|
||||||
path_max_distance = 800.0
|
path_max_distance = 800.0
|
||||||
path_postprocessing = 1
|
path_postprocessing = 1
|
||||||
avoidance_enabled = true
|
avoidance_enabled = true
|
||||||
|
debug_enabled = true
|
||||||
debug_path_custom_color = Color(1, 0, 0, 1)
|
debug_path_custom_color = Color(1, 0, 0, 1)
|
||||||
|
|
||||||
[node name="EnemyWeapon" parent="." instance=ExtResource("17_i8o8t")]
|
[node name="EnemyWeapon" parent="." instance=ExtResource("17_i8o8t")]
|
||||||
|
|
|
||||||
|
|
@ -108,11 +108,12 @@ DamageReceiver = NodePath("../../DamageReceiver")
|
||||||
NavigationModule = NodePath("../../NavigationModule")
|
NavigationModule = NodePath("../../NavigationModule")
|
||||||
_moduleNodes = [NodePath("../../AnimationModule")]
|
_moduleNodes = [NodePath("../../AnimationModule")]
|
||||||
|
|
||||||
[node name="Shooting" type="Node2D" parent="StateMachine" node_paths=PackedStringArray("StorageModule", "PlayerDetection", "DamageReceiver", "EquippedWeapon", "_moduleNodes")]
|
[node name="Shooting" type="Node2D" parent="StateMachine" node_paths=PackedStringArray("StorageModule", "PlayerDetection", "DamageReceiver", "NavigationModule", "EquippedWeapon", "_moduleNodes")]
|
||||||
script = ExtResource("7_0b4ec")
|
script = ExtResource("7_0b4ec")
|
||||||
StorageModule = NodePath("../../Storage")
|
StorageModule = NodePath("../../Storage")
|
||||||
PlayerDetection = NodePath("../../PlayerDetection")
|
PlayerDetection = NodePath("../../PlayerDetection")
|
||||||
DamageReceiver = NodePath("../../DamageReceiver")
|
DamageReceiver = NodePath("../../DamageReceiver")
|
||||||
|
NavigationModule = NodePath("../../NavigationModule")
|
||||||
EquippedWeapon = NodePath("../../EnemyWeapon")
|
EquippedWeapon = NodePath("../../EnemyWeapon")
|
||||||
_moduleNodes = [NodePath("../../AnimationModule")]
|
_moduleNodes = [NodePath("../../AnimationModule")]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,4 +66,15 @@ public partial class NavigationMovementModule : Node2D
|
||||||
StorageModule.FacingDirection = _characterBody.Velocity.Normalized();
|
StorageModule.FacingDirection = _characterBody.Velocity.Normalized();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsNavigable(Vector2 newPos)
|
||||||
|
{
|
||||||
|
_navigationAgent.SetTargetPosition(newPos);
|
||||||
|
return _navigationAgent.IsTargetReachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsNavigationFinished()
|
||||||
|
{
|
||||||
|
return _navigationAgent.IsNavigationFinished();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -60,18 +60,20 @@ public partial class PlayerDetectionModule : Area2D
|
||||||
//if (_cachedPlayer == null) return false;
|
//if (_cachedPlayer == null) return false;
|
||||||
if (!GameManager.Instance.PlayerPosition.HasValue) return false;
|
if (!GameManager.Instance.PlayerPosition.HasValue) return false;
|
||||||
|
|
||||||
var spaceState = GetWorld2D().DirectSpaceState;
|
var found = HasLineOfSight(this.GlobalPosition, GameManager.Instance.PlayerPosition.Value);
|
||||||
|
|
||||||
// It needs to use its own collision mask because it's detecting obstacles rather than the player
|
// var spaceState = GetWorld2D().DirectSpaceState;
|
||||||
var query = PhysicsRayQueryParameters2D.Create(this.GlobalPosition, GameManager.Instance.PlayerPosition.Value, ObstaclesCollisionMask,
|
//
|
||||||
[GetRid()]);
|
// // It needs to use its own collision mask because it's detecting obstacles rather than the player
|
||||||
//query.CollideWithBodies = true;
|
// var query = PhysicsRayQueryParameters2D.Create(this.GlobalPosition, GameManager.Instance.PlayerPosition.Value, ObstaclesCollisionMask,
|
||||||
//query.CollideWithAreas = true;
|
// [GetRid()]);
|
||||||
// var query = PhysicsRayQueryParameters2D.Create(this.GlobalPosition, _cachedPlayer.GlobalPosition, CollisionMask, new Array<Rid> { GetRid() });
|
// //query.CollideWithBodies = true;
|
||||||
var result = spaceState.IntersectRay(query);
|
// //query.CollideWithAreas = true;
|
||||||
|
// // var query = PhysicsRayQueryParameters2D.Create(this.GlobalPosition, _cachedPlayer.GlobalPosition, CollisionMask, new Array<Rid> { GetRid() });
|
||||||
// If count is 0 then the player is in sight, otherwise there is level geometry in the way
|
// var result = spaceState.IntersectRay(query);
|
||||||
var found = result.Count == 0;
|
//
|
||||||
|
// // If count is 0 then the player is in sight, otherwise there is level geometry in the way
|
||||||
|
// var found = result.Count == 0;
|
||||||
if (found)
|
if (found)
|
||||||
{
|
{
|
||||||
LastKnownPlayerPosition = GameManager.Instance.PlayerPosition;
|
LastKnownPlayerPosition = GameManager.Instance.PlayerPosition;
|
||||||
|
|
@ -92,4 +94,22 @@ public partial class PlayerDetectionModule : Area2D
|
||||||
EmitSignal(SignalName.PlayerOutOfRange);
|
EmitSignal(SignalName.PlayerOutOfRange);
|
||||||
//PlayerInActiveArea = false;
|
//PlayerInActiveArea = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HasLineOfSight(Vector2 startPos, Vector2 endPos)
|
||||||
|
{
|
||||||
|
var spaceState = GetWorld2D().DirectSpaceState;
|
||||||
|
|
||||||
|
// It needs to use its own collision mask because it's detecting obstacles rather than the player
|
||||||
|
var query = PhysicsRayQueryParameters2D.Create(startPos, endPos, ObstaclesCollisionMask,
|
||||||
|
[GetRid()]);
|
||||||
|
//query.CollideWithBodies = true;
|
||||||
|
//query.CollideWithAreas = true;
|
||||||
|
// var query = PhysicsRayQueryParameters2D.Create(this.GlobalPosition, _cachedPlayer.GlobalPosition, CollisionMask, new Array<Rid> { GetRid() });
|
||||||
|
var result = spaceState.IntersectRay(query);
|
||||||
|
|
||||||
|
// If count is 0 then the player is in sight, otherwise there is level geometry in the way
|
||||||
|
var found = result.Count == 0;
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -17,10 +17,18 @@ public partial class Shooting : EnemyStateBase
|
||||||
[Export]
|
[Export]
|
||||||
public GenericDamageReceiver DamageReceiver { get; private set; }
|
public GenericDamageReceiver DamageReceiver { get; private set; }
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public NavigationMovementModule NavigationModule { get; private set; }
|
||||||
|
|
||||||
|
[Export] public float MaxStrafeDistance { get; private set; } = 64f;
|
||||||
|
[Export] public float MinStrafeDistance { get; private set; } = 16f;
|
||||||
|
|
||||||
[Export] public Weapon EquippedWeapon;
|
[Export] public Weapon EquippedWeapon;
|
||||||
|
|
||||||
private bool _isPlayerInRange = false;
|
private bool _isPlayerInRange = false;
|
||||||
|
|
||||||
|
private Vector2? _currentStrafeTarget = null;
|
||||||
|
|
||||||
public override void EnterState()
|
public override void EnterState()
|
||||||
{
|
{
|
||||||
base.EnterState();
|
base.EnterState();
|
||||||
|
|
@ -35,6 +43,8 @@ public partial class Shooting : EnemyStateBase
|
||||||
DamageReceiver.HealthProvider.ResourceDepleted += HealthProviderOnResourceDepleted;
|
DamageReceiver.HealthProvider.ResourceDepleted += HealthProviderOnResourceDepleted;
|
||||||
|
|
||||||
EquippedWeapon.WeaponData = StorageModule.Root.EnemyResource.Weapon;
|
EquippedWeapon.WeaponData = StorageModule.Root.EnemyResource.Weapon;
|
||||||
|
|
||||||
|
_currentStrafeTarget = null;
|
||||||
// PlayerDetection.SetRange(StorageModule.Root.EnemyResource.PlayerDetectionRange);
|
// PlayerDetection.SetRange(StorageModule.Root.EnemyResource.PlayerDetectionRange);
|
||||||
//
|
//
|
||||||
// _isPlayerInRange = PlayerDetection.IsPlayerInRange(StorageModule.Root.EnemyResource.PlayerDetectionRange);
|
// _isPlayerInRange = PlayerDetection.IsPlayerInRange(StorageModule.Root.EnemyResource.PlayerDetectionRange);
|
||||||
|
|
@ -67,6 +77,9 @@ public partial class Shooting : EnemyStateBase
|
||||||
PlayerDetection.PlayerOutOfRange -= PlayerDetectionOnPlayerOutOfRange;
|
PlayerDetection.PlayerOutOfRange -= PlayerDetectionOnPlayerOutOfRange;
|
||||||
|
|
||||||
DamageReceiver.HealthProvider.ResourceDepleted -= HealthProviderOnResourceDepleted;
|
DamageReceiver.HealthProvider.ResourceDepleted -= HealthProviderOnResourceDepleted;
|
||||||
|
|
||||||
|
_currentStrafeTarget = null;
|
||||||
|
|
||||||
DamageReceiver.ChangeState(false);
|
DamageReceiver.ChangeState(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -79,14 +92,62 @@ public partial class Shooting : EnemyStateBase
|
||||||
{
|
{
|
||||||
// SHOOT
|
// SHOOT
|
||||||
Shoot();
|
Shoot();
|
||||||
|
|
||||||
|
// Check if a strafe position is needed
|
||||||
|
if (!_currentStrafeTarget.HasValue || NavigationModule.IsNavigationFinished())
|
||||||
|
{
|
||||||
|
_currentStrafeTarget = CalculateStrafePosition();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
StateMachine.SetState(EnemyState.Alert);
|
StateMachine.SetState(EnemyState.Alert);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strafe
|
if (_currentStrafeTarget.HasValue)
|
||||||
|
{
|
||||||
|
NavigationModule.SetTarget(_currentStrafeTarget.Value);
|
||||||
|
NavigationModule.Move();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2? CalculateStrafePosition()
|
||||||
|
{
|
||||||
|
Vector2 playerPos = PlayerDetection.LastKnownPlayerPosition.Value;
|
||||||
|
Vector2 enemyPos = MainObject.GlobalPosition;
|
||||||
|
|
||||||
|
// Calculate direction to player
|
||||||
|
Vector2 directionToPlayer = (playerPos - enemyPos).Normalized();
|
||||||
|
|
||||||
|
// Get perpendicular vectors (left and right strafing directions)
|
||||||
|
Vector2 leftStrafe = directionToPlayer.Rotated(-Mathf.Pi / 2);
|
||||||
|
Vector2 rightStrafe = directionToPlayer.Rotated(Mathf.Pi / 2);
|
||||||
|
|
||||||
|
// Randomly decide left or right first
|
||||||
|
bool tryLeftFirst = GD.Randf() < 0.5f;
|
||||||
|
|
||||||
|
for (float factor = 1f; factor > 0.25f; factor *= 0.75f)
|
||||||
|
{
|
||||||
|
float strafeDistance = Mathf.Lerp(MinStrafeDistance, MaxStrafeDistance, factor);
|
||||||
|
Vector2 newPos = enemyPos + (tryLeftFirst ? leftStrafe : rightStrafe) * strafeDistance;
|
||||||
|
if (NavigationModule.IsNavigable(newPos) && PlayerDetection.HasLineOfSight(newPos, playerPos))
|
||||||
|
{
|
||||||
|
return newPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (float factor = 1f; factor > 0.25f; factor *= 0.75f)
|
||||||
|
{
|
||||||
|
float strafeDistance = Mathf.Lerp(MinStrafeDistance, MaxStrafeDistance, factor);
|
||||||
|
Vector2 newPos = enemyPos + (tryLeftFirst ? rightStrafe : leftStrafe) * strafeDistance;
|
||||||
|
if (NavigationModule.IsNavigable(newPos) && PlayerDetection.HasLineOfSight(newPos, playerPos))
|
||||||
|
{
|
||||||
|
return newPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null; // No valid strafe position found
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Shoot()
|
private void Shoot()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue