working some more on enemy movement, they stilldon't always want to move to the sides when they reach the front so still some more work to do. also updated the aiming a bit to clamp the arc for firing and drawing, rewrote the draw arc function

This commit is contained in:
2026-06-07 21:14:46 -04:00
parent 12b565715a
commit 3c9a51bd6a
10 changed files with 144 additions and 39 deletions
+3 -2
View File
@@ -19,8 +19,7 @@ public partial class Commander : Sprite2D
{
if (_attack.Position.Y > GetViewportRect().Size.Y + 50)
{
_attack.QueueFree();
_attack = null;
UnloadAttack();
if (_actions <= 0)
{
EmitSignal(SignalName.ActionsUp);
@@ -32,6 +31,7 @@ public partial class Commander : Sprite2D
public void AttackEnteredBucket()
{
_actions += (_actions + 1) > _actionsMax ? 0 : 1;
UnloadAttack();
}
public void LoadAttack(Vector2 OFFSET)
@@ -65,5 +65,6 @@ public partial class Commander : Sprite2D
public void UnloadAttack()
{
_attack.QueueFree();
_attack = null;
}
}
+10 -1
View File
@@ -7,8 +7,10 @@ public partial class Enemy : StaticBody2D
{
[Signal]
public delegate void DeathEventHandler(Enemy THIS);
public int _damage = 1, _health = 2, _speed, _visibilityRange = 10;
public bool _hovered = false;
public int _damage = 1, _health = 2, _speed, _speedRemaining, _visibilityRange = 4;
public Vector2I _address, _range = Vector2I.Up;
public List<Vector2I> _path = new();
public float _movement = 0;
public EnemyController _enemyController;
@@ -40,4 +42,11 @@ public partial class Enemy : StaticBody2D
EmitSignal(SignalName.Death, this);
}
}
public void OnMouseEntered(){
_hovered = true;
}
public void OnMouseExited(){
_hovered = true;
}
}
+59 -19
View File
@@ -35,6 +35,7 @@ public partial class EnemyController : TurnController
newEnemy.Death += RemoveEnemy;
newEnemy._speed = Globals._rng.Next(2,4+1);
newEnemy._speed = 3;
newEnemy.Modulate = new Color(newEnemy._speed == 2 ? "#FF0000" : newEnemy._speed == 3 ? "#00FF00" : "#0000FF");
newEnemy._enemyController = this;
@@ -56,33 +57,69 @@ public partial class EnemyController : TurnController
{
Tween tween = CreateTween();
tween.SetParallel();
Dictionary<Enemy, List<Vector2I>> enemyPaths = new();
Dictionary<Enemy, int> enemyOffsets = new();
_enemies = [.. _enemies.OrderBy(e => e._address.Y).ThenBy(e => e._address.X)];
for (int i = 0; i < _enemies.Count; i++)
{
Enemy enemy = _enemies[i];
List<Vector2I> path = _playArea._map.GetPath(enemy._address, new Vector2I(enemy._address.X, _playArea._map._minY));
enemyPaths[enemy] = path;
enemyOffsets[enemy] = 0;
Vector2I goal = new(enemy._address.X, _playArea._map._minY);
if (Math.Abs(goal.Y - enemy._address.Y) <= enemy._visibilityRange)
{
// GD.Print(goal.Y, enemy._address.Y,enemy._visibilityRange);
while(_playArea._map.IsRowFull(goal.Y))
{
goal.Y++;
}
// if (_playArea._map.GetOccupant(goal) != null)
// {
// goal.X = Math.Clamp(goal.X+1, _playArea._map._minX+1, _playArea._map._maxX-1);
// }
int loop = 0;
while (_playArea._map.GetOccupant(goal) != null)
{
goal.X = Math.Clamp(enemy._address.X + ((int)MathF.Floor(loop / 2) + 1) * (loop % 2 == 0 ? 1 : -1), _playArea._map._minX, _playArea._map._maxX);
loop++;
}
}
List<Vector2I> path = _playArea._map.GetPath(enemy._address, goal);
enemy._path = path;
enemy._speedRemaining = enemy._address.Y <= _playArea._map._minY ? 0 : enemy._speed;
}
_enemies = [.. _enemies.OrderByDescending(e => e._path.Count).ThenBy(e => e._address.Y).ThenBy(e => Math.Abs(e._address.X))];
int maxSpeed = _enemies.Max(e => e._speedRemaining);
int maxSpeed = _enemies.Max(e => e._speed);
List<Enemy> remainingEnemies = [.. _enemies];
for (int i = 0; i < maxSpeed; i++)
{
for (int j = 0; j < _enemies.Count; j++)
for (int j = 0; j < remainingEnemies.Count; j++)
{
Enemy enemy = _enemies[j];
int offset = enemyOffsets[enemy];
if (i >= enemy._speed - offset)
Enemy enemy = remainingEnemies[j];
if (enemy._speedRemaining <= 0)
{
remainingEnemies.Remove(enemy);
continue;
}
List<Vector2I> path = enemyPaths[enemy];
Vector2I cell = path[i - offset];
List<Vector2I> path = enemy._path;
int pathIndex = enemy._speed - enemy._speedRemaining;
Vector2I cell = path[pathIndex];
Enemy obstacle = _playArea._map.GetOccupant(cell);
if (obstacle != null)
{
int obstacleRemainingMoves = obstacle._speedRemaining;
if (obstacleRemainingMoves <= 0)
{
_playArea._map.EvaluateSolidCells();
List<Vector2I> newPath = _playArea._map.GetPath(enemy._address, new Vector2I(enemy._address.X, _playArea._map._minY));
enemy._path = newPath;
if (_playArea._map.GetOccupant(cell) == null)
newPath.InsertRange(0, [.. Enumerable.Repeat(newPath[0], pathIndex)]);
cell = enemy._path[pathIndex];
obstacle = _playArea._map.GetOccupant(cell);
}
}
if (obstacle == null)
{
_playArea._map.SetCellEnemy(enemy._address, null);
enemy._address = cell;
@@ -96,12 +133,14 @@ public partial class EnemyController : TurnController
tween.Parallel();
}
tween.TweenProperty(enemy, "global_position", _playArea._map.GetCellPositionFromAddress(enemy._address), .2f);
}
else
{
enemyOffsets[enemy]++;
}
if (enemy._address.Y <= _playArea._map._minY)
{
remainingEnemies.Remove(enemy);
continue;
}
enemy._speedRemaining--;
}
}
}
@@ -111,6 +150,7 @@ public partial class EnemyController : TurnController
public void RemoveEnemy(Enemy ENEMY_TO_REMOVE)
{
_enemies.Remove(ENEMY_TO_REMOVE);
_playArea._map.SetCellEnemy(ENEMY_TO_REMOVE._address, null);
ENEMY_TO_REMOVE.QueueFree();
}
+7
View File
@@ -38,6 +38,13 @@ public partial class Main : Node
{
GetTree().Quit();
}
if (Input.IsActionJustPressed("changeTurn"))
{
if(_turnController == _playerController)
{
ChangeTurn();
}
}
}
public void ChangeTurn()
+15 -1
View File
@@ -17,11 +17,12 @@ public partial class Map : TileMapLayer
{
base._Ready();
_cellSize = TileSet.TileSize;
List<Vector2I> cells = GetUsedCells().ToList();
List<Vector2I> cells = [.. GetUsedCells()];
_minX = cells.Min(c => c.X);
_maxX = cells.Max(c => c.X);
_minY = cells.Min(c => c.Y);
_maxY = cells.Max(c => c.Y);
// GD.Print(_minX, _maxX, _minY, _maxY);
_leftmostColumn = [.. cells.Where(c => c.X == _minX)];
_rightmostColumn = [.. cells.Where(c => c.X == _maxX)];
_topRow = [.. cells.Where(c => c.Y == _minY)];
@@ -51,6 +52,14 @@ public partial class Map : TileMapLayer
return hasOccupant || isSolid;
}
public bool IsRowFull(int ROW_TO_CHECK)
{
List<Vector2I> cells = [.. GetUsedCells()];
List<Vector2I> rowCells = [.. cells.Where(c => c.Y == ROW_TO_CHECK)];
return rowCells.All(c => GetOccupant(c) != null);
}
public void SetCellEnemy(Vector2I ADDRESS, Enemy ENEMY)
{
_addressOccupants[ADDRESS] = ENEMY;
@@ -89,4 +98,9 @@ public partial class Map : TileMapLayer
return pathTaken;
}
public void EvaluateSolidCells()
{
GetUsedCells().ToList().ForEach(c => IsCellSolid(c));
}
}
+4 -3
View File
@@ -1,7 +1,7 @@
[gd_scene format=4 uid="uid://dumcridek4xy3"]
[ext_resource type="Script" uid="uid://bnaxgcafcvtfv" path="res://PlayArea.cs" id="1_lq4m8"]
[ext_resource type="PackedScene" uid="uid://mjinvqj25wha" path="res://Map.tscn" id="2_wqv88"]
[ext_resource type="PackedScene" path="res://Map.tscn" id="2_wqv88"]
[ext_resource type="Texture2D" uid="uid://cf554xlykq1o4" path="res://Art/tile_set.png" id="3_md2b6"]
[ext_resource type="PackedScene" uid="uid://b2mb7mimdu5ad" path="res://bucket.tscn" id="5_vqhsw"]
@@ -14,7 +14,7 @@ size = Vector2(400, 1000)
[sub_resource type="RectangleShape2D" id="RectangleShape2D_7h2rc"]
size = Vector2(400, 80)
[sub_resource type="TileMapPattern" id="TileMapPattern_vqhsw"]
[sub_resource type="TileMapPattern" id="TileMapPattern_xy0lb"]
tile_data = PackedInt32Array(0, 0, 0, 65536, 0, 1, 1, 0, 1, 65537, 0, 0)
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_vqhsw"]
@@ -38,7 +38,7 @@ tile_size = Vector2i(40, 40)
custom_data_layer_0/name = "is_solid"
custom_data_layer_0/type = 1
sources/0 = SubResource("TileSetAtlasSource_vqhsw")
pattern_0 = SubResource("TileMapPattern_vqhsw")
pattern_0 = SubResource("TileMapPattern_xy0lb")
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_xy0lb"]
texture = ExtResource("3_md2b6")
@@ -233,5 +233,6 @@ tile_set = SubResource("TileSet_he03c")
position = Vector2(0, 440)
[node name="InitialPositions" type="TileMapLayer" parent="." unique_id=555896823]
visible = false
tile_map_data = PackedByteArray("AAD0//3/AAADAAEAAAD1//z/AAADAAEAAAD1//7/AAADAAEAAAD2//3/AAADAAEAAAD5/wMAAAADAAEAAAD4/wIAAAADAAEAAAD6/wQAAAADAAEAAAD7/wMAAAADAAEAAAD8/wQAAAADAAEAAAD9/wMAAAADAAEAAAD+/wQAAAADAAEAAAD//wMAAAADAAEAAAAAAAMAAAADAAEAAAABAAQAAAADAAEAAAACAAMAAAADAAEAAAADAAQAAAADAAEAAAAEAAMAAAADAAEAAAAFAAQAAAADAAEAAAAGAAMAAAADAAEAAAAHAAIAAAADAAEAAAD8////AAADAAEAAAD9//7/AAADAAEAAAD+//3/AAADAAEAAAD///7/AAADAAEAAAAAAP7/AAADAAEAAAABAP3/AAADAAEAAAACAP7/AAADAAEAAAADAP//AAADAAEAAAAJAP3/AAADAAEAAAAKAP7/AAADAAEAAAALAP3/AAADAAEAAAAKAPz/AAADAAEAAAA=")
tile_set = SubResource("TileSet_vqhsw")
+38 -10
View File
@@ -29,7 +29,8 @@ public partial class Tower : Sprite2D
{
if (_arcEnd != GetGlobalMousePosition())
{
DrawArc(_attackSpawn.GlobalPosition, GetGlobalMousePosition());
float arcAngle = CalculateLaunchAngle(_attackSpawn.GlobalPosition, GetGlobalMousePosition());
DrawArc(arcAngle, _attackSpawn.GlobalPosition, GetGlobalMousePosition());
}
if (Input.IsActionJustPressed("rightClick"))
{
@@ -39,7 +40,7 @@ public partial class Tower : Sprite2D
else if (Input.IsActionJustPressed("leftClick"))
{
_aimOffset = CalculateLaunchAngle(_attackSpawn.GlobalPosition, GetGlobalMousePosition());
_aimOffset = ToLaunchVector(CalculateLaunchAngle(_attackSpawn.GlobalPosition, GetGlobalMousePosition()));
_commander.ShootCurrentAttack(_aimOffset);
@@ -58,7 +59,7 @@ public partial class Tower : Sprite2D
}
}
public Vector2 CalculateLaunchAngle(Vector2 START, Vector2 END, int MAX_ITERATIONS = 20)
public float CalculateLaunchAngle(Vector2 START, Vector2 END, int MAX_ITERATIONS = 20)
{
Vector2 offset = END - START;
float baseAngle = offset.Angle();
@@ -95,8 +96,9 @@ public partial class Tower : Sprite2D
bestAngle = testAngle;
}
bestAngle = (float)Mathf.Clamp(bestAngle, Math.PI, Math.PI * 2);
return Vector2.FromAngle(bestAngle) * _launchSpeed;
return bestAngle;
}
public void ClearArc()
@@ -105,16 +107,40 @@ public partial class Tower : Sprite2D
path.Curve.ClearPoints();
}
public void DrawArc(Vector2 START, Vector2 END, int MAX_ITERATIONS = 20)
public void DrawArc(float ANGLE, Vector2 START, Vector2 END, int MAX_ITERATIONS = 20)
{
CalculateLaunchAngle(START, END);
float speed = _launchSpeed, gravity = Globals._gravity, drag = Globals._drag, delta = 1.0f / 60.0f;
Vector2 velocity = new Vector2(Mathf.Cos(ANGLE), Mathf.Sin(ANGLE)) * speed;
Vector2 position = START;
float directionSign = Mathf.Sign(END.X - START.X);
float maxFlightTime = 6.0f;
int totalSteps = (int)(maxFlightTime / delta);
List<Vector2> arc = [START];
for (int step = 0; step < totalSteps; step++)
{
velocity.Y += gravity * delta;
velocity -= velocity * drag * delta;
position += velocity * delta;
arc.Add(position);
bool reachedTargetX = (directionSign >= 0) ? (position.X >= END.X) : (position.X <= END.X);
if (reachedTargetX)
{
break;
}
}
Path2D path = _commander._attack.GetNode<Path2D>("PredictedPath");
path.Curve.ClearPoints();
for (int i = 0; i < _arc.Count; i++)
for (int i = 0; i < arc.Count; i++)
{
path.Curve.AddPoint(_arc[i]);
path.Curve.AddPoint(arc[i]);
}
}
@@ -127,14 +153,12 @@ public partial class Tower : Sprite2D
float directionSign = Mathf.Sign(TARGET.X - START.X);
float maxFlightTime = 6.0f;
int totalSteps = (int)(maxFlightTime / delta);
_arc = [START];
for (int step = 0; step < totalSteps; step++)
{
velocity.Y += gravity * delta;
velocity -= velocity * drag * delta;
position += velocity * delta;
_arc.Add(position);
bool reachedTargetX = (directionSign >= 0) ? (position.X >= TARGET.X) : (position.X <= TARGET.X);
@@ -144,7 +168,6 @@ public partial class Tower : Sprite2D
}
}
_arcEnd = _arc[^1];
return (position.Y > TARGET.Y) ? 99999f : -99999f;
}
@@ -156,6 +179,11 @@ public partial class Tower : Sprite2D
}
}
public Vector2 ToLaunchVector(float ANGLE)
{
return Vector2.FromAngle(ANGLE) * _launchSpeed;
}
public void OnMouseEntered(){
_hovered = true;
}
+2 -2
View File
@@ -1,7 +1,7 @@
[gd_scene format=3 uid="uid://dcp7p6al4i0b7"]
[ext_resource type="Script" uid="uid://cg1m762ed04kv" path="res://Main.cs" id="1_ig7tw"]
[ext_resource type="PackedScene" path="res://PlayArea.tscn" id="2_1bvp3"]
[ext_resource type="PackedScene" uid="uid://dumcridek4xy3" path="res://PlayArea.tscn" id="2_1bvp3"]
[ext_resource type="PackedScene" uid="uid://c6b188d2a20eq" path="res://enemy_controller.tscn" id="4_1bvp3"]
[ext_resource type="PackedScene" uid="uid://b7kvx7p0b2086" path="res://player_controller.tscn" id="4_lquwl"]
@@ -9,7 +9,7 @@
script = ExtResource("1_ig7tw")
[node name="PlayArea" parent="." unique_id=1123610167 instance=ExtResource("2_1bvp3")]
position = Vector2(960, 510)
position = Vector2(931, 542)
[node name="EnemyController" parent="." unique_id=1894449838 instance=ExtResource("4_1bvp3")]
+5
View File
@@ -46,6 +46,11 @@ rightClick={
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":2,"position":Vector2(109, 18),"global_position":Vector2(118, 66),"factor":1.0,"button_index":2,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}
changeTurn={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":84,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
[physics]
+1 -1
View File
@@ -22,7 +22,7 @@ debug_color = Color(0.6411928, 0.52469516, 0, 0.41960785)
position = Vector2(25, -50)
[node name="AttackSpawn" type="Marker2D" parent="." unique_id=715904909]
position = Vector2(0, 50)
position = Vector2(0, -24)
[connection signal="mouse_entered" from="Area" to="." method="OnMouseEntered"]
[connection signal="mouse_exited" from="Area" to="." method="OnMouseExited"]