private void CalculatePath() { path = new ArrayList(); navmesh = GameObject.Find ("NavmeshGenerator").GetComponent<NavmeshSpawner> ().GetNavmesh (); // cast rays up, down, left, right and if its longer than it should be, ignore the impact float expectedDist = navmesh.gameUnitStep + fudge; // Start the calculation by finding the closest node to the player (or GameObject to which we are attached) GameObject startPoint = FindClosestNavmeshPointTo(sourceClick); startPoint.GetComponent<SpriteRenderer> ().enabled = true; startPoint.GetComponent<SpriteRenderer> ().color = Color.red; //Debug.Log ("Starting from: (" + startPoint.transform.position.x + ", " + startPoint.transform.position.y + ")"); _SearchElement startElement = new _SearchElement(startPoint, 0.0f); // Then find the closest GameObject to the target endPoint = FindClosestNavmeshPointTo(destClick); endPoint.GetComponent<SpriteRenderer> ().enabled = true; endPoint.GetComponent<SpriteRenderer> ().color = Color.red; //Debug.Log ("Ending at: (" + endPoint.transform.position.x + ", " + endPoint.transform.position.y + ")"); // Keep a priority queue of points on the frontier, sorted in increasing order by F = G + H // The CompareTo() function of each _SearchElement takes into account the H value SortedList openSet = new SortedList(); // Keep a list of NavmeshPoints we have already found in the SPT ArrayList closedSet = new ArrayList(); openSet.Add(startElement, null); _SearchElement finalSearchElement = null; while (openSet.Count > 0) { // Dequeue the element in the openSet with the smallest distance _SearchElement current = openSet.GetKey(0) as _SearchElement; // Is this what we are looking for? if (current.point == endPoint) { closedSet.Add(current.point); finalSearchElement = current; break; } // Remove this NavmeshPoint from the openSet and add it to the closedSet openSet.Remove(current); closedSet.Add(current.point); current.point.layer = 9; //Debug.Log ("Processing point at (" + current.point.transform.position.x // + ", " + current.point.transform.position.y + ")"); // Get all NavmeshPoints adjacent to this point in the form of _SearchElements whose dists are current.dist // plus however long the edge from current to the adjacent point is (measured in terms of game space dist) ArrayList adj = GetAdjacentPoints(current, expectedDist); // Find out if any points adjacent to current are already in the openSet // If they are, find out if the distance through the current path is shorter than the distance // they are currently at through other paths. If the distance through current is shorter, update the dist // to be the dist through current, and update the from field to be current. // Note: We do not explicitly handle the heuristic estimate at this time, as it is taken care of for us // behind the scenes in the openSet.Add() function by the IComparable interface implemented by _SearchElement foreach (_SearchElement newFrontierElement in adj) { bool elementInOpenSet = false; bool replaceExistingElement = false; _SearchElement existingElementIndex = null; foreach (_SearchElement establishedFrontierElement in openSet.Keys) { if (newFrontierElement.point == establishedFrontierElement.point) { // This NavmeshPoint exists in the openSet elementInOpenSet = true; if (newFrontierElement.dist < establishedFrontierElement.dist) { // The new path is a better path than the current path replaceExistingElement = true; existingElementIndex = establishedFrontierElement; } // Break out of the openSet for-loop; we are done here since we found a match break; } } if (!elementInOpenSet) { openSet.Add(newFrontierElement, null); } else if (elementInOpenSet && replaceExistingElement) { openSet.Remove(existingElementIndex); openSet.Add(newFrontierElement, null); } } } // We either ran out of elements in the navmesh and should throw an error, or we arrived at the target if (finalSearchElement == null) { throw new Exception("Target element not found by A* algorithm"); } else { // We shouldn't show the close navpoints any longer //this.gameObject.GetComponent<ShowNearbyNavpoints>().SendMessage("Cleanup"); // Reconstruct the path that won path = new ArrayList(); pathDist = 0.0f; _SearchElement pathPoint = finalSearchElement; while (pathPoint != null) { path.Add(pathPoint.point); pathDist += pathPoint.dist; pathPoint = (_SearchElement)pathPoint.from; } // Finally, reverse the path, since we added elements to it in reverse order (i.e. starting from target) path.Reverse(); foreach (GameObject navmeshPoint in path) { SpriteRenderer sr = navmeshPoint.GetComponent<SpriteRenderer>(); sr.enabled = true; sr.color = Color.red; } //this.gameObject.GetComponent<FollowPath>().StartFollowing(path); //Debug.Log ("Final path distance: " + pathDist); } }
public _SearchElement(GameObject g, float d) { point = g; dist = d; from = null; }
public _SearchElement(GameObject g, float d, _SearchElement f) { point = g; dist = d; from = f; }
// Returns an ArrayList of up to four adjacent _SearchElements in the up, down, left, right directions // relative to element, with their relevant fields filled out private ArrayList GetAdjacentPoints(_SearchElement element, float expectedDist) { // Set the NavmeshPoint pointed to by element.point to be invisible to raycasting so that we can raycast // from inside it int oldLayer = element.point.layer; element.point.layer = 8; // Cast rays up, down, left, right // We don't need to cast diagonally because we will have path smoothing kicking in for that later // If they intersect with a GameObject with tag NavmeshObject in <= expectedDist, add that point to AdjPoints ArrayList adj = new ArrayList(); RaycastHit2D raycastHitUp = Physics2D.Raycast(element.point.transform.position, Vector2.up, expectedDist, layerMask); RaycastHit2D raycastHitDown = Physics2D.Raycast(element.point.transform.position, -Vector2.up, expectedDist, layerMask); RaycastHit2D raycastHitLeft = Physics2D.Raycast(element.point.transform.position, -Vector2.right, expectedDist, layerMask); RaycastHit2D raycastHitRight = Physics2D.Raycast(element.point.transform.position, Vector2.right, expectedDist, layerMask); RaycastHit2D raycastHitUpLeft = Physics2D.Raycast(element.point.transform.position, (Vector2.up - Vector2.right), expectedDist*sqrt_2, layerMask); RaycastHit2D raycastHitUpRight = Physics2D.Raycast(element.point.transform.position, (Vector2.up + Vector2.right), expectedDist*sqrt_2, layerMask); RaycastHit2D raycastHitDownLeft = Physics2D.Raycast(element.point.transform.position, (-Vector2.up - Vector2.right), expectedDist*sqrt_2, layerMask); RaycastHit2D raycastHitDownRight = Physics2D.Raycast(element.point.transform.position, (-Vector2.up + Vector2.right), expectedDist*sqrt_2, layerMask); // If the raycast hits something, and it's a NavmeshPoint, add it to adj with dist = G (we account for H later, // when adding to the priority queue) and with its from field pointing to element //Debug.Log ("Rays are cast"); if (raycastHitUp.collider != null) { if (raycastHitUp.collider.gameObject.CompareTag("NavmeshObject")) { adj.Add(new _SearchElement(raycastHitUp.collider.gameObject, element.dist + Vector2.Distance(raycastHitUp.point, element.point.transform.position), element)); //Debug.Log ("Adding frontier element at (" // + raycastHitUp.collider.gameObject.transform.position.x // + ", " // + raycastHitUp.collider.gameObject.transform.position.y // + ")"); } } if (raycastHitDown.collider != null) { if (raycastHitDown.collider.gameObject.CompareTag("NavmeshObject")) { adj.Add(new _SearchElement(raycastHitDown.collider.gameObject, element.dist + Vector2.Distance(raycastHitDown.point, element.point.transform.position), element)); //Debug.Log ("Adding frontier element at (" // + raycastHitDown.collider.gameObject.transform.position.x // + ", " // + raycastHitDown.collider.gameObject.transform.position.y // + ")"); } } if (raycastHitLeft.collider != null) { if (raycastHitLeft.collider.gameObject.CompareTag("NavmeshObject")) { adj.Add(new _SearchElement(raycastHitLeft.collider.gameObject, element.dist + Vector2.Distance(raycastHitLeft.point, element.point.transform.position), element)); //Debug.Log ("Adding frontier element at (" // + raycastHitLeft.collider.gameObject.transform.position.x // + ", " // + raycastHitLeft.collider.gameObject.transform.position.y // + ")"); } } if (raycastHitRight.collider != null) { if (raycastHitRight.collider.gameObject.CompareTag("NavmeshObject")) { adj.Add(new _SearchElement(raycastHitRight.collider.gameObject, element.dist + Vector2.Distance(raycastHitRight.point, element.point.transform.position), element)); //Debug.Log ("Adding frontier element at (" // + raycastHitRight.collider.gameObject.transform.position.x // + ", " // + raycastHitRight.collider.gameObject.transform.position.y // + ")"); } } if (raycastHitUpLeft.collider != null) { if (raycastHitUpLeft.collider.gameObject.CompareTag("NavmeshObject")) { adj.Add(new _SearchElement(raycastHitUpLeft.collider.gameObject, element.dist + Vector2.Distance(raycastHitUpLeft.point, element.point.transform.position), element)); //Debug.Log ("Adding frontier element at (" // + raycastHitUpLeft.collider.gameObject.transform.position.x // + ", " // + raycastHitUpLeft.collider.gameObject.transform.position.y // + ")"); } } if (raycastHitUpRight.collider != null) { if (raycastHitUpRight.collider.gameObject.CompareTag("NavmeshObject")) { adj.Add(new _SearchElement(raycastHitUpRight.collider.gameObject, element.dist + Vector2.Distance(raycastHitUpRight.point, element.point.transform.position), element)); //Debug.Log ("Adding frontier element at (" // + raycastHitUpRight.collider.gameObject.transform.position.x // + ", " // + raycastHitUpRight.collider.gameObject.transform.position.y // + ")"); } } if (raycastHitDownLeft.collider != null) { if (raycastHitDownLeft.collider.gameObject.CompareTag("NavmeshObject")) { adj.Add(new _SearchElement(raycastHitDownLeft.collider.gameObject, element.dist + Vector2.Distance(raycastHitDownLeft.point, element.point.transform.position), element)); //Debug.Log ("Adding frontier element at (" // + raycastHitDownLeft.collider.gameObject.transform.position.x // + ", " // + raycastHitDownLeft.collider.gameObject.transform.position.y // + ")"); } } if (raycastHitDownRight.collider != null) { if (raycastHitDownRight.collider.gameObject.CompareTag("NavmeshObject")) { adj.Add(new _SearchElement(raycastHitDownRight.collider.gameObject, element.dist + Vector2.Distance(raycastHitDownRight.point, element.point.transform.position), element)); //Debug.Log ("Adding frontier element at (" // + raycastHitDownRight.collider.gameObject.transform.position.x // + ", " // + raycastHitDownRight.collider.gameObject.transform.position.y // + ")"); } } // Reset old element.point.layer element.point.layer = oldLayer; return adj; }