Exemple #1
0
        /// <summary> This returns a list of nodes around the given node, using the notion of "movement costs" to determine
        /// if a node should be included in the returned list or not. The callback can be used to specify the "cost" to
        /// reach the specified node, making this useful to select the nodes that a unit might be able to move to. </summary>
        /// <param name="nodeIdx"> The central node's index. </param>
        /// <param name="radius"> The maximum area around the node to select nodes from. </param>
        /// <param name="callback"> An optional callback (pass null to not use it) that can be used to indicate the cost of
        /// moving from one node to another. By default it will "cost" 1 to move from one node to another. By returning 0 in
        /// this callback you can indicate that the target node can't be moved to (for example when the tile is occupied).
        /// Return a value higher than one (like 2 or 3) if moving to the target node would cost more and potentially
        /// exclude the node from the returned list of nodes (when cost to reach it would be bigger than "radius"). </param>
        /// <returns> Returns a list of node indices that can be used with grid[]. Returns empty list (not null) if there was an error. </returns>
        public virtual List <int> NodeIndicesAround(int nodeIdx, int radius, NodeCostCallback callback)
        {
            List <int> accepted           = new List <int>();              // accepted nodes
            Dictionary <int, float> costs = new Dictionary <int, float>(); // <idx, cost> - used to track which nodes have been checked

            CheckNodesRecursive(nodeIdx, radius, callback, -1, 0, ref accepted, ref costs);
            return(accepted);
        }
Exemple #2
0
        /// <summary> This returns a list of nodes around the given node, using the notion of "movement costs" to determine
        /// if a node should be included in the returned list or not. The callback can be used to specify the "cost" to
        /// reach the specified node, making this useful to select the nodes that a unit might be able to move to. </summary>
        /// <param name="node"> The central node. </param>
        /// <param name="radius"> The maximum area around the node to select nodes from. </param>
        /// <param name="callback"> An optional callback (pass null to not use it) that can be used to indicate the cost of
        /// moving from one node to another. By default it will "cost" 1 to move from one node to another. By returning 0 in
        /// this callback you can indicate that the target node can't be moved to (for example when the tile is occupied).
        /// Return a value higher than one (like 2 or 3) if moving to the target node would cost more and potentially
        /// exclude the node from the returned list of nodes (when cost to reach it would be bigger than "radius"). </param>
        /// <returns> Returns a list of nodes that can be used with grid[]. Returns empty list (not null) if there was an error. </returns>
        public virtual List <T> NodesAround <T>(MapNavNode node, int radius, NodeCostCallback callback)
            where T : MapNavNode
        {
            List <int> accepted = NodeIndicesAround(node.idx, radius, callback);

            if (accepted.Count > 0)
            {
                List <T> res = new List <T>();
                for (int i = 0; i < accepted.Count; i++)
                {
                    res.Add((T)grid[accepted[i]]);
                }
                return(res);
            }
            return(new List <T>(0));
        }
Exemple #3
0
        /// <summary> This is a Helper for NodeIndicesAround(int idx, int radius, bool includeCentralNode, ValidationCallback callback) </summary>
        protected virtual void CheckNodesRecursive(int idx, int radius, NodeCostCallback callback, int cameFrom, float currDepth, ref List <int> accepted, ref Dictionary <int, float> costs)
        {
            List <int> ids = _neighbours(idx);

            for (int i = 0; i < ids.Count; i++)
            {
                // skip if came into this function from this node. no point in testing
                // against the node that caused this one to be called for checking
                if (cameFrom == ids[i])
                {
                    continue;
                }

                // get cost to move to the node
                float res = callback == null ? 1f : callback(grid[idx], grid[ids[i]]);

                // can move to node?
                if (res <= 0.0f)
                {
                    continue;
                }

                // how much would it cost in total?
                float d = currDepth + res;

                // too much to reach node?
                if (d > radius)
                {
                    continue;
                }

                // this neighbour node can be moved to, add it to the accepted list if not yet present
                if (false == accepted.Contains(ids[i]))
                {
                    accepted.Add(ids[i]);
                }

                // do not bother to check the node's neighbours if already reached the max
                if (d == radius)
                {
                    continue;
                }

                // check if should look into the neighbours of this node
                if (costs.ContainsKey(ids[i]))
                {
                    // if the new total cost is higher than previously checked then skip this neighbour
                    // since testing with the higher costs will not change any results when checking
                    // the neighbours of this neighbour node
                    if (costs[ids[i]] <= d)
                    {
                        continue;
                    }
                }
                else
                {
                    costs.Add(ids[i], d);
                }

                // update the cost to move to this node
                costs[ids[i]] = d;

                // and test its neighbours for possible valid nodes
                CheckNodesRecursive(ids[i], radius, callback, idx, d, ref accepted, ref costs);
            }
        }
Exemple #4
0
        /// <summary> Returns a list of nodes that represents a path from one node to another. An A* algorithm is used
        /// to calculate the path. Return an empty list on error or if the destination node can't be reached. </summary>
        /// <param name="start">	The node where the path should start. </param>
        /// <param name="end">		The node to reach. </param>
        /// <param name="callback"> An optional callback that can return an integer value to indicate the
        ///							cost of moving onto the specified node. This callback should return 1
        ///							for normal nodes and 2+ for higher cost to move onto the node and 0
        ///							if the node can't be moved onto; for example when the node is occupied. </param>
        /// <returns> Return an empty list on error or if the destination node can't be reached. </returns>
        public virtual List <T> Path <T>(MapNavNode start, MapNavNode end, NodeCostCallback callback)
            where T : MapNavNode
        {
            if (start == null || end == null)
            {
                return(new List <T>(0));
            }
            if (start.idx == end.idx)
            {
                return(new List <T>(0));
            }

            List <T> path      = new List <T>();
            int      current   = -1;
            int      next      = -1;
            float    new_cost  = 0;
            float    next_cost = 0;
            double   priority  = 0;

            // first check if not direct neighbour and get out early
            List <int> neighbors = PathNodeIndicesAround(start.idx);            // NodeIndicesAround(start.idx, false, false, null);

            if (neighbors != null)
            {
                if (neighbors.Contains(end.idx))
                {
                    if (callback != null)
                    {
                        next_cost = callback(start, end);
                        if (next_cost >= 1)
                        {
                            path.Add((T)end);
                        }
                    }
                    return(path);
                }
            }

            HeapPriorityQueue <PriorityQueueNode> frontier = new HeapPriorityQueue <PriorityQueueNode>(grid.Length);

            frontier.Enqueue(new PriorityQueueNode()
            {
                idx = start.idx
            }, 0);
            Dictionary <int, int>   came_from   = new Dictionary <int, int>();       // <for_idx, came_from_idx>
            Dictionary <int, float> cost_so_far = new Dictionary <int, float>();     // <idx, cost>

            came_from.Add(start.idx, -1);
            cost_so_far.Add(start.idx, 0);

            while (frontier.Count > 0)
            {
                current = frontier.Dequeue().idx;
                if (current == end.idx)
                {
                    break;
                }

                neighbors = PathNodeIndicesAround(current);                 //NodeIndicesAround(current, false, false, null);
                if (neighbors != null)
                {
                    for (int i = 0; i < neighbors.Count; i++)
                    {
                        next = neighbors[i];
                        if (callback != null)
                        {
                            next_cost = callback(grid[current], grid[next]);
                        }
                        if (next_cost <= 0.0f)
                        {
                            continue;
                        }

                        new_cost = cost_so_far[current] + next_cost;
                        if (false == cost_so_far.ContainsKey(next))
                        {
                            cost_so_far.Add(next, new_cost + 1);
                        }
                        if (new_cost < cost_so_far[next])
                        {
                            cost_so_far[next] = new_cost;
                            priority          = new_cost + Heuristic(start.idx, end.idx, next);
                            frontier.Enqueue(new PriorityQueueNode()
                            {
                                idx = next
                            }, priority);
                            if (false == came_from.ContainsKey(next))
                            {
                                came_from.Add(next, current);
                            }
                            else
                            {
                                came_from[next] = current;
                            }
                        }
                    }
                }
            }

            // build path
            next = end.idx;
            while (came_from.ContainsKey(next))
            {
                if (came_from[next] == -1)
                {
                    break;
                }
                if (came_from[next] == start.idx)
                {
                    break;
                }
                path.Add((T)grid[came_from[next]]);
                next = came_from[next];
            }

            if (path.Count > 0)
            {
                path.Reverse();
                path.Add((T)end);
            }

            return(path);
        }