- feat: implement ai ship weapon firing (no collision yet) - fix: better ai movement implementation
This commit is contained in:
parent
70862d3903
commit
3799687ae5
|
|
@ -39,15 +39,14 @@ position = Vector2(387, 230)
|
|||
[node name="Friendly" type="Node" parent="Ships"]
|
||||
|
||||
[node name="Player" parent="Ships/Friendly" instance=ExtResource("1_1w06w")]
|
||||
position = Vector2(800, 450)
|
||||
position = Vector2(959, 539)
|
||||
scale = Vector2(0.6, 0.6)
|
||||
collision_layer = 8
|
||||
type = 1
|
||||
type = 2
|
||||
|
||||
[node name="AI_Fighter" parent="Ships/Friendly" instance=ExtResource("5_nkk10")]
|
||||
position = Vector2(989, 330)
|
||||
position = Vector2(1151, 378)
|
||||
scale = Vector2(0.6, 0.6)
|
||||
type = 0
|
||||
faction = 1
|
||||
|
||||
[node name="Enemy" type="Node" parent="Ships"]
|
||||
|
|
@ -55,6 +54,7 @@ faction = 1
|
|||
[node name="AI_Fighter" parent="Ships/Enemy" instance=ExtResource("5_nkk10")]
|
||||
position = Vector2(992, 205)
|
||||
scale = Vector2(0.6, 0.6)
|
||||
type = 0
|
||||
|
||||
[connection signal="Exploded" from="Asteroids/Asteroid" to="." method="OnAsteroidExploded"]
|
||||
[connection signal="Exploded" from="Asteroids/Asteroid2" to="." method="OnAsteroidExploded"]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[gd_scene load_steps=4 format=3 uid="uid://dx4wnk5okjs6x"]
|
||||
|
||||
[ext_resource type="Texture2D" path="res://assets/Player/Fighter/ShipBlue.png" id="1_g3tsu"]
|
||||
[ext_resource type="Script" path="res://script/ship.cs" id="1_u8ww1"]
|
||||
[ext_resource type="Texture2D" uid="uid://ofevjaw7ld0a" path="res://assets/Ships/Fighters/Player/Fighter/ShipBlue.png" id="2_fvvdf"]
|
||||
|
||||
[sub_resource type="CircleShape2D" id="CircleShape2D_tndfm"]
|
||||
radius = 41.0488
|
||||
|
|
@ -14,7 +14,7 @@ script = ExtResource("1_u8ww1")
|
|||
shape = SubResource("CircleShape2D_tndfm")
|
||||
|
||||
[node name="ShipSprite" type="Sprite2D" parent="."]
|
||||
texture = ExtResource("1_g3tsu")
|
||||
texture = ExtResource("2_fvvdf")
|
||||
|
||||
[node name="LaserSpawn" type="Node2D" parent="."]
|
||||
position = Vector2(0, -58)
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ public partial class ai_fighter : ship
|
|||
[Export]
|
||||
public float AggroRange = 500f;
|
||||
[Export]
|
||||
public float FireCooldown = 2f;
|
||||
[Export]
|
||||
public int ScorePayout = 50;
|
||||
[Export]
|
||||
public float EngageDistance = 300f;
|
||||
|
|
@ -16,12 +14,10 @@ public partial class ai_fighter : ship
|
|||
//retreat logic
|
||||
private float previousDistance = 0f;
|
||||
private float stuckTime = 0f;
|
||||
private const float stuckThreshold = 0.5f; // how many seconds to count as “stuck”
|
||||
private const float distanceTolerance = 5f; // how little the distance must change
|
||||
|
||||
private const float stuckThreshold = 0.5f;
|
||||
private const float distanceTolerance = 5f;
|
||||
|
||||
private Node2D currentTarget;
|
||||
private float fireTimer = 0f;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
|
|
@ -35,25 +31,48 @@ public partial class ai_fighter : ship
|
|||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
UpdateMovement(delta);
|
||||
GD.Print(Name, ": MainSpeed = ", MainSpeed, " Velocity = ", Velocity.Length());
|
||||
HandleFiring(delta);
|
||||
//GD.Print(Name, ": MainSpeed = ", MainSpeed, " Velocity = ", Velocity.Length());
|
||||
}
|
||||
|
||||
// SETUP
|
||||
|
||||
private void SetupAI()
|
||||
{
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// COMBAT
|
||||
private void FireWeapons()
|
||||
private void HandleFiring(double delta)
|
||||
{
|
||||
//GD.Print(Name, ": checking fire");
|
||||
//GD.Print(Name, ": fireTimer = ", fireTimer);
|
||||
|
||||
|
||||
|
||||
fireTimer -= (float)delta; // countdown to next shot available
|
||||
if (fireTimer > 0f) return; // if countdown not finished then no shoot
|
||||
|
||||
if (currentTarget == null || !IsInstanceValid(currentTarget)) return; // checks for valid target, returns if not
|
||||
|
||||
// determining direction and distance to target
|
||||
Vector2 toTarget = (currentTarget.GlobalPosition - GlobalPosition).Normalized();
|
||||
float angleToTarget = toTarget.Angle() + Mathf.Pi / 2;
|
||||
float angleDiff = Mathf.Abs(Mathf.AngleDifference(Rotation, angleToTarget));
|
||||
float distance = GlobalPosition.DistanceTo(currentTarget.GlobalPosition);
|
||||
|
||||
//GD.Print(Name, ": angleDiff = ", Mathf.RadToDeg(angleDiff));
|
||||
|
||||
if (angleDiff < 0.4f && distance <= EngageDistance + 200f) // fire when in range and mostly aligned
|
||||
{
|
||||
//GD.Print(Name, ": angleDiff = ", angleDiff, ", distance = ", distance);
|
||||
|
||||
ShootLaser();
|
||||
fireTimer = FireCooldown; // resets cooldown
|
||||
}
|
||||
}
|
||||
|
||||
// TARGETING
|
||||
private Node2D FindClosestTarget()
|
||||
private Node2D FindClosestTarget() // returns nearest valid ship depending on faction
|
||||
{
|
||||
Node2D closest = null;
|
||||
float closestDistance = Mathf.Inf;
|
||||
|
|
@ -85,11 +104,10 @@ public partial class ai_fighter : ship
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
private bool EnsureTarget()
|
||||
private bool EnsureTarget() // finds a new target if no current or if invalid (dead)
|
||||
{
|
||||
if (currentTarget == null || !IsInstanceValid(currentTarget))
|
||||
{
|
||||
|
|
@ -99,7 +117,7 @@ public partial class ai_fighter : ship
|
|||
return true;
|
||||
}
|
||||
|
||||
private void UpdateDistanceTracking(float distance)
|
||||
private void UpdateDistanceTracking(float distance) // tracks how long AI has been stuck in same spot
|
||||
{
|
||||
if (MathF.Abs(distance - previousDistance) < distanceTolerance)
|
||||
{
|
||||
|
|
@ -112,53 +130,58 @@ public partial class ai_fighter : ship
|
|||
previousDistance = distance;
|
||||
}
|
||||
|
||||
private void HandleThrust(Vector2 direction, float angleDiff, float distance)
|
||||
private void HandleThrust(float angleDiff, float distance)
|
||||
{
|
||||
float retreatThreshold = EngageDistance * 0.75f;
|
||||
|
||||
if (Mathf.Abs(angleDiff) < 1f && Velocity.Length() < MaxSpeed - MainSpeed)
|
||||
GD.Print(Name, " | angleDiff: ", Mathf.RadToDeg(angleDiff), " | velocity: ", Velocity.Length());
|
||||
|
||||
float retreatThreshold = EngageDistance * 0.75f; // retreat distance
|
||||
|
||||
if (Mathf.Abs(angleDiff) < 1f) // only move forward when facing the target roughly
|
||||
{
|
||||
if (distance > EngageDistance)
|
||||
{
|
||||
Velocity += -Transform.Y * MainSpeed;
|
||||
Velocity += -Transform.Y * MainSpeed; // approach target
|
||||
}
|
||||
else if (distance < retreatThreshold)
|
||||
{
|
||||
if (stuckTime > stuckThreshold)
|
||||
{
|
||||
// reposition if stuck
|
||||
Vector2 away = (GlobalPosition - currentTarget.GlobalPosition).Normalized();
|
||||
Velocity += away * MainSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
Velocity += Transform.X * StrafeSpeed;
|
||||
Velocity += Transform.X * StrafeSpeed; // try and orbit target laterally (strafe)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Velocity += Transform.X * StrafeSpeed * 0.5f; // lateral drift in hold range
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RotateToTarget(Vector2 direction, double delta)
|
||||
private void RotateToTarget(Vector2 direction, double delta) // rotates ship toward angle using rotation speed
|
||||
{
|
||||
float targetAngle = direction.Angle() + Mathf.Pi / 2;
|
||||
float angleDiff = Mathf.AngleDifference(Rotation, targetAngle);
|
||||
Rotation += Mathf.Clamp(angleDiff, -RotationSpeed * (float)delta, RotationSpeed * (float)delta);
|
||||
}
|
||||
|
||||
|
||||
private void UpdateMovement(double delta)
|
||||
{
|
||||
if (!EnsureTarget())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!EnsureTarget()) return; // skip if no valid target
|
||||
|
||||
// math for direction, distance and rotation
|
||||
Vector2 direction = (currentTarget.GlobalPosition - GlobalPosition).Normalized();
|
||||
float angleToTarget = direction.Angle() + MathF.PI / 2;
|
||||
float angleDiff = Mathf.AngleDifference(Rotation, angleToTarget);
|
||||
float distance = GlobalPosition.DistanceTo(currentTarget.GlobalPosition);
|
||||
|
||||
UpdateDistanceTracking(distance);
|
||||
HandleThrust(direction, angleDiff, distance);
|
||||
HandleThrust(angleDiff, distance);
|
||||
RotateToTarget(direction, delta);
|
||||
|
||||
Velocity = Velocity.MoveToward(Vector2.Zero, 2.5f); //2.5f FA value (on for AI)
|
||||
|
|
@ -166,76 +189,4 @@ public partial class ai_fighter : ship
|
|||
|
||||
MoveAndSlide();
|
||||
}
|
||||
|
||||
/*private void UpdateMovement(double delta)
|
||||
{
|
||||
if (currentTarget == null || !IsInstanceValid(currentTarget))
|
||||
{
|
||||
currentTarget = FindClosestTarget();
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 direction = (currentTarget.GlobalPosition - GlobalPosition).Normalized();
|
||||
float distance = GlobalPosition.DistanceTo(currentTarget.GlobalPosition);
|
||||
float retreatThreshold = EngageDistance * 0.75f;
|
||||
//Velocity += direction * MainSpeed;
|
||||
float angleToTarget = direction.Angle() + Mathf.Pi / 2;
|
||||
float angleDiff = Mathf.AngleDifference(Rotation, angleToTarget);
|
||||
|
||||
|
||||
|
||||
|
||||
float currentDistance = GlobalPosition.DistanceTo(currentTarget.GlobalPosition);
|
||||
|
||||
if (Mathf.Abs(currentDistance - previousDistance) < distanceTolerance)
|
||||
stuckTime += (float)delta;
|
||||
else
|
||||
stuckTime = 0f;
|
||||
|
||||
previousDistance = currentDistance;
|
||||
|
||||
|
||||
//GD.Print("angleDiff: ", Mathf.RadToDeg(angleDiff));
|
||||
//GD.Print("rotation: ", Mathf.RadToDeg(Rotation), " target: ", Mathf.RadToDeg(angleToTarget));
|
||||
|
||||
|
||||
|
||||
if (Mathf.Abs(angleDiff) < 1f && Velocity.Length() < MaxSpeed - MainSpeed)
|
||||
{
|
||||
if (distance > EngageDistance)
|
||||
{
|
||||
Velocity += -Transform.Y * MainSpeed;
|
||||
}
|
||||
else if (distance < retreatThreshold)
|
||||
{
|
||||
if (stuckTime > stuckThreshold){
|
||||
Velocity += Transform.Y * MainSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
Velocity += Transform.X * StrafeSpeed;
|
||||
}
|
||||
|
||||
//Velocity += Transform.Y * MainSpeed;
|
||||
//Velocity += Transform.X * StrafeSpeed;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Velocity = Velocity.MoveToward(Vector2.Zero, 2.5f);
|
||||
|
||||
|
||||
//GD.Print(MainSpeed);
|
||||
//GD.Print(Velocity);
|
||||
//Rotation = direction.Angle() + Mathf.Pi / 2; // Keeps ship pointing right way
|
||||
|
||||
angleDiff = Mathf.AngleDifference(Rotation, direction.Angle() + Mathf.Pi / 2);
|
||||
Rotation += Mathf.Clamp(angleDiff, -RotationSpeed * (float)delta, RotationSpeed * (float)delta);
|
||||
|
||||
Velocity.LimitLength(MaxSpeed);
|
||||
|
||||
MoveAndSlide();
|
||||
}*/
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,31 @@ public partial class game : Node2D
|
|||
Enemies = GetNode<Node>("Ships/Enemy");
|
||||
Friendlies = GetNode<Node>("Ships/Friendly");
|
||||
Player = GetNode<CharacterBody2D>("Ships/Friendly/Player");
|
||||
|
||||
|
||||
//AI fighter signals
|
||||
foreach (Node node in Enemies.GetChildren())
|
||||
{
|
||||
if (node is ai_fighter ai)
|
||||
{
|
||||
GD.Print("Connected laser signal for: ", ai.Name);
|
||||
|
||||
ai.LaserShot += OnAILaserShot;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Node node in Friendlies.GetChildren())
|
||||
{
|
||||
if (node is player) continue;
|
||||
|
||||
if (node is ai_fighter ai)
|
||||
{
|
||||
GD.Print("Connected laser signal for: ", ai.Name);
|
||||
|
||||
ai.LaserShot += OnAILaserShot;
|
||||
}
|
||||
}
|
||||
|
||||
//var p = new player();
|
||||
//p.LaserShot += OnPlayerLaserShot;
|
||||
}
|
||||
|
|
@ -105,6 +130,11 @@ public partial class game : Node2D
|
|||
}
|
||||
|
||||
//Signals and Connections
|
||||
public void OnAILaserShot(Area2D Laser)
|
||||
{
|
||||
Lasers.AddChild(Laser);
|
||||
}
|
||||
|
||||
public void OnPlayerLaserShot(Area2D Laser)
|
||||
{
|
||||
Lasers.AddChild(Laser);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ public partial class player : ship // Inherits from base ship class
|
|||
[Export]
|
||||
public float FlightAssistValue { get; set; } = 2.5f;
|
||||
|
||||
|
||||
public void GetInput()
|
||||
{
|
||||
/*LookAt(GetGlobalMousePosition()); //used for mouse-based rotation and movement
|
||||
|
|
@ -87,9 +88,13 @@ public partial class player : ship // Inherits from base ship class
|
|||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
fireTimer -= (float)delta;
|
||||
|
||||
if(Input.IsActionJustPressed("shoot"))
|
||||
{
|
||||
if (fireTimer > 0f) return;
|
||||
ShootLaser();
|
||||
fireTimer = FireCooldown;
|
||||
}
|
||||
|
||||
if (Health <= 0)
|
||||
|
|
@ -106,7 +111,7 @@ public partial class player : ship // Inherits from base ship class
|
|||
|
||||
//GD.Print(MainSpeed);
|
||||
//GD.Print("v ",Velocity, "v");
|
||||
GD.Print(Name, ": MainSpeed = ", MainSpeed, " Velocity = ", Velocity.Length());
|
||||
//GD.Print(Name, ": MainSpeed = ", MainSpeed, " Velocity = ", Velocity.Length());
|
||||
|
||||
MoveAndSlide();
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ public partial class ship : CharacterBody2D
|
|||
[Export]
|
||||
public float RotationSpeed { get; set; }
|
||||
[Export]
|
||||
public int Damage {get; set;}
|
||||
public int Damage {get; set; }
|
||||
[Export]
|
||||
public float FireCooldown { get; set; }
|
||||
[Export]
|
||||
public ShipType type;
|
||||
[Export]
|
||||
|
|
@ -34,6 +36,8 @@ public partial class ship : CharacterBody2D
|
|||
public Sprite2D Sprite = new Sprite2D();
|
||||
|
||||
public Node2D LaserSpawn = null;
|
||||
|
||||
protected float fireTimer = 0f;
|
||||
|
||||
protected string spritePath = "";
|
||||
|
||||
|
|
@ -43,6 +47,8 @@ public partial class ship : CharacterBody2D
|
|||
|
||||
public virtual void ShootLaser()
|
||||
{
|
||||
GD.Print(Name, ": firing laser");
|
||||
|
||||
Node2D Laser = LaserScene.Instantiate<Node2D>();
|
||||
Laser.Position = LaserSpawn.GlobalPosition;
|
||||
Laser.Rotation = Rotation;
|
||||
|
|
@ -145,6 +151,7 @@ public partial class ship : CharacterBody2D
|
|||
RotationSpeed = 2f;
|
||||
MaxHealth = 100;
|
||||
Damage = 40;
|
||||
FireCooldown = 0.6f;
|
||||
break;
|
||||
|
||||
case ShipType.INTERCEPTOR:
|
||||
|
|
@ -155,6 +162,7 @@ public partial class ship : CharacterBody2D
|
|||
RotationSpeed = 4f;
|
||||
MaxHealth = 75;
|
||||
Damage = 20;
|
||||
FireCooldown = 0.3f;
|
||||
break;
|
||||
|
||||
case ShipType.GUARDIAN:
|
||||
|
|
@ -165,6 +173,7 @@ public partial class ship : CharacterBody2D
|
|||
RotationSpeed = 1.5f;
|
||||
MaxHealth = 200;
|
||||
Damage = 60;
|
||||
FireCooldown = 1.1f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue