I recently started up again on a 2D game engine I have been working on over the last couple of years. I managed to implement a version of the a* algorithm that seems to work fine for my game, but it seems to be having a dramatic impact on my games performance when I have multiple enemies using it.
Currently I am calling a* only if there is an obstacle between the player and the enemy, and then every frame while there is still a collision between enemy and player. Could you please take a look at my code and let me know if you can see anything obvious that might be slowing it down
public class PathFinder
{
// Open list using HashSet for O(1) coontains lookup times
private HashSet<PathTile> closedList;
// Closed list using Minheap for O(1) smallest lookup times
private MinHeap<PathTile> openList;
// The player target position
private RectangleF target;
public List<PathTile> FindPath(TileEngine tilemap, RectangleF target, RectangleF startPos)
{
// Assign Values to variables
openList = new MinHeap<PathTile>();
closedList = new HashSet<PathTile>(new PathTileComparer());
this.target = target;
// Compute the F-Value of the starting position and create a new PathTile Object
int HValue = ComputeHValue(startPos);
PathTile p = new PathTile(HValue, HValue, 0, startPos, null);
// Add the path tile to the
openList.Add(p);
// Begin the search for the shortest path and return a list containing the Path Tiles or null
return GeneratePath(p, startPos,tilemap);
}
private List<PathTile> GeneratePath(PathTile tile, RectangleF startPos, TileEngine tileMap)
{
PathTile temp = tile;
// Loop until a path to the target is found or the enemy is blocked in
while (true)
{
//Checks all adjacent tiles
Check(temp, "Right", startPos, tileMap);
Check(temp, "Left", startPos, tileMap);
Check(temp, "Up", startPos, tileMap);
Check(temp, "Down", startPos, tileMap);
// If open list is empty not path has been found
if (openList.Count == 0)
return null;
//Finds the tile with the smallest F value in the open list.
PathTile tempTile = openList.Peek();
// Adds the PathTile with the smallest F Score into the closed list storing a reference to it's parent
closedList.Add(new PathTile(tempTile.fScore, tempTile.hScore, tempTile.gScore, tempTile.rect, temp));
// Sets the PathTile to be stored as the parent to the next smallest value
temp = tempTile;
// Removes the PathTile from the open list
openList.RemoveMin();
// If the target has been reached move back up the closed list and return the path
if (temp.rect.IntersectsWith(target))
{
List<PathTile> tempPathList = new List<PathTile>();
PathTile t = temp;
tempPathList.Add(t);
while (t.parent != null)
{
t = t.parent;
tempPathList.Insert(0, t);
}
closedList.Clear();
openList = null;
return tempPathList;
}
}
}
private void Check(PathTile tile, String direction, RectangleF enemy, TileEngine tileMap)
{
PathTile tempTile = new PathTile(new RectangleF(tile.rect.X, tile.rect.Y, tile.rect.Width, tile.rect.Height));
int Hvalue = 0;
Boolean condition = true;
if (direction.Equals("Right"))
{
tempTile.rect.X = tempTile.rect.X + tempTile.rect.Width;
condition = tempTile.rect.X < target.X;
}
else if (direction.Equals("Left"))
{
tempTile.rect.X = tempTile.rect.X - tempTile.rect.Width;
condition = tempTile.rect.X > target.X;
}
else if (direction.Equals("Up"))
{
tempTile.rect.Y = tempTile.rect.Y - tempTile.rect.Height;
condition = tempTile.rect.Y > target.Y;
}
else if (direction.Equals("Down"))
{
tempTile.rect.Y = tempTile.rect.Y + tempTile.rect.Height;
condition = tempTile.rect.Y < target.Y;
}
if (!tempTile.rect.IntersectsWith(target))
{
if (condition)
Hvalue = tile.hScore - 1;
else
Hvalue = tile.hScore + 1;
}
else
Hvalue = tile.hScore - 1;
if (!tileMap.checkTileCollision(tempTile.rect) && !closedList.Contains(tempTile) && tempTile.rect.X > 0 && tempTile.rect.Y > 0)
{
//if (!tilemap.CheckEnemyCollisionRecter(tempTile.rect,enemy2))
openList.Add(new PathTile(Hvalue + (tile.gScore + 1), Hvalue, tile.gScore + 1, tempTile.rect, tile));
}
}
private int ComputeHValue(RectangleF rec)
{
int FCount = 0;
while (true)
{
if (rec.X > target.X && rec.X >= (target.X + target.Width))
{
rec.X = rec.X - rec.Width;
FCount++;
}
else if (rec.X < target.X && (rec.X + rec.Width <= target.X))
{
rec.X = rec.X + rec.Width;
FCount++;
}
if (rec.Y > target.Y && rec.Y >= (target.Y + target.Height))
{
rec.Y = rec.Y - rec.Width;
FCount++;
}
else if (rec.Y < target.Y && (rec.Y + rec.Height <= target.Y))
{
rec.Y = rec.Y + rec.Width;
FCount++;
}
if (rec.IntersectsWith(target))
return FCount;
}
}
}