Grid-based navigation of multiple enemies on a TileMap

Godot Version

Godot 4.6 (.NET)

Question

Hi!
I’ve been trying to create a grid-based turn-based movement system on a TileMapLayer using a NavigationAgent2D node on my enemy scene.
As of now they correctly move on all 8 sides looking for the player, and when they reach it they use their turn to attack.
Everything’s great up until I decide to create another instance of the aforementioned enemy. They both work properly, but there are situations where they overlap each other.

Specifics:

  • I’m using the Navigation Layers property of the TileMapLayer to ensure the enemy won’t walk on water or terrain, this works just fine as of now.
  • If there’s anything else I need to add to the info, feel free to ask!
  • I’ve started coding less than a month ago, so feel free to point any newbie mistake.

Here’s the code I’m using for the enemy:

using Godot;
using System;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;

public partial class BlueBlob : CharacterBody2D
{	
	// Area used to check wether the player is in range for attack
	public Area2D attackArea;
	// X-axis value to which the enemy is going to move
	public double valueToLerpX;
	// Y-axis value to which the enemy is going to move
	public double valueToLerpY;
	// Future check on wether this lil thing can walk on water or walls (as of now, not used, I am aware)
	public bool canThisMonsterWalkThroughWater = false;
	public bool canThisMonsterWalkThroughWalls = false;
	// Movement animation
	public Tween tween;
	// Point to reach (attached to the enemy)
	public NavigationAgent2D navigator;

	public void EnemyMovement()
	{
		// Check wether the enemy wants to move diagonally and sets the right path
		if (Position.X < navigator.GetNextPathPosition().X)
		{
		valueToLerpX = Position.X + NewPlayer.tileSize;
		}
		else if (Position.X == navigator.GetNextPathPosition().X)
		{
			valueToLerpX = Position.X;
		}
		else {valueToLerpX = Position.X - NewPlayer.tileSize;}

		if (Position.Y < navigator.GetNextPathPosition().Y)
		{
			valueToLerpY = Position.Y + NewPlayer.tileSize;
		}
		else if (Position.Y == navigator.GetNextPathPosition().Y)
		{
			valueToLerpY = Position.Y;
		}
		else { valueToLerpY = Position.Y - NewPlayer.tileSize;}
		//

		if (!attackArea.OverlapsArea(NewPlayer.playerArea))
		{
		// Movement animation
		tween = CreateTween();
		tween.TweenProperty(this, "position", new Godot.Vector2((float)valueToLerpX, (float)valueToLerpY), 0.4f);
		}
		else 
		{
			tween = CreateTween();
			tween.TweenProperty(this, "position", Position, 0.4f);
			NewPlayer.HP = NewPlayer.HP - 5;
		}

	}

	public override void _Ready()
	{
		navigator = GetNode<NavigationAgent2D>("NavigationAgent2D");
		attackArea = GetNode<Area2D>("AttackCheckArea");
		tween = CreateTween();
		tween.TweenProperty(this, "position", Position, 0.4f);

		valueToLerpX = Position.X;
		valueToLerpY = Position.Y;
		EventManager.enemiesInLevel++;
	}


	public override void _PhysicsProcess(double delta)
	{
		// Sets target (the player)
		navigator.TargetPosition = NewPlayer.playerTarget;
		
		// Enemy movement if it's their turn and not all enemies have played yet
		if (NewPlayer.isPlayerTurn == false && EventManager.enemiesTurns < EventManager.enemiesInLevel)
		{
			EnemyMovement();
			EventManager.enemiesTurns++;
		}

		// if all enemies have reached their position, stops their turn
		if (tween.IsRunning() == false)
		{
			NewPlayer.isPlayerTurn = true;
		}

		}
	}
}

And here’s the situation for the Enemy scene and the NavigationAgent2D:

My Questions:

  • Is there a way to avoid these enemies overlapping each other on the same cell? I’d like to avoid doing this through code alone directly on the Character since it’s bug-prone, I’d rather code on the NavigationAgent (or the NavigationObstacles? I’d think they are supposed to be used this way, but I couldn’t manage to make them work).
  • Am I supposed to use the avoidance system on the NavigationAgent? Again, I’ve tried messing with it for a couple hours but to no avail.
  • Looking for ways to solve this problem I’ve read about the AStarGrid2D system, am I correct assuming this is the same as AStar Edgebased system? Is it something else entirely?