//TODO : Addapt to diffrent agent size and height
        private bool HesJumpOpportunity(NavGrid map, int x, int y, int maxJumpHeight)
        {
            //Check if agent is on the egde
            if (map.IsFreeNode(x - 1, y - 1) || map.IsFreeNode(x + 1, y - 1))
            {
                return(true);
            }


            for (int i = 0; i < maxJumpHeight; i++)
            {
                if (map.IsFreeNode(x, y + i) == false || map.IsFreeNode(x, y + i + 1) == false)
                {
                    break;
                }

                if (map.IsFreeNode(x - 1, y + i) == false && map.IsFreeNode(x - 1, y + i + 1))
                {
                    return(true);
                }

                if (map.IsFreeNode(x + 1, y + i) == false && map.IsFreeNode(x + 1, y + i + 1))
                {
                    return(true);
                }
            }

            return(false);
        }
        public void SetSerachTiles(Dictionary <Vector3Int, Node> cameFrom, NavGrid navGrid)
        {
#if UNITY_EDITOR
            serachPoints.Clear();
            if (cameFrom != null)
            {
                serachPoints.AddRange(cameFrom.Values.ToList().ConvertAll(node => node == null ? default : navGrid.IndexToPosition(node.position)));
                cellSize = navGrid.CellSize;
            }
#endif
        }
        public bool TryGetPath(Vector2 startPosition, Vector2 goalPosition, NavGrid navGrid, NavGridAgentModel agentModel, out List <Vector2> path)
        {
            if (navGrid == null || navGrid.IsWithinNavGridBounds(goalPosition) == false)
            {
                path = null;
                return(false);
            }

            goalFound = false;

            var startIndexVector = navGrid.PositionToIndex(startPosition);
            var goalIndexVector  = navGrid.PositionToIndex(goalPosition);

            if (navGrid.IsNodeOfType(goalIndexVector.x, goalIndexVector.y, NavGrid.NodeType.wall))
            {
                path = null;
                return(false);
            }

            var predictSize = (int)Heuristics.GetManhattan(startPosition, goalPosition) * 2;

            predictSize = Mathf.Max(MINIMAL_DICTIONARY_SIZE, predictSize);

            var cameFrom     = new Dictionary <Vector3Int, Node>(predictSize); //Dictionary use to recreate path
            var jumpNodeData = new Dictionary <Vector2Int, JumpData>(predictSize);
            var open         = new SimplePriorityQueue <Node, float>();

            Node current = null;
            Node start   = new Node(startIndexVector);

            open.Enqueue(start, 0);
            cameFrom.Add(new Vector3Int(start.x, start.y, 0), null);

            while (open.Count != 0)
            {
                current = open.Dequeue();

                //early exit if goal found
                if (current.position == goalIndexVector)
                {
                    goalFound = true;
                    break;
                }

                bool jumpOpportunity = false;

                if (current.jumpCost == 0)
                {
                    var distanceToGoal = Heuristics.GetManhattan(current.position, goalIndexVector);
                    jumpOpportunity = current.y < goalIndexVector.y && distanceToGoal <= agentModel.maxJumpHeight * 2 ||
                                      HesJumpOpportunity(navGrid, current.x, current.y, agentModel.maxJumpHeight);
                }

                foreach (var neightbor in navGrid.GetNeightbors(current.position))
                {
                    var atCeling = false;
                    var grounded = false;

                    //continue if neightbor is the start node
                    if (neightbor == start.position)
                    {
                        continue;
                    }

                    if (navGrid.IsGroundedNode(neightbor.x, neightbor.y))
                    {
                        grounded = true;
                    }
                    else if (navGrid.IsCelingNode(neightbor.x, neightbor.y))
                    {
                        atCeling = true;
                    }

                    var jumpCost = GetJumpCost(current, neightbor, atCeling, grounded, agentModel);

                    if (grounded == false)
                    {
                        //Prevent starting the jump if there is non location to jump to
                        if (current.jumpCost == 0 && jumpCost == 3 && jumpOpportunity == false)
                        {
                            continue;
                        }
                        //Allow horizontal movement only from even nodes
                        if (current.jumpCost >= 0 && current.jumpCost % 2 != 0 && current.x != neightbor.x)
                        {
                            continue;
                        }
                        //Prevent from moving up further than the agent max jump height allow
                        if (current.jumpCost >= agentModel.maxJumpHeight * 2 && current.y < neightbor.y)
                        {
                            continue;
                        }
                        //After first 3 cells horizontal movement is allowed every 4-th cell (on cells with jumpvalue of 13,21,29...)
                        if (jumpCost >= agentModel.maxJumpHeight * 2 + 6 && neightbor.x != current.x &&
                            (jumpCost - (agentModel.maxJumpHeight * 2 + 6)) % 8 != 3)
                        {
                            continue;
                        }
                    }


                    Vector3Int location = new Vector3Int(neightbor.x, neightbor.y, jumpCost);

                    //-1f means node was not visited
                    float oldCost = cameFrom.TryGetValue(location, out Node oldNode) ? oldNode.cost : -1f;
                    var   cost    = current.cost + 1 + jumpCost * 0.001f;

                    if (oldCost == -1f || oldCost > cost)
                    {
                        if (jumpCost != 0)
                        {
                            var hesNodeJumpData = jumpNodeData.TryGetValue(neightbor, out JumpData jumpData);

                            if (hesNodeJumpData && jumpData.lowerJumpValue <= jumpCost)
                            {
                                if (jumpCost % 2 != 0 || jumpData.horizontalMovement || jumpCost >= agentModel.maxJumpHeight * 2 + 6)
                                {
                                    continue;
                                }
                            }

                            jumpData.lowerJumpValue      = jumpCost;
                            jumpData.horizontalMovement |= jumpCost % 2 == 0;

                            if (hesNodeJumpData)
                            {
                                jumpNodeData[neightbor] = jumpData;
                            }
                            else
                            {
                                jumpNodeData.Add(neightbor, jumpData);
                            }
                        }

                        if (oldCost == -1)
                        {
                            cameFrom.Add(location, current);
                        }
                        else
                        {
                            cameFrom[location] = current;
                        }

                        Node newNode = new Node(neightbor)
                        {
                            cost     = cost,
                            distance = Heuristics.GetManhattan(current.position, goalIndexVector),
                            jumpCost = jumpCost
                        };

                        open.Enqueue(newNode, newNode.Priority);
                    }
                }
            }

            //DEBUG-------------------------------
            debugger.SetSerachTiles(cameFrom, navGrid);
            //------------------------------------

            path = goalFound ? pathReconstractor.RecreatePath(cameFrom, start, current, navGrid) : null;
            return(goalFound);
        }
Пример #4
0
        /// <summary>
        /// Recostract path base on direction changing
        /// </summary>
        public List <Vector2> RecreatePath(Dictionary <Vector3Int, Node> cameFrom, Node start, Node goal, NavGrid navGrid)
        {
            var     path             = new List <Vector2>();
            var     current          = goal;
            Vector2 currentDirection = Vector2.zero;

            path.Add(navGrid.IndexToPosition(current.position));
            while (current.position != start.position)
            {
                var currentPosition = navGrid.IndexToPosition(current.position);
                if (cameFrom.TryGetValue(new Vector3Int(current.x, current.y, current.jumpCost), out current))
                {
                    var nodeCenterPosition = navGrid.IndexToPosition(current.position);
                    var newDirection       = nodeCenterPosition - currentPosition;
                    if (currentDirection != newDirection)
                    {
                        path.Add(nodeCenterPosition);
                        currentDirection = newDirection;
                    }
                    else
                    {
                        path[path.Count - 1] = nodeCenterPosition;
                    }
                }
                else
                {
                    Debug.LogError("Data for path reconstration was incorrect");
                    return(null);
                }
            }

            path.Reverse();

            return(path);
        }