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); }
private int GetJumpCost(Node current, Vector2Int neightbor, bool atCeling, bool grounded, NavGridAgentModel agentModel) { var output = 0; if (atCeling) { if (current.x != neightbor.x) { output = Mathf.Max(agentModel.maxJumpHeight * 2 + 1, current.jumpCost + 1); } else { output = Mathf.Max(agentModel.maxJumpHeight * 2, current.jumpCost + 2); } } else if (grounded) { output = 0; } else if (current.y < neightbor.y) // jumping { if (current.jumpCost < 2) { output = 3; } else { output = current.jumpCost + (current.jumpCost % 2 == 0 ? 2 : 1); } } else if (current.y > neightbor.y) // falling { output = Mathf.Max(agentModel.maxJumpHeight * 2, current.jumpCost + (current.jumpCost % 2 == 0 ? 2 : 1)); } else if (grounded == false && current.x != neightbor.x) // walling off the egde { output = current.jumpCost + 1; } return(output); }