/// <summary> /// Функция поиска ближайшей к финишу точки /// </summary> /// <param name="position">координаты финиша</param> /// <param name="outpos">выходные координаты</param> /// <param name="_maxHeight">максимальная высота</param> /// <returns>true - найдено, false - не найдено</returns> public static bool FindFreeCell(Vector2Int position, out Vector2Int outpos, float _maxHeight = 0) { PriorityQueue <NavGridPoint> queue = new PriorityQueue <NavGridPoint>(); NavGridPoint temp = new NavGridPoint(position, 0); queue.Add(temp); while (queue.Peek().order < GameConstants.FreeCellMaxOrder) { temp = queue.GetMin(); if (!TerrainNavGrid.Instance.IsCellUsed(temp.Position)) { outpos = temp.Position; return(true); } foreach (Vector2Int v in MoveArray) { NavGridPoint add = new NavGridPoint(temp.Position + v, temp.order + 1); if (!IsPointInField(add.Position) || (_maxHeight != 0 && TerrainHeightMap.Instance.GetHeight(temp.Position) - TerrainHeightMap.Instance.GetHeight(add.Position) >= _maxHeight)) { continue; } add.distance = GetDistance(position, add.Position); queue.Add(add); } } outpos = position; return(false); }
/// <summary> /// Функция поиска пути /// </summary> public void FindPath() { startTime = DateTime.UtcNow; // получение времени начала работы FinishNotFoundReasons notFoundReason = FinishNotFoundReasons.None; bool[,] scannedCells = new bool[GameParams.Width, GameParams.Length]; // просмотренные ячейки PriorityQueue <NavGridPoint> queue = new PriorityQueue <NavGridPoint>(); // очередь с приоритетом из навигационных точек NavGridPoint point = new NavGridPoint(start, 0), movePoint; point.distance = GetDistance(start, finish); // получение дистанции между стартом и финишом queue.Add(point); scannedCells[start.x, start.y] = true; // пока очередь не пустая, поиск пути актуален и поток не должен быть завершён while (queue.Count != 0 && IsActual && (FindThread == null || (FindThread.ThreadState & ThreadState.AbortRequested) == 0)) { // если финиш занят, выходим из цикла if (TerrainNavGrid.Instance.IsCellUsed(finish)) { notFoundReason = FinishNotFoundReasons.FinishUsed; break; } point = queue.GetMin(); // если текущая позиция равна финишу, то выходим из цикла if (point.Position == finish) { IsPathFound = true; break; } if (IsPositionBlocked(point.Position)) { notFoundReason = FinishNotFoundReasons.Blocked; break; } // находим соседние клетки, ближайшие к финишу foreach (Vector2Int move in MoveArray) { movePoint = new NavGridPoint(point.Position + move, point.order + 1); if (!IsPointInField(movePoint.Position) || TerrainNavGrid.Instance.IsCellUsed(movePoint.Position) || TerrainHeightMap.Instance.GetHeight(movePoint.Position) > TerrainHeightMap.Instance.GetHeight(point.Position) + maxHeight) { continue; } // пропускает поинт, если он вне карты, занят или выше текущей позиции //float heightDist = TerrainHeightMap.Instance.GetHeight(movePoint.Position) - TerrainHeightMap.Instance.GetHeight(point.Position); //if (heightDist < 0) heightDist = 0; movePoint.oldPoint = point; movePoint.distance = GetDistance(movePoint.Position, finish) /* + heightDist*/; if (scannedCells[movePoint.Position.x, movePoint.Position.y]) // если позиция просмотрена { if (queue.Contains(movePoint)) // если в очереди уже есть такая позиция, то берётся ближайшая к финишу { NavGridPoint remove = queue.Find(movePoint); if (remove.Weight < movePoint.Weight) { continue; } queue.Remove(remove); queue.Add(movePoint); } continue; } scannedCells[movePoint.Position.x, movePoint.Position.y] = true; queue.Add(movePoint); // добавление поинта в очередь с приоритетом } } IsComplete = true; if (IsPathFound) // если путь найден, получаем список координат { while (point.oldPoint != null) { path.Add(point.Position); point = point.oldPoint; } if (onPathfound == null) { return; } onPathfound.BeginInvoke(path, null, null); } else { // иначе сообщаем причину if (notFoundReason == FinishNotFoundReasons.None) { notFoundReason = FinishNotFoundReasons.PathNotFound; } if (onPathNotFound == null) { return; } onPathNotFound.BeginInvoke(notFoundReason, null, null); } }