NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/Gameplay/Area/Path/PathFinder.cs

394 lines
14 KiB
C#

using System.Collections;
using System.Collections.Generic;
using System.Text;
using AOT.PostProcess.BlackArea;
using Gameplay.Area;
using Gameplay.Building.Impl;
using Gameplay.Character;
using Gameplay.Common;
using Gameplay.Level;
using Gameplay.PostProcess.BlackArea;
using Gameplay.Unit;
using Gameplay.Unit.Data;
using UnityEngine;
using static Gameplay.Map;
namespace Gameplay
{
public static class PathFinder
{
private static List<int> _cacheChangedCellIndexs = new List<int>();
private static void _RuntimeMarkMapCrossState(GameUnit moveUnit)
{
var map = LevelManager.Instance.CurrentLevel.Map;
// 找到所有的单位
var allUnits = GameUnitManager.instance.infightUnits.unitList;
for (int i = 0; i < allUnits.Count; i++)
{
var unit = allUnits[i];
if (!unit.statusData.isOnFloor)
{
// 在载具上的单位不需要设置
continue;
}
if (unit.transData.cellIndex == moveUnit.transData.cellIndex)
{
// 重叠时特殊处理
continue;
}
var cellIndex = unit.transData.cellIndex;
if (moveUnit.gameunitType == EGameUnitType.Vehicle && unit is BuildAntitankCell)
{
// 当前格子需要动态设置为比载具通行级别高
var moveLevel = moveUnit.commonData.crossLevel;
map.GetRuntimeBlockProperty(cellIndex, out RuntimeBlockProperty tempBlockProperty);
if (tempBlockProperty.crossLevel <= moveLevel)
{
tempBlockProperty.crossLevel = moveLevel + 1;
map.SetRuntimeBlockProperty(cellIndex, tempBlockProperty);
_cacheChangedCellIndexs.Add(cellIndex);
}
continue;
}
// 跳过可移动单位的站位等级检查
if (CodeDefine.Fight.SkipMoveUnitStandLevelCheck
&& (unit.gameunitType == EGameUnitType.Vehicle
|| unit.gameunitType == EGameUnitType.Character))
{
// 己方单位不需要检查
if (moveUnit.commonData.troopId == ETroopsId.Self)
{
continue;
}
// 同一方的单位不需要检查
if (unit.commonData.troopId == moveUnit.commonData.troopId)
{
continue;
}
}
var unitStandLevel = unit.commonData.standLevel;
map.GetRuntimeBlockProperty(cellIndex, out RuntimeBlockProperty runtimeBlockProperty);
if (unitStandLevel > runtimeBlockProperty.crossLevel)
{
runtimeBlockProperty.crossLevel = unitStandLevel;
map.SetRuntimeBlockProperty(cellIndex, runtimeBlockProperty);
_cacheChangedCellIndexs.Add(cellIndex);
}
}
// if (moveUnit.commonData.troopId == ETroopsId.Self)
// {
// // 如果是己方,需要将所有未照亮的格子设置为不可通行
// var allLightPoints = (BlackAreaManager.instance as BlackAreaManagerImpl).pointList;
// for (int i = 0; i < allLightPoints.Count; i++)
// {
// var point = allLightPoints[i];
// if (point.isLight) continue;
// var cellIndex = point.cellIndex;
// map.GetRuntimeBlockProperty(cellIndex, out RuntimeBlockProperty runtimeBlockProperty);
// runtimeBlockProperty.crossLevel = int.MaxValue;
// map.SetRuntimeBlockProperty(cellIndex, runtimeBlockProperty);
// _cacheChangedCellIndexs.Add(cellIndex);
// }
// }
}
private static void _RuntimeRecoverMapCrossState()
{
var map = LevelManager.Instance.CurrentLevel.Map;
for (int i = 0; i < _cacheChangedCellIndexs.Count; i++)
{
var cellIndex = _cacheChangedCellIndexs[i];
map.GetRuntimeBlockProperty(cellIndex, out RuntimeBlockProperty runtimeBlockProperty);
var mapData = map.GetBlockData(cellIndex);
runtimeBlockProperty.crossLevel = mapData.GetTerrainTypeProperty().crossLevel;
map.SetRuntimeBlockProperty(cellIndex, runtimeBlockProperty);
}
_cacheChangedCellIndexs.Clear();
}
public static bool CanPass(int cellIndex,
GameUnit moveUnit)
{
if (!CanBasePass(cellIndex, moveUnit))
{
return false;
}
var crossLevel = moveUnit.commonData.crossLevel;
// 额外判断当前格子是否有单位
var units = AreaManager.instance.GetUnitsByCellIndex(cellIndex);
for (int i = 0; i < units.Count; i++)
{
var unit = units[i];
if (unit == moveUnit) continue;
if (moveUnit.gameunitType == EGameUnitType.Vehicle && unit is BuildAntitankCell)
{
return false;
}
// 跳过可移动单位的站位等级检查
if (CodeDefine.Fight.SkipMoveUnitStandLevelCheck
&& (unit.gameunitType == EGameUnitType.Vehicle
|| unit.gameunitType == EGameUnitType.Character))
{
// 己方单位不需要检查
if (moveUnit.commonData.troopId == ETroopsId.Self)
{
continue;
}
// 同一方的单位不需要检查
if (unit.commonData.troopId == moveUnit.commonData.troopId)
{
continue;
}
}
if (unit.commonData.standLevel >= crossLevel) return false;
}
return true;
}
public static bool CanBasePass(int cellIndex,
GameUnit moveUnit)
{
var map = LevelManager.Instance.CurrentLevel.Map;
var crossLevel = moveUnit.commonData.crossLevel;
var runtimeBlockProperties = map.runtimeTerrainProperty;
if (cellIndex < 0 || cellIndex >= runtimeBlockProperties.Count)
{
return false;
}
var runtimeBlockProperty = runtimeBlockProperties[cellIndex];
if (!runtimeBlockProperty.selectable) return false;
if (crossLevel <= runtimeBlockProperty.crossLevel) return false;
return true;
}
private static bool _CheckEndCanPass(int to,
GameUnit moveUnit)
{
var map = LevelManager.Instance.CurrentLevel.Map;
var crossLevel = moveUnit.commonData.crossLevel;
map.GetRuntimeBlockProperty(to, out var runtimeBlockProperty);
if (!runtimeBlockProperty.selectable) return false;
if (crossLevel <= runtimeBlockProperty.crossLevel) return false;
return true;
}
public static List<int> FindPath(Map map,
int to,
GameUnit moveUnit)
{
var moveLevel = moveUnit.commonData.crossLevel;
var from = moveUnit.transData.cellIndex;
var result = new List<int>();
if (from == to)
{
// 同一个格子
result.Add(to);
return result;
}
var canReach = true;
var mapData = map.GetBlockData(to);
if (mapData == null)
{
DebugUtil.LogError("FindPath: mapData is null, to: " + to);
return result;
}
if (!_CheckEndCanPass(to, moveUnit))
{
// 如果终点不可通行,直接走无法走通的逻辑
// 地形层面的不可通行
canReach = false;
}
_RuntimeMarkMapCrossState(moveUnit);
if (canReach)
{
map.GetRuntimeBlockProperty(to, out var toData);
MapUtils.ForceSetCanCross(true, ref toData);
map.SetRuntimeBlockProperty(to, toData);
_cacheChangedCellIndexs.Add(to);
result = map.TerrainGridSystem.FindPath(from, to, moveLevel);
}
// 无法直接走通
if (!_IsPathExist(result))
{
var cacheFrom = from;
var deepSearch = _ProcessCannotFindPath(map, cacheFrom, to, result, moveLevel);
while (deepSearch)
{
cacheFrom = result[^1];
deepSearch = _ProcessCannotFindPath(map, cacheFrom, to, result, moveLevel);
}
}
if (result.Count == 1 && !CanPass(to, moveUnit))
{
// 只有一格 且下一格子不可通行,则无法到达
result.Clear();
}
if (CodeDefine.ENABLE_FIGHT_NORMAL_ATTACK_LOG)
{
var stringBuilder = new StringBuilder();
stringBuilder.Append("FindPath: ");
stringBuilder.Append(from);
stringBuilder.Append(" -> ");
stringBuilder.Append(to);
stringBuilder.Append(" : ");
for (int i = 0; i < result.Count; i++)
{
stringBuilder.Append(result[i]);
stringBuilder.Append(" ");
}
DebugUtil.Log(stringBuilder);
}
_RuntimeRecoverMapCrossState();
return result;
}
private static bool _ProcessCannotFindPath(Map map,
int from,
int to,
ICollection<int> pathList,
int moveLevel)
{
// 如果两个格子相邻,则直接返回
var distanceToNow = AreaManager.instance.GetDistance(from, to);
if (distanceToNow <= 1)
{
return false;
}
// 无法到达
// 获得附近一圈的格子
var nearByCells = AreaManager.instance.GetCellIndexsAround(from, 1, false);
if (nearByCells == null || nearByCells.Count <= 0) return false;
// 取最近的格子
int closestCell = -1;
var selfPos = map.TGSCellIndex2WorldPosition(from);
var targetPos = map.TGSCellIndex2WorldPosition(to);
var minDistance = Vector3.Distance(selfPos, targetPos) - 0.5f;
for (int i = 0; i < nearByCells.Count; i++)
{
var cellIndex = nearByCells[i];
// 不走回头路
if (pathList.Contains(cellIndex))
{
continue;
}
// 判断是否可通行
map.GetRuntimeBlockProperty(cellIndex, out var toData);
if (!MapUtils.CanPass(moveLevel, toData))
{
continue;
}
var newPos = map.TGSCellIndex2WorldPosition(cellIndex);
var distance = Vector3.Distance(targetPos, newPos);
if (distance < minDistance)
{
minDistance = distance;
closestCell = cellIndex;
}
}
if (closestCell != -1)
{
pathList.Add(closestCell);
return true;
}
return false;
}
private static bool _IsPathExist(ICollection pathList)
{
return !(pathList == null || pathList.Count <= 0);
}
/// <summary>
/// !!! 该方法极其费性能,慎用 !!!
/// </summary>
/// <param name="owner"></param>
public static void AvoidRepeat(GameUnit owner)
{
if (!owner.commonData.canControl)
{
// 只判断可控制单位
return;
}
var units = AreaManager.instance.GetUnitsByCellIndex(owner.transData.cellIndex);
var hasOther = false;
for (int i = 0; i < units.Count; i++)
{
var unit = units[i];
if (unit == owner)
{
continue;
}
if (!unit.commonData.canClaimStandCellIndex)
{
continue;
}
if (unit.statusData.isMoveing)
{
continue;
}
// TODO: 目前实测AI并不会自动避让
// if (!unit.commonData.canControl)
// {
// // AI会自动避让,不需要处理
// continue;
// }
hasOther = true;
break;
}
if (hasOther)
{
if (owner.controlData.targetEnemy != null)
{
_MoveAnyOtherCanAttackEnemy(owner);
}
else
{
_MoveAnyOtherPoint(owner);
}
}
}
private static void _MoveAnyOtherPoint(GameUnit owner)
{
var searchCellIndex = AreaManager.instance.SearchNearCanClaimCellIndex(owner);
if (searchCellIndex == -1 || searchCellIndex == owner.transData.cellIndex) return;
owner.controlData.targetCellIndex = searchCellIndex;
}
private static void _MoveAnyOtherCanAttackEnemy(GameUnit owner)
{
var searchIndex = AreaManager.instance.SearchNearCanClaimCellIndexWithAttack(owner);
if (searchIndex == -1 || searchIndex == owner.transData.cellIndex) return;
owner.controlData.targetCellIndex = searchIndex;
}
}
}