This never came up as I was always trying to shoot them or blow them up (or I missed it), but…
While testing the door I found myself walking through the enemies even though their gridPositions were visually “not-walkable”.
I solved it by putting the UnitEnemy prefab on the Obstacle layer. Im not sure this would come up in gameplay, but I thought Id mention it.
I took a simpler approach… In my game, all Units are not walkable (creates an extra amount of tactics required, as you can accidentally block off your teammates in a choke point, etc).
So when the pathfinding starts, I simply FindObjectsOfType<Unit>()
and add their Grid Positions to the list of spaces that have already been searched. This effectively removes the spaces from consideration.
That’s a design question, in the course’s design yup the enemies (and all units) are walkable. You cannot stop on top of another unit but you can go through them.
If that’s not the design you want then the solution is like Brian mentioned, when pathfinding look at where the units are and define those positions as unwalkable. Just with that the pathfinding will no longer go through them.
FindPath(
GridPosition startGridPosition,
GridPosition endGridPosition,
out int pathLength
)
{
List<PathNode> openList = new List<PathNode>();
List<PathNode> closedList = new List<PathNode>();
PathNode startNode = gridSystem.GetGridObject(startGridPosition);
PathNode endNode = gridSystem.GetGridObject(endGridPosition);
openList.Add (startNode);
// https://community.gamedev.tv/t/are-enemies-obstacles/208862?_gl=1*hth6o8*_ga*NzEyNzA4MzAyLjE2MzQ5Mjg0NjM.*_ga_2C81L26GR9*MTY2MDI3MzY1Ni4xNjUuMS4xNjYwMjc0NDc4LjA
Unit[] units = FindObjectsOfType<Unit>();
foreach (Unit unit in units)
{
GridPosition gridPosition = unit.GetGridPosition();
if (startGridPosition != gridPosition)
{
closedList.Add(gridSystem.GetGridObject(gridPosition));
}
}
...
You might want to also filter the EndGridPosition
Thanks Hugo, Brian and Johnathan, this is very useful from a correct code solution standpoint and I think I will implement this shortly. However, correct or not, as a stopgap while playing with level design, just putting the enemies on the obstacle layer gave me just the behavior I needed in terms of going around any enemies on my way to an interactable etc.
This was an excellent class and Im sure I will go back and do it all over again at some point.
I took a slightly different approach by excluding the positions from the list returned by GetNeighbourList
if there’s a unit on the position.
private List<PathNode> GetNeighbourList(PathNode currentNode)
{
var neighbours = new List<PathNode>();
var gridPosition = currentNode.GetGridPosition();
for (var z = -1; z <= 1; z++)
{
for (var x = -1; x <= 1; x++)
{
if (x == 0 && z == 0) continue; // skip self
//if (x != 0 && z != 0) continue; // skip diagonals
var neighbourPosition = new GridPosition(gridPosition.X + x, gridPosition.Z + z);
if (!_grid.IsValidGridPosition(neighbourPosition)) continue;
// skip the node if there's a Unit on it
if (LevelGrid.Instance.GetFirstUnitAtGridPosition(neighbourPosition) != null) continue;
var neighbourNode = GetNode(neighbourPosition);
if (!neighbourNode.GetIsWalkable()) continue;
neighbours.Add(neighbourNode);
}
}
return neighbours;
}
I didn’t look into optimisation, so it may well be a pretty expensive operation
This feels significantly slower. But I’m not sure.
Would it make sense to have the unit handle that as it moves around? (and at start)
Setting the “iswalkable”
My first instinct is no. It’s already doing something like that with LevelGrid. In fact, we have an event in LevelGrid that would be ideal for this purpose:
public event EventHandler<OnAnyUnitMovedGridPositionEventArgs> OnAnyUnitMovedGridPosition;
public class OnAnyUnitMovedGridPositionEventArgs : EventArgs
{
public Unit unit;
public GridPosition fromGridPosition;
public GridPosition toGridPosition;
}
This event is raised in the LevelGrid.UnitMovedGridPosition called by Unit.
You could have PathFinder subscribe to this event in LevelGrid, and then set the isWalkable on the applicable PathNodes.
public void UnitMovedGridPosition(Unit unit, GridPosition fromGridPosition, GridPosition toGridPosition)
{
RemoveUnitAtGridPosition(fromGridPosition, unit);
AddUnitAtGridPosition(toGridPosition, unit);
OnAnyUnitMovedGridPosition?.Invoke(this, new OnAnyUnitMovedGridPositionEventArgs {
unit = unit,
fromGridPosition = fromGridPosition,
toGridPosition = toGridPosition,
});
}