/// <summary> /// Returns an optimal route from startPosition to endPosition with options. /// </summary> /// <returns>The route.</returns> /// <param name="terrainCapability">Type of terrain that the unit can pass through</param> /// <param name="minAltitude">Minimum altitude (0..1)</param> /// <param name="maxAltitude">Maximum altutude (0..1)</param> /// <param name="maxSearchCost">Maximum search cost for the path finding algorithm. A value of -1 will use the global default defined by pathFindingMaxCost</param> public List <Vector2> FindRoute(City startCity, City endCity, TERRAIN_CAPABILITY terrainCapability = TERRAIN_CAPABILITY.Any, float minAltitude = 0, float maxAltitude = 1f, int maxSearchCost = -1, int maxSearchSteps = -1) { if (startCity == null || endCity == null) { return(null); } return(FindRoute(startCity.unity2DLocation, endCity.unity2DLocation, terrainCapability, minAltitude, maxAltitude, maxSearchCost, maxSearchSteps)); }
/// <summary> /// Get a list of cells which are nearer than a given distance in cell count with optional parameters /// </summary> public List <int> GetCellNeighbours(int cellIndex, int distance, float maxSearchCost = 0, TERRAIN_CAPABILITY terrainCapability = TERRAIN_CAPABILITY.Any) { if (cellIndex < 0 || cellIndex >= cells.Length) { return(null); } distance++; Cell cell = cells[cellIndex]; List <int> cc = new List <int>(); for (int x = cell.column - distance; x <= cell.column + distance; x++) { if (x < 0 || x >= _gridColumns) { continue; } for (int y = cell.row - distance; y <= cell.row + distance; y++) { if (y < 0 || y >= _gridRows) { continue; } if (x == cell.column && y == cell.row) { continue; } Cell otherCell = GetCell(y, x); if (otherCell == null) { continue; } List <int> steps = FindRoute(cell, otherCell, terrainCapability, maxSearchCost); if (steps != null && steps.Count <= distance) { cc.Add(GetCellIndex(otherCell)); } } } return(cc); }
/// <summary> /// Returns an optimal route from starting Cell to destination Cell. The capability to move from one province to another is determined by the neighbours array of the Cell object. /// </summary> /// <returns>The result is a list of province indices.</returns> /// <param name="startingCell">The index for the starting province</param> /// <param name="destinationCell">The index for the destination province</param> /// <param name="terrainCapability">The allowed terrains for the route. Any/Ground/Water</param> /// <param name="maxSearchCost">Maximum search cost for the path finding algorithm. A value of -1 will use the global default defined by pathFindingMaxCost</param> /// <param name="minAltitude">Minimum terrain altitude allowed</param> /// <param name="minAltitude">Maximum terrain altitude allowed</param> public List <int> FindRoute(Cell startingCell, Cell destinationCell, out int totalCost, TERRAIN_CAPABILITY terrainCapability, int maxSearchCost = -1, float minAltitude = 0f, float maxAltitude = 1f, int maxSearchSteps = -1) { ComputeCellsCostsInfo(); totalCost = 0; int startingCellIndex = GetCellIndex(startingCell); int destinationCellIndex = GetCellIndex(destinationCell); if (destinationCellIndex == startingCellIndex || destinationCellIndex < 0 || startingCellIndex < 0) { return(null); } List <int> routePoints = null; if (finderCells == null) { return(null); } finderCells.Formula = _pathFindingHeuristicFormula; finderCells.MaxSearchCost = maxSearchCost < 0 ? _pathFindingMaxCost : maxSearchCost; finderCells.MaxSteps = maxSearchSteps < 0 ? _pathFindingMaxSteps : maxSearchSteps; finderCells.TerrainCapability = terrainCapability; finderCells.MinAltitude = minAltitude; finderCells.MaxAltitude = maxAltitude; if (OnPathFindingCrossCell != null) { finderCells.OnCellCross = FindRouteCellValidator; } else { finderCells.OnCellCross = null; } Point startPoint = new Point(startingCell.column, startingCell.row); Point endPoint = new Point(destinationCell.column, destinationCell.row); List <PathFinderNode> route = finderCells.FindPath(startPoint, endPoint, out totalCost); if (route != null) { int routeCount = route.Count; routePoints = new List <int> (routeCount); for (int r = routeCount - 1; r >= 0; r--) { routePoints.Add(route [r].Y * _gridColumns + route [r].X); } } else { return(null); // no route available } // Add final step if it's appropiate return(routePoints); }
/// <summary> /// Returns an optimal route from starting Cell to destination Cell. The capability to move from one province to another is determined by the neighbours array of the Cell object. /// </summary> /// <returns>The result is a list of province indices.</returns> /// <param name="startingCell">The index for the starting province</param> /// <param name="destinationCell">The index for the destination province</param> /// <param name="terrainCapability">The allowed terrains for the route. Any/Ground/Water</param> /// <param name="maxSearchCost">Maximum search cost for the path finding algorithm. A value of -1 will use the global default defined by pathFindingMaxCost</param> /// <param name="maxSearchSteps">Maximum number of steps for the path finding algorithm. A value of -1 will use the global default defined by pathFindingMaxSteps</param> public List <int> FindRoute(Cell startingCell, Cell destinationCell, TERRAIN_CAPABILITY terrainCapability = TERRAIN_CAPABILITY.Any, int maxSearchCost = -1, int maxSearchSteps = -1) { int dummy; return(FindRoute(startingCell, destinationCell, out dummy, terrainCapability, maxSearchCost, maxSearchSteps)); }
/// <summary> /// Returns an optimal route from startPosition to endPosition with options. /// </summary> /// <returns>The route.</returns> /// <param name="startPosition">Start position in map coordinates (-0.5...0.5)</param> /// <param name="endPosition">End position in map coordinates (-0.5...0.5)</param> /// <param name="totalCost">The total cost of traversing the path</param> /// <param name="terrainCapability">Type of terrain that the unit can pass through</param> /// <param name="minAltitude">Minimum altitude (0..1)</param> /// <param name="maxAltitude">Maximum altutude (0..1)</param> /// <param name="maxSearchCost">Maximum search cost for the path finding algorithm. A value of -1 will use the global default defined by pathFindingMaxCost</param> public List <Vector2> FindRoute(Vector2 startPosition, Vector2 endPosition, out int totalCost, TERRAIN_CAPABILITY terrainCapability = TERRAIN_CAPABILITY.Any, float minAltitude = 0, float maxAltitude = 1f, int maxSearchCost = -1, int maxSearchSteps = -1) { ComputeRouteMatrix(terrainCapability, minAltitude, maxAltitude); totalCost = 0; Point startingPoint = new Point((int)((startPosition.x + 0.5f) * EARTH_ROUTE_SPACE_WIDTH), (int)((startPosition.y + 0.5f) * EARTH_ROUTE_SPACE_HEIGHT)); Point endingPoint = new Point((int)((endPosition.x + 0.5f + 0.5f / EARTH_ROUTE_SPACE_WIDTH) * EARTH_ROUTE_SPACE_WIDTH), (int)((endPosition.y + 0.5f + 0.5f / EARTH_ROUTE_SPACE_HEIGHT) * EARTH_ROUTE_SPACE_HEIGHT)); endingPoint.X = Mathf.Clamp(endingPoint.X, 0, EARTH_ROUTE_SPACE_WIDTH - 1); endingPoint.Y = Mathf.Clamp(endingPoint.Y, 0, EARTH_ROUTE_SPACE_HEIGHT - 1); // Helper to find a minimum path in case the destination position is on a different terrain type if (terrainCapability == TERRAIN_CAPABILITY.OnlyWater) { int arrayIndex = endingPoint.Y * EARTH_ROUTE_SPACE_WIDTH + endingPoint.X; if ((earthRouteMatrix [arrayIndex] & 4) == 0) { int regionIndex = -1; int countryIndex = GetCountryIndex(endPosition, out regionIndex); if (countryIndex >= 0 && regionIndex >= 0) { List <Vector2> coastPositions = GetCountryCoastalPoints(countryIndex, regionIndex, 0.001f); float minDist = float.MaxValue; Vector2 bestPosition = Misc.Vector2zero; int coastPositionsCount = coastPositions.Count; // Get nearest position to the ship which is on water for (int k = 0; k < coastPositionsCount; k++) { Vector2 waterPosition; if (ContainsWater(coastPositions [k], 0.001f, out waterPosition)) { float dist = FastVector.SqrDistance(ref endPosition, ref waterPosition); // (endPosition - waterPosition).sqrMagnitude; if (dist < minDist) { minDist = dist; bestPosition = waterPosition; } } } if (minDist < float.MaxValue) { endPosition = bestPosition; } endingPoint = new Point((int)((endPosition.x + 0.5f + 0.5f / EARTH_ROUTE_SPACE_WIDTH) * EARTH_ROUTE_SPACE_WIDTH), (int)((endPosition.y + 0.5f + 0.5f / EARTH_ROUTE_SPACE_HEIGHT) * EARTH_ROUTE_SPACE_HEIGHT)); arrayIndex = endingPoint.Y * EARTH_ROUTE_SPACE_WIDTH + endingPoint.X; if ((earthRouteMatrix [arrayIndex] & 4) == 0) { Vector2 direction = Misc.Vector2zero; for (int k = 1; k <= 10; k++) { if (k == 10 || startPosition == endPosition) { return(null); } FastVector.NormalizedDirection(ref endPosition, ref startPosition, ref direction); Vector2 p = endPosition + direction * (float)k / EARTH_ROUTE_SPACE_WIDTH; endingPoint = new Point((int)((p.x + 0.5f + 0.5f / EARTH_ROUTE_SPACE_WIDTH) * EARTH_ROUTE_SPACE_WIDTH), (int)((p.y + 0.5f + 0.5f / EARTH_ROUTE_SPACE_HEIGHT) * EARTH_ROUTE_SPACE_HEIGHT)); arrayIndex = endingPoint.Y * EARTH_ROUTE_SPACE_WIDTH + endingPoint.X; if ((earthRouteMatrix [arrayIndex] & 4) > 0) { break; } } } } } } List <Vector2> routePoints = null; // Minimum distance for routing? if (Mathf.Abs(endingPoint.X - startingPoint.X) > 0 || Mathf.Abs(endingPoint.Y - startingPoint.Y) > 0) { finder.Formula = _pathFindingHeuristicFormula; finder.MaxSearchCost = maxSearchCost < 0 ? _pathFindingMaxCost : maxSearchCost; finder.MaxSteps = maxSearchSteps < 0 ? _pathFindingMaxSteps : maxSearchSteps; if (_pathFindingEnableCustomRouteMatrix) { finder.OnCellCross = FindRoutePositionValidator; } else { finder.OnCellCross = null; } List <PathFinderNode> route = finder.FindPath(startingPoint, endingPoint, out totalCost); if (route != null) { routePoints = new List <Vector2> (route.Count); routePoints.Add(startPosition); for (int r = route.Count - 1; r >= 0; r--) { float x = (float)route [r].X / EARTH_ROUTE_SPACE_WIDTH - 0.5f; float y = (float)route [r].Y / EARTH_ROUTE_SPACE_HEIGHT - 0.5f; Vector2 stepPos = new Vector2(x, y); // due to grid effect the first step may be farther than the current position, so we skip it in that case. if (r == route.Count - 1 && (endPosition - startPosition).sqrMagnitude < (endPosition - stepPos).sqrMagnitude) { continue; } routePoints.Add(stepPos); } } else { return(null); // no route available } } // Add final step if it's appropiate bool hasWater = ContainsWater(endPosition); if (terrainCapability == TERRAIN_CAPABILITY.Any || (terrainCapability == TERRAIN_CAPABILITY.OnlyWater && hasWater) || (terrainCapability == TERRAIN_CAPABILITY.OnlyGround && !hasWater)) { if (routePoints == null) { routePoints = new List <Vector2> (); routePoints.Add(startPosition); routePoints.Add(endPosition); } else { routePoints [routePoints.Count - 1] = endPosition; } } // Check that ground units ends in a position where GetCountryIndex returns a valid index if (terrainCapability == TERRAIN_CAPABILITY.OnlyGround) { int rr = routePoints.Count - 1; Vector2 dd = routePoints [rr - 1] - routePoints [rr]; dd *= 0.1f; while (GetCountryIndex(routePoints [rr]) < 0) { routePoints [rr] += dd; } } return(routePoints); }
/// <summary> /// Returns an optimal route from startPosition to endPosition with options. /// </summary> /// <returns>The route.</returns> /// <param name="startPosition">Start position in map coordinates (-0.5...0.5)</param> /// <param name="endPosition">End position in map coordinates (-0.5...0.5)</param> /// <param name="terrainCapability">Type of terrain that the unit can pass through</param> /// <param name="minAltitude">Minimum altitude (0..1)</param> /// <param name="maxAltitude">Maximum altutude (0..1)</param> /// <param name="maxSearchCost">Maximum search cost for the path finding algorithm. A value of -1 will use the global default defined by pathFindingMaxCost</param> public List <Vector2> FindRoute(Vector2 startPosition, Vector2 endPosition, TERRAIN_CAPABILITY terrainCapability = TERRAIN_CAPABILITY.Any, float minAltitude = 0, float maxAltitude = 1f, int maxSearchCost = -1, int maxSearchSteps = -1) { int dummy; return(FindRoute(startPosition, endPosition, out dummy, terrainCapability, minAltitude, maxAltitude, maxSearchCost, maxSearchSteps)); }
/// <summary> /// Returns an optimal route from startPosition to endPosition with options. /// </summary> /// <returns>The route.</returns> /// <param name="terrainCapability">Type of terrain that the unit can pass through</param> /// <param name="minAltitude">Minimum altitude (0..1)</param> /// <param name="maxAltitude">Maximum altutude (0..1)</param> /// <param name="maxSearchCost">Maximum search cost for the path finding algorithm. A value of -1 will use the global default defined by pathFindingMaxCost</param> public List <Vector2> FindRoute(string startCityName, string startCountryName, string endCityName, string endCountryName, TERRAIN_CAPABILITY terrainCapability = TERRAIN_CAPABILITY.Any, float minAltitude = 0, float maxAltitude = 1f, int maxSearchCost = -1, int maxSearchSteps = -1) { City city1 = GetCity(startCityName, startCountryName); City city2 = GetCity(endCityName, endCountryName); if (city1 == null || city2 == null) { return(null); } return(FindRoute(city1, city2, terrainCapability, minAltitude, maxAltitude, maxSearchCost, maxSearchSteps)); }
void ComputeRouteMatrix(TERRAIN_CAPABILITY terrainCapability, float minAltitude, float maxAltitude) { bool computeMatrix = false; byte thisMatrix = 1; // prepare matrix if (earthRouteMatrix == null) { earthRouteMatrix = new byte[EARTH_ROUTE_SPACE_WIDTH * EARTH_ROUTE_SPACE_HEIGHT]; computedMatrixBits = 0; } // prepare water mask data bool checkWater = terrainCapability != TERRAIN_CAPABILITY.Any; if (checkWater) { computeMatrix = CheckRouteWaterMask(); } // check elevation data if needed bool checkElevation = minAltitude > 0f || maxAltitude < 1.0f; if (checkElevation) { if (viewportElevationPoints == null) { Debug.LogError("Viewport needs to be initialized before calling using Path Finding functions."); return; } if (minAltitude != earthRouteMatrixWithElevationMinAltitude || maxAltitude != earthRouteMatrixWithElevationMaxAltitude) { computeMatrix = true; earthRouteMatrixWithElevationMinAltitude = minAltitude; earthRouteMatrixWithElevationMaxAltitude = maxAltitude; } } else { if (terrainCapability == TERRAIN_CAPABILITY.OnlyGround) { thisMatrix = 2; } else { thisMatrix = 4; // water } if ((computedMatrixBits & thisMatrix) == 0) { computeMatrix = true; computedMatrixBits |= thisMatrix; // mark computedMatrixBits } } // Compute route if (computeMatrix) { int jj_waterMask = 0, kk_waterMask; int jj_terrainElevation = 0, kk_terrainElevation; bool dry = false; float elev = 0; for (int j = 0; j < EARTH_ROUTE_SPACE_HEIGHT; j++) { int jj = j * EARTH_ROUTE_SPACE_WIDTH; if (checkWater) { jj_waterMask = (int)((j * (float)earthWaterMaskHeight / EARTH_ROUTE_SPACE_HEIGHT)) * earthWaterMaskWidth; } if (checkElevation) { jj_terrainElevation = ((int)(j * (float)heightmapTextureHeight / EARTH_ROUTE_SPACE_HEIGHT)) * heightmapTextureWidth; } for (int k = 0; k < EARTH_ROUTE_SPACE_WIDTH; k++) { bool setBit = false; // Check altitude if (checkElevation) { kk_terrainElevation = (int)(k * (float)heightmapTextureWidth / EARTH_ROUTE_SPACE_WIDTH); elev = viewportElevationPoints[jj_terrainElevation + kk_terrainElevation]; } if (elev >= minAltitude && elev <= maxAltitude) { if (checkWater) { kk_waterMask = (int)(k * (float)earthWaterMaskWidth / EARTH_ROUTE_SPACE_WIDTH); dry = !earthWaterMask.GetBit(jj_waterMask + kk_waterMask); } if (terrainCapability == TERRAIN_CAPABILITY.Any || terrainCapability == TERRAIN_CAPABILITY.OnlyGround && dry || terrainCapability == TERRAIN_CAPABILITY.OnlyWater && !dry) { setBit = true; } } if (setBit) // set navigation bit { earthRouteMatrix[jj + k] |= thisMatrix; } else // clear navigation bit { earthRouteMatrix[jj + k] &= (byte)(byte.MaxValue ^ thisMatrix); } } } } if (finder == null) { if (_customRouteMatrix == null || !_pathFindingEnableCustomRouteMatrix) { PathFindingCustomRouteMatrixReset(); } finder = new PathFinderFast(earthRouteMatrix, thisMatrix, EARTH_ROUTE_SPACE_WIDTH, EARTH_ROUTE_SPACE_HEIGHT, _customRouteMatrix); } else { if (computeMatrix || thisMatrix != lastMatrix) { lastMatrix = thisMatrix; finder.SetCalcMatrix(earthRouteMatrix, thisMatrix); } } }