367 lines
16 KiB
C#
367 lines
16 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using TGS.PathFinding;
|
|
using UnityEngine;
|
|
|
|
|
|
namespace TGS
|
|
{
|
|
public partial class Cell
|
|
{
|
|
public int crossLevel = 0;
|
|
|
|
public bool isVisited = false;
|
|
public bool isInArea = false;
|
|
|
|
public Vector3Int mathPointV3;
|
|
|
|
public bool CanCross(int crossLevel)
|
|
{
|
|
return crossLevel > this.crossLevel && canCross;
|
|
}
|
|
|
|
public void ForceCanCross(bool value)
|
|
{
|
|
canCross = value;
|
|
crossLevel = value ? 0 : Int32.MaxValue;
|
|
}
|
|
}
|
|
|
|
public partial class TerrainGridSystem
|
|
{
|
|
public int CellGetCrossLevel(int cellIndex)
|
|
{
|
|
if (!ValidCellIndex(cellIndex)) return 0;
|
|
return cells[cellIndex].crossLevel;
|
|
}
|
|
|
|
public void CellSetCrossLevel(int cellIndex, int crossLevel)
|
|
{
|
|
if (!ValidCellIndex(cellIndex)) return;
|
|
cells[cellIndex].crossLevel = crossLevel;
|
|
needRefreshRouteMatrix = true;
|
|
}
|
|
|
|
public void CellSetCrossLevel(List<int> cellIndices, int crossLevel)
|
|
{
|
|
int count = cellIndices.Count;
|
|
for (int k = 0; k < count; k++)
|
|
{
|
|
Cell cell = cells[cellIndices[k]];
|
|
cell.crossLevel = crossLevel;
|
|
}
|
|
needRefreshRouteMatrix = true;
|
|
}
|
|
|
|
|
|
public List<int> FindPath(int cellIndexStart, int cellIndexEnd, int crossLevel, float maxSearchCost = 0, int maxSteps = 0, int cellGroupMask = -1, CanCrossCheckType canCrossCheckType = CanCrossCheckType.Default, bool ignoreCellCosts = false, bool includeInvisibleCells = true, int minClearance = 1, float maxCellCrossCost = float.MaxValue) {
|
|
float dummy;
|
|
return FindPath(cellIndexStart, cellIndexEnd, crossLevel, out dummy, maxSearchCost, maxSteps, cellGroupMask, canCrossCheckType, ignoreCellCosts, includeInvisibleCells, minClearance, maxCellCrossCost);
|
|
}
|
|
|
|
public List<int> FindPath(int cellIndexStart, int cellIndexEnd, int crossLevel, out float totalCost, float maxSearchCost = 0, int maxSteps = 0, int cellGroupMask = -1, CanCrossCheckType canCrossCheckType = CanCrossCheckType.Default, bool ignoreCellCosts = false, bool includeInvisibleCells = true, int minClearance = 1, float maxCellCrossCost = float.MaxValue) {
|
|
List<int> results = new List<int>();
|
|
FindPath(cellIndexStart, cellIndexEnd, results, crossLevel,out totalCost, maxSearchCost, maxSteps, cellGroupMask, canCrossCheckType, ignoreCellCosts, includeInvisibleCells, minClearance, maxCellCrossCost);
|
|
return results;
|
|
}
|
|
|
|
public void FindPath(List<int> result,
|
|
int cellIndexStart,
|
|
int cellIndexEnd,
|
|
int crossLevel)
|
|
{
|
|
FindPath(cellIndexStart, cellIndexEnd, result, crossLevel, out _);
|
|
}
|
|
|
|
public int FindPath(int cellIndexStart, int cellIndexEnd, List<int> cellIndices, int crossLevel, out float totalCost, float maxSearchCost = 0, int maxSteps = 0, int cellGroupMask = -1, CanCrossCheckType canCrossCheckType = CanCrossCheckType.Default, bool ignoreCellCosts = false, bool includeInvisibleCells = true, int minClearance = 1, float maxCellCrossCost = float.MaxValue) {
|
|
totalCost = 0;
|
|
if (cellIndices == null) return 0;
|
|
cellIndices.Clear();
|
|
if (cellIndexStart == cellIndexEnd || cellIndexStart < 0 || cellIndexEnd < 0 || cellIndexStart >= cells.Count || cellIndexEnd >= cells.Count) return 0;
|
|
Cell startCell = cells[cellIndexStart];
|
|
Cell endCell = cells[cellIndexEnd];
|
|
if (startCell == null || endCell == null) return 0;
|
|
bool startCellCanCross = startCell.canCross;
|
|
bool endCellCanCross = endCell.canCross;
|
|
int startCellCrossLevel = startCell.crossLevel;
|
|
int endCellCrossLevel = endCell.crossLevel;
|
|
|
|
if (canCrossCheckType != CanCrossCheckType.IgnoreCanCrossCheckOnAllCells) {
|
|
switch (canCrossCheckType) {
|
|
case CanCrossCheckType.ignoreCanCrossCheckOnStartAndEndCells:
|
|
startCell.ForceCanCross(true);
|
|
endCell.ForceCanCross(true);
|
|
break;
|
|
case CanCrossCheckType.ignoreCanCrossCheckOnStartCells:
|
|
if (!endCell.CanCross(crossLevel)) return 0;
|
|
startCell.ForceCanCross(true);
|
|
break;
|
|
case CanCrossCheckType.ignoreCanCrossCheckOnEndCells:
|
|
if (!startCell.CanCross(crossLevel)) return 0;
|
|
endCell.ForceCanCross(true);
|
|
break;
|
|
default:
|
|
if (!startCell.CanCross(crossLevel) || !endCell.CanCross(crossLevel))
|
|
return 0;
|
|
break;
|
|
}
|
|
}
|
|
if (needRefreshRouteMatrix && minClearance > 1) {
|
|
ComputeClearance(cellGroupMask, crossLevel);
|
|
}
|
|
ComputeRouteMatrix();
|
|
|
|
finder.Formula = _pathFindingHeuristicFormula;
|
|
finder.MaxSteps = maxSteps > 0 ? maxSteps : _pathFindingMaxSteps;
|
|
finder.Diagonals = _pathFindingUseDiagonals;
|
|
finder.HeavyDiagonalsCost = _pathFindingHeavyDiagonalsCost;
|
|
switch(_gridTopology)
|
|
{
|
|
case GridTopology.Irregular: finder.CellShape = CellType.Irregular; break;
|
|
case GridTopology.Hexagonal: finder.CellShape = _pointyTopHexagons ? CellType.PointyTopHexagon : CellType.FlatTopHexagon; break;
|
|
default: finder.CellShape = CellType.Box; break;
|
|
}
|
|
finder.MaxSearchCost = maxSearchCost > 0 ? maxSearchCost : _pathFindingMaxCost;
|
|
finder.CellGroupMask = cellGroupMask;
|
|
finder.IgnoreCanCrossCheck = canCrossCheckType == CanCrossCheckType.IgnoreCanCrossCheckOnAllCells || canCrossCheckType == CanCrossCheckType.ignoreCanCrossCheckOnAllCellsExceptStartAndEndCells;
|
|
finder.IgnoreCellCost = ignoreCellCosts;
|
|
finder.IncludeInvisibleCells = includeInvisibleCells;
|
|
finder.MinClearance = minClearance;
|
|
finder.MaxCellCrossCost = maxCellCrossCost;
|
|
if (OnPathFindingCrossCell != null) {
|
|
finder.OnCellCross = FindRoutePositionValidator;
|
|
} else {
|
|
finder.OnCellCross = null;
|
|
}
|
|
List<PathFinderNode> route = finder.FindPath(this, startCell, endCell,crossLevel, out totalCost, _evenLayout);
|
|
startCell.canCross = startCellCanCross;
|
|
endCell.canCross = endCellCanCross;
|
|
startCell.crossLevel = startCellCrossLevel;
|
|
endCell.crossLevel = endCellCrossLevel;
|
|
if (route != null) {
|
|
int routeCount = route.Count;
|
|
if (_gridTopology == GridTopology.Irregular) {
|
|
for (int r = routeCount - 2; r >= 0; r--) {
|
|
cellIndices.Add(route[r].PX);
|
|
}
|
|
} else {
|
|
for (int r = routeCount - 2; r >= 0; r--) {
|
|
int cellIndex = route[r].PY * _cellColumnCount + route[r].PX;
|
|
cellIndices.Add(cellIndex);
|
|
}
|
|
}
|
|
cellIndices.Add(cellIndexEnd);
|
|
} else {
|
|
return 0; // no route available
|
|
}
|
|
return cellIndices.Count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates clearance data for each cell. Clearance is used with FindPath method (minClearance parameter) and it's used to specify the minimum width of a path
|
|
/// </summary>
|
|
public void ComputeClearance(int cellGroupMask, int objectCrossLevel) {
|
|
|
|
if (clearanceComputed && clearanceCellGroupMask == cellGroupMask) return;
|
|
|
|
clearanceComputed = true;
|
|
clearanceCellGroupMask = cellGroupMask;
|
|
|
|
int cellsCount = cells.Count;
|
|
// clear clearance
|
|
for (int k = 0; k < cellsCount; k++) {
|
|
cells[k].clearance = 0;
|
|
}
|
|
|
|
int maxDim = Mathf.Max(rowCount, columnCount);
|
|
// uses true clearance
|
|
for (int j = rowCount - 1; j >= 0; j--) {
|
|
for (int k = 0; k < columnCount; k++) {
|
|
Cell cell = CellGetAtPosition(k, j);
|
|
if (cell == null) continue;
|
|
for (int maxClearance = 2; maxClearance < maxDim; maxClearance++) {
|
|
bool blocked = false;
|
|
int maxIter = maxClearance * maxClearance;
|
|
for (int i = 1; i < maxIter; i++) {
|
|
int nj = j - (i / maxClearance);
|
|
int nk = k + (i % maxClearance);
|
|
if (nj < 0 || nk >= columnCount) {
|
|
blocked = true;
|
|
break;
|
|
}
|
|
Cell neighbour = CellGetAtPosition(nk, nj);
|
|
if (neighbour == null || (neighbour.group & cellGroupMask) == 0 || !neighbour.CanCross(objectCrossLevel)) {
|
|
blocked = true;
|
|
break;
|
|
}
|
|
}
|
|
if (blocked) {
|
|
cell.clearance = (byte)(maxClearance - 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool CellGetLineOfSight(int startCellIndex, int endCellIndex, int crossLevel, ref List<int> cellIndices, ref List<Vector3> worldPositions, int cellGroupMask = -1, int lineResolution = 2, bool exhaustiveCheck = false, bool ignoreCanCrossCheck = false, bool checkLastCell = false)
|
|
{
|
|
|
|
if (cellIndices == null)
|
|
{
|
|
cellIndices = new List<int>();
|
|
}
|
|
else
|
|
{
|
|
cellIndices.Clear();
|
|
}
|
|
if (worldPositions == null)
|
|
{
|
|
worldPositions = new List<Vector3>();
|
|
}
|
|
else
|
|
{
|
|
worldPositions.Clear();
|
|
}
|
|
if (startCellIndex < 0 || startCellIndex >= cells.Count || endCellIndex < 0 || endCellIndex >= cells.Count)
|
|
return false;
|
|
|
|
Vector3 startPosition = CellGetPosition(startCellIndex);
|
|
Vector3 endPosition;
|
|
int vertexCount = exhaustiveCheck ? cells[endCellIndex].region.points.Count : 0;
|
|
bool success = true;
|
|
|
|
for (int p = 0; p <= vertexCount; p++)
|
|
{
|
|
if (p == 0)
|
|
{
|
|
endPosition = CellGetPosition(endCellIndex);
|
|
}
|
|
else
|
|
{
|
|
cellIndices.Clear();
|
|
worldPositions.Clear();
|
|
endPosition = CellGetVertexPosition(endCellIndex, p - 1);
|
|
}
|
|
|
|
int numSteps;
|
|
switch (_gridTopology)
|
|
{
|
|
case GridTopology.Hexagonal:
|
|
// Hexagon distance
|
|
numSteps = CellGetHexagonDistance(startCellIndex, endCellIndex);
|
|
lineResolution = Mathf.Max(2, lineResolution);
|
|
numSteps *= lineResolution;
|
|
break;
|
|
case GridTopology.Box:
|
|
numSteps = CellGetBoxDistance(startCellIndex, endCellIndex);
|
|
lineResolution = Mathf.Max(2, lineResolution);
|
|
numSteps *= lineResolution;
|
|
if (numSteps % 2 == 0)
|
|
numSteps++;
|
|
break;
|
|
default:
|
|
float dist = Vector3.Distance(startPosition, endPosition);
|
|
numSteps = Mathf.CeilToInt(dist * lineResolution);
|
|
break;
|
|
}
|
|
|
|
Cell lastCell = cells[startCellIndex];
|
|
success = true;
|
|
for (int k = 1; k <= numSteps; k++)
|
|
{
|
|
Vector3 position = Vector3.Lerp(startPosition, endPosition, (float)k / numSteps);
|
|
Cell cell = k == numSteps ? cells[endCellIndex] : CellGetAtPosition(position, true);
|
|
if (cell != null && cell != lastCell)
|
|
{
|
|
if (checkLastCell || cell != cells[endCellIndex])
|
|
{
|
|
if (!cell.CanCross(crossLevel) && !ignoreCanCrossCheck)
|
|
{
|
|
success = false;
|
|
break;
|
|
}
|
|
if ((cell.group & cellGroupMask) == 0)
|
|
{
|
|
success = false;
|
|
break;
|
|
}
|
|
}
|
|
// Check LOD blocks
|
|
if (LOSIsBlocked(lastCell, cell) || LOSIsBlocked(cell, lastCell))
|
|
{
|
|
success = false;
|
|
break;
|
|
}
|
|
cellIndices.Add(cell.index);
|
|
lastCell = cell;
|
|
}
|
|
worldPositions.Add(position);
|
|
}
|
|
if (success)
|
|
{
|
|
if (p == 0 && _gridTopology != GridTopology.Irregular)
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (success)
|
|
{
|
|
CellGetLine(startCellIndex, endCellIndex, ref cellIndices, ref worldPositions, lineResolution);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
public bool CellGetLineOfSight(Vector3 startPosition, Vector3 endPosition, int crossLevel, ref List<int> cellIndices, ref List<Vector3> worldPositions, int cellGroupMask = -1, int lineResolution = 2, bool exhaustiveCheck = false, bool ignoreCanCrossCheck = false, bool checkLastCell = false)
|
|
{
|
|
|
|
cellIndices = null;
|
|
|
|
Cell startCell = CellGetAtPosition(startPosition, true);
|
|
Cell endCell = CellGetAtPosition(endPosition, true);
|
|
if (startCell == null || endCell == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int cell1 = CellGetIndex(startCell);
|
|
int cell2 = CellGetIndex(endCell);
|
|
if (cell1 < 0 || cell2 < 0)
|
|
return false;
|
|
|
|
return CellGetLineOfSight(cell1, cell2, crossLevel, ref cellIndices, ref worldPositions, cellGroupMask, lineResolution, exhaustiveCheck, ignoreCanCrossCheck, checkLastCell);
|
|
}
|
|
|
|
public void CellTestLineOfSight(int startCellIndex, List<int> targetCellIndices, int crossLevel, int cellGroupMask = -1, int lineResolution = 2, bool exhaustiveCheck = false, bool ignoreCanCrossCheck = false, bool checkLastCell = false)
|
|
{
|
|
int count = targetCellIndices.Count;
|
|
List<Vector3> dummyPositions = null;
|
|
cellIteration++;
|
|
for (int k = 0; k < count; k++)
|
|
{
|
|
int targetCellIndex = targetCellIndices[k];
|
|
if (cells[targetCellIndex].iteration != cellIteration)
|
|
{
|
|
if (CellGetLineOfSight(startCellIndex, targetCellIndex, crossLevel, ref tempListCells, ref dummyPositions, cellGroupMask, lineResolution, exhaustiveCheck, ignoreCanCrossCheck, checkLastCell))
|
|
{
|
|
int lineCount = tempListCells.Count;
|
|
for (int j = 0; j < lineCount; j++)
|
|
{
|
|
int index = tempListCells[j];
|
|
cells[index].iteration = cellIteration;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
targetCellIndices.RemoveAt(k);
|
|
k--;
|
|
count--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |