Beispiel #1
0
        /// <summary> Destroy Grid array and creates a new one from properties. </summary>
        /// <param name="nodeType"> You can specify a custom Node class derived from MapNavNode </param>
        public override void CreateGrid(System.Type nodeType)
        {
            grid = new MapNavNode[mapHorizontalSize * mapVerticalSize];

            Vector3 posOffs = new Vector3(-mapHorizontalSize * nodeSize / 2f, 0f, -mapVerticalSize * nodeSize / 2f) + new Vector3(nodeSize / 2f, 0f, nodeSize / 2f);
            int     idx     = -1;

            for (int y = 0; y < mapVerticalSize; y++)
            {
                for (int x = 0; x < mapHorizontalSize; x++)
                {
                    idx++;
                    grid[idx]        = (MapNavNode)ScriptableObject.CreateInstance(nodeType);
                    grid[idx].idx    = idx;
                    grid[idx].q      = x;
                    grid[idx].r      = y;
                    grid[idx].parent = transform;
                    grid[idx].h      = (nodeHeightOpt == NodeHeightOpt.Flat ? minNodeHeight : Random.Range(minNodeHeight, maxNodeHeight + 1));

                    grid[idx].localPosition = new Vector3(nodeSize * x, nodeHeightStep * grid[idx].h, nodeSize * y) + posOffs;

                    OnNodeCreated(grid[idx]);
                }
            }

            OnGridChanged(true);
        }
Beispiel #2
0
        /// <summary> Return a list of nodes in a specified range around the given node.
        /// The returned list is in no specific order. Excludes invalid nodes. </summary>
        /// <param name="node">		The central node. </param>
        /// <param name="radius">	Radius from central node that ring starts. </param>
        /// <param name="width">	Width of the ring. </param>
        /// <param name="callback">	An optional callback that can first check if the node is "valid" and
        ///							return True if so, else it should return False. </param>
        /// <returns>Returns null if there was an error. </returns>
        public virtual List <T> NodesRing <T>(MapNavNode node, int radius, int width, ValidationCallback callback)
            where T : MapNavNode
        {
            if (radius < 1)
            {
                radius = 1;
            }
            if (width < 1)
            {
                width = 1;
            }

            List <T> all   = NodesAround <T>(node, radius + (width - 1), false, callback);
            List <T> inner = radius > 1 ? NodesAround <T>(node, radius - 1, false, callback) : new List <T>();

            if (all == null || inner == null)
            {
                return(null);
            }

            for (int i = 0; i < inner.Count; i++)
            {
                if (all.Contains(inner[i]))
                {
                    all.Remove(inner[i]);
                }
            }

            return(all);
        }
Beispiel #3
0
        // ------------------------------------------------------------------------------------------------------------

        /// <summary> Returns the distance from node 1 to node 2. Returns 0 on error. </summary>
        public virtual int Distance(MapNavNode node1, MapNavNode node2)
        {
            if (node1 == null || node2 == null)
            {
                return(0);
            }
            return(Distance(node1.idx, node2.idx));
        }
        private void DrawLinkToolMarkMode()
        {
            // check what node was clicked on
            if (Event.current.button == 0 && (Tools.viewTool == ViewTool.Pan || Tools.viewTool == ViewTool.None))
            {
                int nSet = Event.current.modifiers == EventModifiers.Shift ? 1 : 0;
                if (Event.current.type == EventType.MouseDown || Event.current.type == EventType.MouseDrag)
                {
                    float rayDist = 0f;
                    Ray   ray     = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
                    if (plane.Raycast(ray, out rayDist))
                    {
                        Vector3    rayPoint = ray.GetPoint(rayDist);
                        MapNavNode n        = mapnav.NodeAtWorldPosition <MapNavNode>(rayPoint);
                        if (n != null)
                        {
                            if (lastMarked != n.idx)
                            {
                                lastMarked = n.idx;
                                if (linkTool_Nodes[nSet].Contains(n.idx))
                                {
                                    linkTool_Nodes[nSet].Remove(n.idx);
                                }
                                else
                                {
                                    linkTool_Nodes[nSet].Add(n.idx);
                                }
                            }
                        }
                    }
                    Event.current.Use();
                }
                else if (Event.current.type == EventType.MouseUp)
                {
                    lastMarked = -1;
                    Event.current.Use();
                }
            }

            float sz = mapnav.nodeSize / 3f;

            Handles.color = Color.blue;
            for (int i = 0; i < linkTool_Nodes[0].Count; i++)
            {
                Handles.DrawSolidDisc(mapnav.grid[linkTool_Nodes[0][i]].position, Vector3.up, sz);
            }
            sz           -= 0.05f;
            Handles.color = Color.red;
            for (int i = 0; i < linkTool_Nodes[1].Count; i++)
            {
                Handles.DrawSolidDisc(mapnav.grid[linkTool_Nodes[1][i]].position, Vector3.up, sz);
            }
        }
Beispiel #5
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));
        }
Beispiel #6
0
 /// <summary> Override this for notification while the while a grid is being created. The function is called for
 /// each Node being added to the node. You could use this to add your own initialization data to a custom node
 /// being created via CreateGrid[T]() </summary>
 /// <param name="node"> The node that was created. </param>
 public virtual void OnNodeCreated(MapNavNode node)
 {
 }
Beispiel #7
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);
        }
Beispiel #8
0
 /// <summary> Return list of nodes in a certain range around given node.
 /// The returned list is in no specific order. Excludes invalid nodes.
 /// </summary>
 /// <param name="node">				The central node around which to get neighbouring nodes.</param>
 /// <param name="range">			The radius around the central node. </param>
 /// <param name="includeCentralNode">Should the central node be included? It will be added first in the list if so.</param>
 /// <param name="callback">			An optional callback that can first check if the node is "valid" and return True if
 ///									so, else it should return False. </param>
 /// <returns>Returns null if there was an error. </returns>
 public virtual List <T> NodesAround <T>(MapNavNode node, int range, bool includeCentralNode, ValidationCallback callback)
     where T : MapNavNode
 {
     return(null);
 }
Beispiel #9
0
        /// <summary>
        /// Return list of nodes in a certain range around given node.
        /// The returned list is in no specific order. Excludes invalid nodes.
        /// </summary>
        /// <param name="node">				The central node around which to get neighboring nodes.</param>
        /// <param name="radius">			The radius around the central node. </param>
        /// <param name="includeCentralNode">Should the central node be included? It will be added first in the list if so.</param>
        /// <param name="callback">			An optional callback that can first check if the node is "valid" and return True if
        ///									so, else it should return False. </param>
        /// <returns>Returns null if there was an error. </returns>
        public override List <T> NodesAround <T>(MapNavNode node, int radius, bool includeCentralNode, ValidationCallback callback)
        {
            if (radius < 1)
            {
                radius = 1;
            }
            if (node == null)
            {
                return(null);
            }
            if (radius == 1)
            {
                return(NodesAround <T>(node, false, includeCentralNode, callback));
            }
            List <T> nodes = new List <T>(0);

            if (node.idx < 0 || node.idx >= grid.Length)
            {
                return(null);
            }

            if (includeCentralNode)
            {
                if (node.isValid)
                {
                    if (callback != null)
                    {
                        if (true == callback(node))
                        {
                            nodes.Add((T)node);
                        }
                    }
                    else
                    {
                        nodes.Add((T)node);
                    }
                }
            }

            for (int x = -radius; x <= radius; x++)
            {
                for (int y = -radius; y <= radius; y++)
                {
                    //if (Mathf.Abs(x + y) > radius) continue;
                    int q = node.q + x;
                    int r = node.r + y;
                    if (q < 0 || r < 0 || q >= mapHorizontalSize || r >= mapVerticalSize)
                    {
                        continue;
                    }
                    int id = (r * mapHorizontalSize + q);

                    if (id == node.idx)
                    {
                        continue;
                    }
                    if (id >= 0 && id < grid.Length)
                    {
                        if (grid[id].isValid)
                        {
                            if (callback != null)
                            {
                                if (false == callback(grid[id]))
                                {
                                    continue;
                                }
                            }

                            if (false == nodes.Contains((T)grid[id]))
                            {
                                nodes.Add((T)grid[id]);
                            }
                        }
                    }
                }
            }

            return(nodes);
        }
Beispiel #10
0
        /// <summary>
        /// Returns list of nodes starting at the "next" node and going anti-clockwise around the central node.
        /// </summary>
        /// <param name="node">				The central node around which to get neighboring nodes.</param>
        /// <param name="includeInvalid">	Should "invalid" nodes be included? If so then a NULL entry will be added to the
        ///									returned list for invalid nodes. An invalid node might be one that is stored in
        ///									the grid array but not considered to be in the grid. This normally happens with
        ///									Hexa grids. An invalid node might also be one marked as invalid by the callback function. </param>
        /// <param name="includeCentralNode">Should the central node be included? It will be added first in the list if so.</param>
        /// <param name="callback">			An optional callback that can first check if the node is "valid" and return True if
        ///									so, else it should return False. </param>
        /// <returns>Returns null if there was an error. </returns>
        public override List <T> NodesAround <T>(MapNavNode node, bool includeInvalid, bool includeCentralNode, ValidationCallback callback)
        {
            if (node == null)
            {
                return(null);
            }
            List <T> nodes = new List <T>(includeCentralNode ? 7 : 6);

            if (node.idx < 0 || node.idx >= grid.Length)
            {
                return(null);
            }

            if (includeCentralNode)
            {
                if (callback != null)
                {
                    if (false == node.isValid || false == callback(node))
                    {
                        if (includeInvalid)
                        {
                            nodes.Add(null);
                        }
                    }
                    else
                    {
                        nodes.Add((T)node);
                    }
                }
                else
                {
                    if (node.isValid)
                    {
                        nodes.Add((T)node);
                    }
                    else if (includeInvalid)
                    {
                        nodes.Add(null);
                    }
                }
            }

            //int[,] neighbours = diagonalNeighbours ? Neighbours8 : Neighbours4;
            int[,] neighbours = Neighbours8;                    // always use 8-neighbour for this so that a ring around can be selected.
            // the special select function witch takes "cost" into account will
            // take 4-neighbour type selection into account

            for (int dir = 0; dir < neighbours.GetLength(0); dir++)
            {
                int        q = grid[node.idx].q + neighbours[dir, 0];
                int        r = grid[node.idx].r + neighbours[dir, 1];
                MapNavNode n = NodeAt <MapNavNode>(q, r);

                if (n == null)
                {
                    if (includeInvalid)
                    {
                        nodes.Add(null);
                    }
                    continue;
                }

                if (false == n.isValid)
                {
                    if (includeInvalid)
                    {
                        nodes.Add(null);
                    }
                    continue;
                }

                if (callback != null)
                {
                    if (false == callback(n))
                    {
                        if (includeInvalid)
                        {
                            nodes.Add(null);
                        }
                        continue;
                    }
                }

                nodes.Add((T)n);
            }

            return(nodes);
        }