-feat: implement ai laser collision and health, add health display to ai ships, player takes dmg from ai ships.

This commit is contained in:
rsxri 2025-04-19 23:24:46 +01:00
parent 3799687ae5
commit 4c561f7e02
13 changed files with 230 additions and 120 deletions

View file

@ -1,16 +1,20 @@
[gd_scene load_steps=4 format=3 uid="uid://3e6fmds2x8q5"]
[gd_scene load_steps=6 format=3 uid="uid://3e6fmds2x8q5"]
[ext_resource type="Texture2D" uid="uid://soden53qtfxf" path="res://assets/Ships/Fighters/Enemy/enemyFighter.png" id="1_4l75b"]
[ext_resource type="Script" path="res://script/ai_fighter.cs" id="1_kyds1"]
[ext_resource type="FontFile" uid="uid://ryhimaxr7tr4" path="res://assets/Fonts/Kenney Mini Square.ttf" id="3_8bw2f"]
[sub_resource type="CircleShape2D" id="CircleShape2D_6vq6f"]
radius = 41.0488
[sub_resource type="LabelSettings" id="LabelSettings_lim1q"]
font = ExtResource("3_8bw2f")
font_size = 40
[node name="AI_Fighter" type="CharacterBody2D"]
collision_mask = 13
motion_mode = 1
script = ExtResource("1_kyds1")
type = 1
faction = 2
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("CircleShape2D_6vq6f")
@ -22,3 +26,24 @@ texture = ExtResource("1_4l75b")
position = Vector2(0, -58)
[node name="EffectSpawn" type="Node2D" parent="."]
[node name="HealthDisplay" type="Node2D" parent="."]
position = Vector2(-30, 90)
scale = Vector2(1.2, 1.2)
[node name="HealthLabel" type="Label" parent="HealthDisplay"]
offset_right = 65.0
offset_bottom = 50.0
text = "100"
label_settings = SubResource("LabelSettings_lim1q")
horizontal_alignment = 1
[node name="HPLabel" type="Label" parent="HealthDisplay"]
offset_left = 62.0
offset_top = -2.0
offset_right = 127.0
offset_bottom = 48.0
scale = Vector2(0.5, 0.5)
text = "HP"
label_settings = SubResource("LabelSettings_lim1q")
horizontal_alignment = 1

View file

@ -22,11 +22,9 @@ position = Vector2(1228, 195)
[node name="Asteroid2" parent="Asteroids" instance=ExtResource("3_b8wlr")]
position = Vector2(332, 661)
size = 1
[node name="Asteroid3" parent="Asteroids" instance=ExtResource("3_b8wlr")]
position = Vector2(1450, 641)
size = 2
[node name="Asteroid4" parent="Asteroids" instance=ExtResource("3_b8wlr")]
position = Vector2(769, 202)
@ -41,20 +39,20 @@ position = Vector2(387, 230)
[node name="Player" parent="Ships/Friendly" instance=ExtResource("1_1w06w")]
position = Vector2(959, 539)
scale = Vector2(0.6, 0.6)
collision_layer = 8
type = 2
[node name="AI_Fighter" parent="Ships/Friendly" instance=ExtResource("5_nkk10")]
position = Vector2(1151, 378)
position = Vector2(1229, 430)
scale = Vector2(0.6, 0.6)
faction = 1
type = 0
Faction = 1
[node name="Enemy" type="Node" parent="Ships"]
[node name="AI_Fighter" parent="Ships/Enemy" instance=ExtResource("5_nkk10")]
position = Vector2(992, 205)
position = Vector2(952, 214)
scale = Vector2(0.6, 0.6)
type = 0
Faction = 2
[connection signal="Exploded" from="Asteroids/Asteroid" to="." method="OnAsteroidExploded"]
[connection signal="Exploded" from="Asteroids/Asteroid2" to="." method="OnAsteroidExploded"]

View file

@ -8,10 +8,9 @@
radius = 41.0488
[node name="Player" type="CharacterBody2D"]
collision_mask = 5
collision_mask = 13
motion_mode = 1
script = ExtResource("1_lhmq0")
color = 1
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("CircleShape2D_btpq3")

View file

@ -7,6 +7,7 @@
radius = 41.0488
[node name="Ship" type="CharacterBody2D"]
collision_mask = 13
motion_mode = 1
script = ExtResource("1_u8ww1")

View file

@ -6,6 +6,8 @@ public partial class Laser : Area2D
[Export]
public int Speed { get; set;} = 2000;
public ship Shooter;
public Vector2 MovementVector { get; set; } = new Vector2(0, -1);
@ -13,6 +15,11 @@ public partial class Laser : Area2D
{
QueueFree();
}
public override void _Ready()
{
Connect("body_entered", new Callable(this, nameof(OnBodyEntered)));
}
public override void _PhysicsProcess(double delta)
{
GlobalPosition += MovementVector.Rotated(Rotation) * Speed * (float)delta;
@ -26,4 +33,37 @@ public partial class Laser : Area2D
QueueFree();
}
}
private bool ValidTargetFaction(ship body)
{
return Shooter.Faction switch
{
ship.ShipFaction.PLAYER => body.Faction is ship.ShipFaction.ENEMY or ship.ShipFaction.ACE,
ship.ShipFaction.ENEMY => body.Faction is ship.ShipFaction.FRIENDLY or ship.ShipFaction.PLAYER,
ship.ShipFaction.FRIENDLY => body.Faction is ship.ShipFaction.ENEMY or ship.ShipFaction.ACE,
ship.ShipFaction.ACE => body.Faction is ship.ShipFaction.FRIENDLY or ship.ShipFaction.PLAYER,
_ => throw new ArgumentOutOfRangeException() // thank you rider very cool
};
}
private void OnBodyEntered(Node body)
{
GD.Print("Laser hit something: ", body.Name);
if (body is ship target)
{
if (Shooter != null && !ValidTargetFaction(target))
{
GD.Print("friendly fire");
QueueFree();
return; // prevent friendly fire
}
else
{
GD.Print("Hit type: ", body.GetType());
target.ShipDamage(Shooter.Damage);
QueueFree();
}
}
}
}

View file

@ -11,6 +11,9 @@ public partial class ai_fighter : ship
[Export]
public float EngageDistance = 300f;
private Label HealthLabel;
private Label HPLabel;
//retreat logic
private float previousDistance = 0f;
private float stuckTime = 0f;
@ -24,10 +27,19 @@ public partial class ai_fighter : ship
SetShipStats();
SetupVisual();
Sprite.Texture = GD.Load<Texture2D>(spritePath);
HealthLabel = GetNode<Label>("HealthDisplay/HealthLabel");
UpdateHealthLabel(Health);
LaserSpawn = GetNode<Node2D>("LaserSpawn");
}
public override void _Process(double delta)
{
base._Process(delta);
HealthLabel.GetParent<Node2D>().GlobalRotation = 0f;
}
public override void _PhysicsProcess(double delta)
{
UpdateMovement(delta);
@ -41,7 +53,27 @@ public partial class ai_fighter : ship
return;
}
private void UpdateHealthLabel(int health)
{
HealthLabel.Text = health.ToString();
float percentage = (float)Health / MaxHealth;
HealthLabel.Modulate = percentage switch
{
<= 0.25f => new Color(1f, 0f, 0f),
<= 0.5f => new Color(1f, 0.5f, 0f),
<= 0.75f => new Color(1f, 1f, 0f),
_ => new Color(0.07f,0.6f, 0.07f)
}; // thank you rider
}
// COMBAT
public override void ShipDamage(int damage)
{
base.ShipDamage(damage);
UpdateHealthLabel(Health);
}
private void HandleFiring(double delta)
{
//GD.Print(Name, ": checking fire");
@ -78,11 +110,11 @@ public partial class ai_fighter : ship
float closestDistance = Mathf.Inf;
Node shipParent = null;
if (faction == ShipFaction.FRIENDLY)
if (Faction == ShipFaction.FRIENDLY)
{
shipParent = GetTree().Root.GetNode("Game/Ships/Enemy");
}
else if (faction == ShipFaction.ENEMY || faction == ShipFaction.ACE)
else if (Faction == ShipFaction.ENEMY || Faction == ShipFaction.ACE)
{
shipParent = GetTree().Root.GetNode("Game/Ships/Friendly");
}
@ -133,7 +165,7 @@ public partial class ai_fighter : ship
private void HandleThrust(float angleDiff, float distance)
{
GD.Print(Name, " | angleDiff: ", Mathf.RadToDeg(angleDiff), " | velocity: ", Velocity.Length());
//GD.Print(Name, " | angleDiff: ", Mathf.RadToDeg(angleDiff), " | velocity: ", Velocity.Length());
float retreatThreshold = EngageDistance * 0.75f; // retreat distance

View file

@ -8,61 +8,62 @@ public partial class asteroid : Area2D
public enum AsteroidSize {LARGE, MEDIUM, SMALL};
private string[] LargeAsteroidTexture = new string[] {"res://assets/PNG/Meteors/meteorBrown_big1.png",
private string[] _largeAsteroidTexture = new string[] {"res://assets/PNG/Meteors/meteorBrown_big1.png",
"res://assets/PNG/Meteors/meteorBrown_big4.png",
"res://assets/PNG/Meteors/meteorGrey_big4.png"};
private string[] MediumAsteroidTexture = new string[] {"res://assets/PNG/Meteors/meteorGrey_med1.png",
private string[] _mediumAsteroidTexture = new string[] {"res://assets/PNG/Meteors/meteorGrey_med1.png",
"res://assets/PNG/Meteors/meteorBrown_med3.png",
"res://assets/PNG/Meteors/meteorBrown_med1.png"};
private string[] SmallAsteroidTexture = new string[] {"res://assets/PNG/Meteors/meteorBrown_small1.png",
private string[] _smallAsteroidTexture = new string[] {"res://assets/PNG/Meteors/meteorBrown_small1.png",
"res://assets/PNG/Meteors/meteorBrown_small2.png",
"res://assets/PNG/Meteors/meteorGrey_small1.png"};
[Export]
public AsteroidSize size;
public int Speed = 200;
public AsteroidSize Size;
public Vector2 MovementVector;
private int _speed = 200;
public Sprite2D Sprite = new Sprite2D();
private Vector2 _movementVector;
public CollisionShape2D ColShape = null;
private Sprite2D _sprite = new Sprite2D();
private CollisionShape2D _colShape;
public override void _Ready()
{
Sprite = GetNode<Sprite2D>("Sprite2D");
ColShape = GetNode<CollisionShape2D>("CollisionShape2D");
_sprite = GetNode<Sprite2D>("Sprite2D");
_colShape = GetNode<CollisionShape2D>("CollisionShape2D");
var rand = new Random();
Rotation = (float)rand.NextDouble();
Rotation = (float)((Rotation * 2) * 3.14);
MovementVector = new Vector2(0, -1);
_movementVector = new Vector2(0, -1);
CircleShape2D ColShapeL;
switch (size)
CircleShape2D colShapeL;
switch (Size)
{
case AsteroidSize.LARGE:
Speed = rand.Next(5, 15);
Sprite.Texture = GD.Load<Texture2D>(LargeAsteroidTexture[rand.Next(3)]);
ColShapeL = GD.Load<CircleShape2D>("res://assets/CollisionShapes/asteroid_cshape_big.tres");
ColShape.SetDeferred("shape", ColShapeL);
_speed = rand.Next(5, 15);
_sprite.Texture = GD.Load<Texture2D>(_largeAsteroidTexture[rand.Next(3)]);
colShapeL = GD.Load<CircleShape2D>("res://assets/CollisionShapes/asteroid_cshape_big.tres");
_colShape.SetDeferred("shape", colShapeL);
break;
case AsteroidSize.MEDIUM:
Speed = rand.Next(25, 50);
Sprite.Texture = GD.Load<Texture2D>(MediumAsteroidTexture[rand.Next(3)]);
ColShapeL = GD.Load<CircleShape2D>("res://assets/CollisionShapes/asteroid_cshape_medcircle.tres");
ColShape.SetDeferred("shape", ColShapeL);
_speed = rand.Next(25, 50);
_sprite.Texture = GD.Load<Texture2D>(_mediumAsteroidTexture[rand.Next(3)]);
colShapeL = GD.Load<CircleShape2D>("res://assets/CollisionShapes/asteroid_cshape_medcircle.tres");
_colShape.SetDeferred("shape", colShapeL);
break;
case AsteroidSize.SMALL:
Speed = rand.Next(50, 75);
Sprite.Texture = GD.Load<Texture2D>(SmallAsteroidTexture[rand.Next(3)]);
ColShapeL = GD.Load<CircleShape2D>("res://assets/CollisionShapes/asteroid_cshape_smallcircle.tres");
ColShape.SetDeferred("shape", ColShapeL);
_speed = rand.Next(50, 75);
_sprite.Texture = GD.Load<Texture2D>(_smallAsteroidTexture[rand.Next(3)]);
colShapeL = GD.Load<CircleShape2D>("res://assets/CollisionShapes/asteroid_cshape_smallcircle.tres");
_colShape.SetDeferred("shape", colShapeL);
break;
}
}
@ -72,26 +73,31 @@ public partial class asteroid : Area2D
//DEBUG PRINT
GD.Print($"DEBUG: Explode() called for {Name} at {GlobalPosition}");
GD.Print($"DEBUG: {Name} calling Explode()");
EmitSignal(SignalName.Exploded, GlobalPosition, (int)size);
EmitSignal(SignalName.Exploded, GlobalPosition, (int)Size);
QueueFree();
}
public void OnBodyEntered(CharacterBody2D body)
private static void OnBodyEntered(CharacterBody2D body)
{
if (body is player player)
/*if (body is player player)
{
player.ShipDamage(30);
GD.Print("player asteroid collide");
}
}*/
if (body is not ship ship) return;
ship.ShipDamage((30));
GD.Print(("ship asteroid collide"));
GD.Print(ship.Name, ship.Health);
}
public override void _PhysicsProcess(double delta)
{
GlobalPosition += MovementVector.Rotated(Rotation) * Speed * (float)delta;
CircleShape2D circleshape = (CircleShape2D)ColShape.Shape;
var radius = circleshape.Radius;
var ScreenSize = GetViewportRect().Size;
GlobalPosition += _movementVector.Rotated(Rotation) * _speed * (float)delta;
CircleShape2D circleShape = (CircleShape2D)_colShape.Shape;
var radius = circleShape.Radius;
var screenSize = GetViewportRect().Size;
/*
if (GlobalPosition.Y + radius < 0)

View file

@ -7,18 +7,18 @@ public partial class camera : Camera2D
[Export] public float SmoothingSpeed = 0.05f;
// Called when the node enters the scene tree for the first time.
public CharacterBody2D Player;
private CharacterBody2D _player;
public override void _Ready()
{
Player = GetParent<CharacterBody2D>();
_player = GetParent<CharacterBody2D>();
}
public override void _Process(double delta)
{
Vector2 playerPos = Player.GlobalPosition;
Vector2 playerV = Player.Velocity;
Vector2 playerPos = _player.GlobalPosition;
Vector2 playerV = _player.Velocity;
Vector2 direction = playerV.Normalized();
Vector2 cameraTargetPos = playerPos + OffsetAmount * direction;
GlobalPosition = GlobalPosition.Lerp(cameraTargetPos, SmoothingSpeed);

View file

@ -3,28 +3,28 @@ using System;
public partial class game : Node2D
{
public Node Lasers = null;
public CharacterBody2D Player = null;
public Node Asteroids = null;
public Node Lasers;
public CharacterBody2D Player;
public Node Asteroids;
public Node Friendlies = null;
public Node Friendlies;
public Node Enemies = null;
public Node Enemies;
public Node Ships = null;
public Node Ships;
public Control HUD = null;
public Control HUD;
public Label ScoreLabel = null;
public Label ScoreLabel;
public Label HealthLabel = null;
public Label HealthLabel;
public Label FlightAssistLabel = null;
public Label FlightAssistLabel;
public hud h;
public hud H;
private readonly PackedScene AsteroidScene = GD.Load<PackedScene>("res://scenes/asteroid.tscn");
private readonly PackedScene _asteroidScene = GD.Load<PackedScene>("res://scenes/asteroid.tscn");
public int Score = 0;
@ -82,7 +82,7 @@ public partial class game : Node2D
UpdateFALabel();
}
/* //AUTO SPAWN AFTER CLEAR
//AUTO SPAWN AFTER CLEAR
if (Asteroids.GetChildCount() == 0)
{
Random rand = new Random();
@ -90,15 +90,15 @@ public partial class game : Node2D
{
SpawnAsteroid(new Vector2(rand.Next(10, 800), rand.Next(10, 800)), (int)asteroid.AsteroidSize.LARGE);
}
}*/
}
}
public void SpawnAsteroid(Vector2 position, int size)
{
var a = new asteroid();
a = AsteroidScene.Instantiate<asteroid>();
a = _asteroidScene.Instantiate<asteroid>();
a.GlobalPosition = position;
a.size = (asteroid.AsteroidSize)size;
a.Size = (asteroid.AsteroidSize)size;
a.Exploded += OnAsteroidExploded;
Asteroids.CallDeferred("add_child", a);
}
@ -135,10 +135,10 @@ public partial class game : Node2D
Lasers.AddChild(Laser);
}
public void OnPlayerLaserShot(Area2D Laser)
public void OnPlayerLaserShot(Area2D laser)
{
Lasers.AddChild(Laser);
GD.Print(Laser.Position);
Lasers.AddChild(laser);
GD.Print(laser.Position);
GD.Print(Player.Position);
}
@ -149,7 +149,13 @@ public partial class game : Node2D
public void OnPlayerDeath()
{
GetTree().ReloadCurrentScene(); //Reload scene to act as restart
CallDeferred(nameof(SafeReloadScene)); //Reload scene to act as restart
}
private void SafeReloadScene()
{
GD.Print("Reloading scene");
GetTree().ReloadCurrentScene();
}
public void OnAsteroidExploded(Vector2 pos, int size)

View file

@ -3,16 +3,16 @@ using System;
public partial class hud : Control
{
public Label Score = new();
public Label FlightAssist = new();
private Label _score = new();
private Label _flightAssist = new();
public override void _Ready()
{
Score = GetNode<Label>("Score");
Score.Text = "SCORE: 0";
_score = GetNode<Label>("Score");
_score.Text = "SCORE: 0";
// Keeping health being initialised in game.cs
FlightAssist = GetNode<Label>("FlightAssist");
FlightAssist.Text = "FA: ON"; // Should be on by default
_flightAssist = GetNode<Label>("FlightAssist");
_flightAssist.Text = "FA: ON"; // Should be on by default
}
}

View file

@ -21,7 +21,7 @@ public partial class main_menu : Control
GetTree().Quit();
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
// Called every frame. 'delta' has been the elapsed time since the previous frame.
public override void _Process(double delta)
{
}

View file

@ -12,20 +12,20 @@ public partial class player : ship // Inherits from base ship class
public delegate void PlayerDeathEventHandler();
[Export]
public ShipColor color;
public ShipColor Color;
[Export]
public float FlightAssistValue { get; set; } = 2.5f;
public void GetInput()
private void GetInput()
{
/*LookAt(GetGlobalMousePosition()); //used for mouse-based rotation and movement
Velocity = Transform.X * Input.GetAxis("down", "up") * Speed;
*/
// Movement, could probably move into its own methods instead of GetInput()
_rotationDirection = (int)Input.GetAxis("left", "right");
RotationDirection = (int)Input.GetAxis("left", "right");
Velocity += (Transform.X * Input.GetAxis("strafe_left", "strafe_right") * StrafeSpeed) + (Transform.Y * Input.GetAxis("up", "down") * MainSpeed);
Velocity = Velocity.LimitLength(MaxSpeed);
@ -48,9 +48,14 @@ public partial class player : ship // Inherits from base ship class
{
base.ShipDamage(damage);
EmitSignal(SignalName.HealthUpdate, Health);
if (Health <= 0)
{
EmitSignal(SignalName.PlayerDeath);
}
}
public void ToggleFlightAssist()
private void ToggleFlightAssist()
{
if (FlightAssistValue == 0f){FlightAssistValue = 2.5f;}
else {FlightAssistValue = 0;}
@ -59,22 +64,15 @@ public partial class player : ship // Inherits from base ship class
public override void _Ready()
{
SetupVisual();
GD.Print(faction);
GD.Print(Faction);
switch(color)
this.spritePath = Color switch
{
case ShipColor.RED:
this.spritePath = this.spritePath + "ShipRed.png";
break;
case ShipColor.GREEN:
this.spritePath = this.spritePath + "ShipGreen.png";
break;
case ShipColor.BLUE:
this.spritePath = this.spritePath + "ShipBlue.png";
break;
}
ShipColor.RED => this.spritePath + "ShipRed.png",
ShipColor.GREEN => this.spritePath + "ShipGreen.png",
ShipColor.BLUE => this.spritePath + "ShipBlue.png",
_ => this.spritePath
};
GD.Print(spritePath);
Sprite.Texture = GD.Load<Texture2D>(spritePath);
@ -90,23 +88,18 @@ public partial class player : ship // Inherits from base ship class
{
fireTimer -= (float)delta;
if(Input.IsActionJustPressed("shoot"))
if(Input.IsActionPressed("shoot"))
{
if (fireTimer > 0f) return;
ShootLaser();
fireTimer = FireCooldown;
}
if (Health <= 0)
{
EmitSignal(SignalName.PlayerDeath);
}
}
public override void _PhysicsProcess(double delta)
{ // every frame
GetInput();
Rotation += _rotationDirection * RotationSpeed * (float)delta;
Rotation += RotationDirection * RotationSpeed * (float)delta;
Velocity.LimitLength(MaxSpeed);
//GD.Print(MainSpeed);

View file

@ -1,4 +1,3 @@
using System.Collections;
using Godot;
public partial class ship : CharacterBody2D
@ -31,7 +30,7 @@ public partial class ship : CharacterBody2D
[Export]
public ShipType type;
[Export]
public ShipFaction faction;
public ShipFaction Faction;
public Sprite2D Sprite = new Sprite2D();
@ -39,20 +38,21 @@ public partial class ship : CharacterBody2D
protected float fireTimer = 0f;
protected string spritePath = "";
public string spritePath = "";
protected int _rotationDirection;
protected int RotationDirection;
protected readonly PackedScene LaserScene = GD.Load<PackedScene>("res://scenes/laser.tscn");
public virtual void ShootLaser()
{
GD.Print(Name, ": firing laser");
//GD.Print(Name, ": firing laser");
Node2D Laser = LaserScene.Instantiate<Node2D>();
Laser.Position = LaserSpawn.GlobalPosition;
Laser.Rotation = Rotation;
EmitSignal(SignalName.LaserShot, Laser);
Laser laser = LaserScene.Instantiate<Laser>();
laser.Position = LaserSpawn.GlobalPosition + -Transform.Y * 25f;
laser.Shooter = this;
laser.Rotation = Rotation;
EmitSignal(SignalName.LaserShot, laser);
}
protected virtual void SetupVisual()
@ -60,7 +60,7 @@ public partial class ship : CharacterBody2D
Sprite = GetNode<Sprite2D>("ShipSprite");
spritePath = ""; // Have to initialise as "" because of switch statements
if (faction == ShipFaction.PLAYER)
if (Faction == ShipFaction.PLAYER)
{
switch (type)
{
@ -77,7 +77,7 @@ public partial class ship : CharacterBody2D
break;
}
}
else if (faction == ShipFaction.FRIENDLY)
else if (Faction == ShipFaction.FRIENDLY)
{
switch (type)
{
@ -95,7 +95,7 @@ public partial class ship : CharacterBody2D
}
}
else if (faction == ShipFaction.ENEMY)
else if (Faction == ShipFaction.ENEMY)
{
switch (type)
{
@ -112,7 +112,7 @@ public partial class ship : CharacterBody2D
break;
}
}
else if (faction == ShipFaction.ACE)
else if (Faction == ShipFaction.ACE)
{
switch (type)
{
@ -129,7 +129,7 @@ public partial class ship : CharacterBody2D
break;
}
}
if (faction == ShipFaction.ENEMY || faction ==ShipFaction.FRIENDLY)
if (Faction == ShipFaction.ENEMY || Faction == ShipFaction.FRIENDLY)
{
Sprite.RotationDegrees = 180;
}
@ -181,7 +181,17 @@ public partial class ship : CharacterBody2D
public virtual void ShipDamage(int damage)
{
Health -= damage;
if (Health < 0){ Health = 0;}
if (Health <= 0)
{
Explode();
}
GD.Print(Name, " health: ", Health);
}
public virtual void Explode()
{
GD.Print(Name, " exploded");
QueueFree();
}
public override void _Ready()
@ -193,7 +203,7 @@ public partial class ship : CharacterBody2D
public override void _PhysicsProcess(double delta)
{
// Common movement logic for all ships
Rotation += _rotationDirection * RotationSpeed * (float)delta;
Rotation += RotationDirection * RotationSpeed * (float)delta;
Velocity.LimitLength(MaxSpeed);
MoveAndSlide();