예제 #1
0
        //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);
        }