public void SetNeighbors(List <DijkstraNode> nodes) { Point left = new Point(Position.X - 1, Position.Y); if (left.X >= 0) { int pos = left.Y * TileMap.Width + left.X; DijkstraNode leftNeighbor = nodes[pos]; if (leftNeighbor != null) { Neighbors.Add(leftNeighbor); } } Point right = new Point(Position.X + 1, Position.Y); if (right.X < TileMap.Width) { int pos = right.Y * TileMap.Width + right.X; DijkstraNode rightNeighbor = nodes[pos]; if (rightNeighbor != null) { Neighbors.Add(rightNeighbor); } } Point top = new Point(Position.X, Position.Y - 1); if (top.Y >= 0) { int pos = top.Y * TileMap.Width + top.X; DijkstraNode topNeighbor = nodes[pos]; if (topNeighbor != null) { Neighbors.Add(topNeighbor); } } Point bottom = new Point(Position.X, Position.Y + 1); if (bottom.Y < TileMap.Height) { int pos = bottom.Y * TileMap.Width + bottom.X; DijkstraNode bottomNeighbor = nodes[pos]; if (bottomNeighbor != null) { Neighbors.Add(bottomNeighbor); } } }
//Source : http://en.wikipedia.org/wiki/Pathfinding //TODO: Still needs improvement //A good way to improve both this algorithm and the a* one, would be to load the Nodes when the map is created, //and keep them either in TileMap or in each tile. Or something like that. /// <summary> /// Find the closest element or the chosen type from the from point. /// </summary> /// <typeparam name="T">Type of the object we are looking for.</typeparam> /// <param name="from">The position we're starting from.</param> /// <param name="validate">A lambda operation to validate if the object found is good. If the lamdba operation fails, the algorithm will look for another one. Note : Won't be tested if null.</param> /// <returns>Return the closest object found. If none are found, returns null.</returns> public static T FindClosest <T>(Point from, Func <T, bool> validate = null, bool includeChilds = false) where T : GameObject { int nbElements = TileMap.Width * TileMap.Height; int nbSolidItems = 0; List <DijkstraNode> nodes = new List <DijkstraNode>(); for (int i = 0; i < nbElements; i++) { int x = i % TileMap.Width; int y = (int)Math.Floor((double)(i / TileMap.Width)); nodes.Add(new DijkstraNode() { Distance = int.MaxValue, Position = new Point(x, y) }); if (!IsWalkable(new Point(x, y))) { nbSolidItems++; } } DijkstraNode start = nodes.FirstOrDefault(x => x.Position == from); start.Distance = 0; List <DijkstraNode> Q = nodes.ToList(); //We don't want to check the solid items in the path, but want to keep them //because we may be looking for them. while (Q.Count - nbSolidItems > 0) { //TODO : Improve it? //DijkstraNode u = // Q.Where(x => IsWalkable(x.Position)) //Get all non-solid items // .MinBy(o => o.Distance); //What is better : MinBy or Aggregate? //.Aggregate((curmin, x) => (curmin == null || x.Distance < curmin.Distance ? x : curmin)); //Get the one with the smallest value DijkstraNode u = Q[0]; for (int i = 1; i < Q.Count; i++) { DijkstraNode n = Q[i]; if (IsWalkable(n.Position) && n.Distance < u.Distance) { u = n; } } Q.Remove(u); if (u.Distance == int.MaxValue) { break; } u.SetNeighbors(nodes); foreach (DijkstraNode v in u.Neighbors) { T obj = TileMap.Tiles[v.Position.X, v.Position.Y].GetFirstObject <T>(includeChilds); if (obj != null) { if (validate == null || validate.Invoke(obj)) { return(obj); } } int alt = u.Distance + 1; if (alt < v.Distance) { v.Distance = alt; v.Previous = u; } } } return(null); }