NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/Gameplay/AI/Utils/AIUtils.cs

731 lines
27 KiB
C#

using System;
using Framework;
using UnityEngine;
using Gameplay.Unit;
using Gameplay.Area;
using Gameplay.Level;
using Gameplay.Unit.Data;
using Gameplay.Character;
using Gameplay.Area.Around;
using Gameplay.Vehicle.Impl;
using System.Collections.Generic;
using static Gameplay.Level.LevelData;
namespace Gameplay.AI
{
public static partial class AIUtils
{
/// <summary>
/// 寻找范围内最近的敌方npc
/// </summary>
public static GameUnit FindNearestEnemyNpcInShootRange(GameUnit owner)
{
try
{
var level = LevelManager.Instance.CurrentLevel;
if (level == null || level.SelfNpcLocalID == null || level.SelfNpcLocalID.Count <= 0) return null;
var map = LevelManager.Instance.CurrentLevel.Map;
if (map == null) return null;
var enemies = GameUnitManager.instance.SearchEnemies(owner);
GameUnit nearestEnemy = null;
var minDistance = float.MaxValue;
var ownerCellIndex = owner.transData?.cellIndex;
if (!ownerCellIndex.HasValue) return null;
foreach (var enemy in enemies)
{
if (enemy == null || enemy.statusData.isDead || enemy.unitLevelData == null) continue;
if (!level.SelfNpcLocalID.Contains(enemy.unitLevelData.LocalId)) continue;
var enemyCellIndex = enemy.transData?.cellIndex;
if (!enemyCellIndex.HasValue) continue;
var distance = MapUtils.Distance(map, ownerCellIndex.Value, enemyCellIndex.Value);
if (distance <= owner.fightData.findEnemyRange)
{
if (distance < minDistance)
{
minDistance = distance;
nearestEnemy = enemy;
}
}
}
return nearestEnemy;
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.FindNearestEnemyNpcInShootRange error: {0}", e);
return null;
}
}
/// <summary>
/// 获取距离自己最近的单位
/// </summary>
public static GameUnit GetNearestGameUnit(GameUnit owner, GameUnit newUnit, GameUnit unit)
{
try
{
if (owner == null || newUnit == null || unit == null) return null;
var map = LevelManager.Instance.CurrentLevel.Map;
if (map == null) return null;
var ownerCellIndex = owner.transData?.cellIndex;
var unitCellIndex = unit.transData?.cellIndex;
var newUnitCellIndex = newUnit.transData?.cellIndex;
if (!ownerCellIndex.HasValue || !unitCellIndex.HasValue || !newUnitCellIndex.HasValue) return null;
var distance = MapUtils.Distance(map, ownerCellIndex.Value, unitCellIndex.Value);
var newDistance = MapUtils.Distance(map, ownerCellIndex.Value, newUnitCellIndex.Value);
return distance <= newDistance ? unit : newUnit;
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.GetNearestGameUnit error: {0}", e);
// 异常返回默认单位
return unit;
}
}
/// <summary>
/// 寻找最近的巡逻点位
/// </summary>
public static int FindNearestPointInPatrolInfo(GameUnit owner, List<PatrolInfo> patrolInfos)
{
try
{
// 使用优化后的通用方法
return FindNearestPatrolInfoIndex(owner, patrolInfos);
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.FindNearestPointInPatrolInfo error: {0}", e);
// 异常返回第一个索引点
return 0;
}
}
/// <summary>
/// 巡逻点位转换为格子索引
/// </summary>
public static int GetPatrolPointIndexFromVector2Int(Map map, int patrolIndex, List<PatrolInfo> patrolInfos)
{
try
{
if (map == null || patrolIndex >= patrolInfos.Count) return -1;
var patrolInfo = patrolInfos[patrolIndex];
if (patrolInfo == null) return -1;
return map.BlockPosition2TGSCellIndex(patrolInfo.patrolPoint);
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.GetPatrolPointIndexFromVector2Int error: {0}", e);
// 异常返回无效格子索引
return -1;
}
}
/// <summary>
/// 是否停止循环逻辑
/// </summary>
public static bool AINeedStopUpdate(global::Gameplay.Level.Level level)
{
if (level == null) return false;
return level.IsPreparing() || level.IsEnd() || Math.Abs(level.GetSpeed() - 0f) < 0.01f;
}
/// <summary>
/// 激活AI攻击
/// </summary>
public static void ActiveAIAttack(GameUnit owner)
{
try
{
if (owner == null) return;
if (owner.statusData.isAttackActive) return;
var map = LevelManager.Instance.CurrentLevel.Map;
if (map == null) return;
if (owner.controlData.targetEnemy != null)
{
owner.statusData.isAttackActive = true;
owner.statusData.isInFight = true;
}
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.ActiveAIAttack error: {0}", e);
}
}
/// <summary>
/// 单位撤退
/// </summary>
/// <returns></returns>
public static bool MoveAway(GameUnit owner, GameUnit enemyUnit, bool cancelFollow = true)
{
try
{
if (enemyUnit == null) return false;
var map = LevelManager.Instance.CurrentLevel.Map;
if (map == null) return false;
var mapData = map.MapData;
if (mapData == null) return false;
map.WorldPosition2BlockPosition(enemyUnit.Node.GetPosition(), out var targetEnemy2IntPos);
var self2IntPos = map.TGSCellIndex2BlockPosition(owner.transData.cellIndex);
var newSelf2IntPos = self2IntPos;
newSelf2IntPos.x = Math.Clamp(
(self2IntPos.x - targetEnemy2IntPos.x) > 0 ? self2IntPos.x + 1 : self2IntPos.x - 1, 0,
map.MapData.Height - 1);
newSelf2IntPos.y = Math.Clamp(
(self2IntPos.y - targetEnemy2IntPos.y) > 0 ? self2IntPos.y + 1 : self2IntPos.y - 1, 0,
map.MapData.Width - 1);
if (self2IntPos == newSelf2IntPos) return false;
var cellIndex = map.BlockPosition2TGSCellIndex(newSelf2IntPos);
if (PathFinder.CanPass(cellIndex, owner))
{
owner.MoveTo(cellIndex, cancelFollow);
return true;
}
return false;
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.MoveAway error: {0}", e);
return false;
}
}
/// <summary>
/// AI 移动向敌人并检查
/// </summary>
public static void CheckAndMove(GameUnit owner, int cellIndex, bool cancelFollow = true)
{
try
{
if (!AIMoveHelper.MoveToCell(owner, cellIndex, cancelFollow)) return;
if (owner.gameunitType != EGameUnitType.Character &&
owner.gameunitType != EGameUnitType.Vehicle) return;
var units = AreaManager.instance.GetUnitsByCellIndex(cellIndex);
foreach (var unit in units)
{
if (unit.commonData.troopId == owner.commonData.troopId)
{
}
else
{
// 如果是敌人, 则设置敌人目标
if (unit.statusData.CheckCanBeTarget(owner, -1))
{
owner.controlData.markEnemy = unit;
owner.controlData.forceUpdateTarget = true;
return;
}
}
}
// 走到这里,说明没有目标,清空标记
owner.controlData.markVehicle = null;
owner.controlData.markEnemy = null;
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.CheckAndMove error: {0}", e);
}
}
/// <summary>
/// 计算当前位置与出生点位置是否超过防守距离
/// </summary>
public static bool CheckDistanceIsOver(GameUnit owner)
{
try
{
if (owner == null) return true;
var map = LevelManager.Instance.CurrentLevel.Map;
if (map == null) return true;
var curPos = owner.transData.cellIndex;
var bornPos = owner.transData.bornCellIndex;
var distance = MapUtils.Distance(map, curPos, bornPos);
return owner.unitLevelData.DefensiveDistance < distance;
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.CheckDistanceIsOver error: {0}", e);
// 默认超出
return true;
}
}
/// <summary>
/// 顺时针获取周围第一个能够到达的一圈格子
/// </summary>
public static int GetCanReachCellAround(Map map, GameUnit owner, int centerIndex, int step = 1)
{
var cellList = GetAroundCells(centerIndex, step);
foreach (var cellIndex in cellList)
{
var blockData = map.GetBlockData(cellIndex);
if (blockData == null) continue;
var typeProperty = blockData.GetTerrainTypeProperty();
if (typeProperty == null) continue;
if (CheckPathExits(owner, cellIndex))
{
return cellIndex;
}
}
return -1;
}
/// <summary>
/// 检测是否存在路径
/// </summary>
public static bool CheckPathExits(GameUnit owner, int cellIndex)
{
var map = LevelManager.Instance.CurrentLevel.Map;
if (owner == null || cellIndex == -1 || map == null) return false;
var path = PathFinder.FindPath(map, cellIndex, owner);
return path != null && path.Count > 0;
}
/// <summary>
/// 设置cellIndex周围的通行级别
/// </summary>
/// <returns>Dic(格子,原来的通行级别)</returns>
public static Dictionary<int, int> SetCellCrossLevel(Map map, GameUnit owner, int cellIndex, int step = 1)
{
if (map == null || owner == null) return null;
var cells = new Dictionary<int, int>();
var cellList = GetAroundCells(cellIndex, step);
var crossLevel = owner.commonData.crossLevel + 1;
foreach (var cell in cellList)
{
var blockData = map.GetBlockData(cell);
if (blockData == null) continue;
map.GetRuntimeBlockProperty(cell, out var data);
var oldCrossLevel = data.crossLevel;
if (data.crossLevel > crossLevel) continue;
data.crossLevel = crossLevel + 1;
map.SetRuntimeBlockProperty(cell, data);
cells[cell] = oldCrossLevel;
//DebugUtil.LogWarning("设置地格{0}的通行级别为:{1}", cell, crossLevel);
}
return cells;
}
/// <summary>
/// 设置cellList通行级别
/// </summary>
public static void SetCellCrossLevel(Map map, Dictionary<int, int> cells)
{
if (map == null) return;
foreach (var cell in cells)
{
var blockData = map.GetBlockData(cell.Key);
if (blockData == null) continue;
map.GetRuntimeBlockProperty(cell.Key, out var data);
data.crossLevel = cell.Value;
map.SetRuntimeBlockProperty(cell.Key, data);
//DebugUtil.LogError("还原格子:{0}到{1}", cell.Key, cell.Value);
}
}
/// <summary>
/// 获得周围格子
/// </summary>
private static List<int> GetAroundCells(int centerIndex, int step = 1)
{
var cellList = new List<int>();
AroundHelper.GetAround(centerIndex, step, cellList);
return cellList;
}
// -------------------- New -------------------- //
#region 载具相关
/// <summary>
/// 寻找周围可以到达的格子 (用于载具下人)
/// </summary>
public static int FindCanReachCell(int cellIndex, GameUnit unit, int maxStep = 2)
{
try
{
var map = LevelManager.Instance.CurrentLevel?.Map;
if (map == null) return -1;
for (var i = 1; i <= maxStep; i++)
{
var minSteps = i - 1;
var maxSteps = i;
var cells = map.TerrainGridSystem.CellGetNeighboursWithinRange(cellIndex, minSteps, maxSteps,
canCrossCheckType: TGS.CanCrossCheckType.IgnoreCanCrossCheckOnAllCells);
foreach (var cell in cells)
{
if (!PathFinder.CanBasePass(cell, unit)) continue;
if (AreaManager.instance.CheckContainsMoveUnit(cell)) continue;
return cell;
}
}
// 无处可去
return -1;
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.FindCanReachCell error: {0}", e);
return -1;
}
}
/// <summary>
/// 寻找最近的载具
/// </summary>
public static NormalVehicle FindNearestVehicle(GameUnit owner)
{
try
{
var inSightDistanceDic = new Dictionary<GameUnit, int>();
foreach (var unit in GameUnitManager.instance.infightUnits.GetListByTroop(ETroopsId.Enemy))
{
if (unit.gameunitType == EGameUnitType.Vehicle)
{
inSightDistanceDic.Add(unit, GetDistance(owner.transData.cellIndex, unit.transData.cellIndex));
}
}
var gameUnit = GetNearestUnit(inSightDistanceDic);
return gameUnit as NormalVehicle;
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.FindNearestVehicle error: {0}", e);
return null;
}
}
#endregion
#region 巡逻相关
/// <summary>
/// 寻找最近的巡逻点位
/// </summary>
public static int FindNearestPatrolInfoIndex(GameUnit owner, List<PatrolInfo> patrolInfos)
{
return NearestSearchUtils.FindNearestIndex(owner, patrolInfos, patrolInfo =>
{
var map = LevelManager.Instance.CurrentLevel?.Map;
return map?.BlockPosition2TGSCellIndex(patrolInfo.patrolPoint) ?? Constants.INVALID_ID;
});
}
/// <summary>
/// 巡逻点位转换为格子索引
/// </summary>
public static int GetPatrolPointIndex(int patrolIndex, List<PatrolInfo> patrolInfos)
{
var map = LevelManager.Instance.CurrentLevel.Map;
if (map == null || patrolIndex >= patrolInfos.Count) return -1;
var patrolInfo = patrolInfos[patrolIndex];
if (patrolInfo == null) return -1;
return map.BlockPosition2TGSCellIndex(patrolInfo.patrolPoint);
}
/// <summary>
/// 寻找最近的路点点位
/// </summary>
public static int FindNearestRoadInfoIndex(GameUnit owner, List<int> cells)
{
return NearestSearchUtils.FindNearestIndex(owner, cells, cellIndex => cellIndex);
}
/// <summary>
/// 获取点位等待时间
/// </summary>
public static float GetPatrolPointWaitTime(int patrolIndex, List<PatrolInfo> patrolInfos)
{
if (patrolIndex < 0 || patrolIndex >= patrolInfos.Count) return 0;
return patrolInfos[patrolIndex]?.waitTime ?? 0;
}
#endregion
#region 防重合
private const float MIN_AVOID_DELAY = 0f;
private const float MAX_AVOID_DELAY = 1f;
/// <summary>
/// AI避让逻辑
/// </summary>
public static void TryAvoidOtherUnit(GameUnit owner)
{
if (IsNeedStop(owner) || owner.unitLevelData.AIBattleInfo == null) return;
var targetId = owner.unitLevelData.AIBattleInfo.Value.targetID;
/*// 移动中:准备检测避让
var context = owner.unitLevelData.AvoidanceData;
if (owner.statusData.isMoveing && !context.NeedAvoidCheck)
{
context.NeedAvoidCheck = true;
context.CurrentAvoidDelay = UnityEngine.Random.Range(MIN_AVOID_DELAY, MAX_AVOID_DELAY);
context.AvoidCheckTimer = 0f;
}
// 移动结束:计时触发检测
if (context.NeedAvoidCheck && !owner.statusData.isMoveing && owner.controlData.targetCellIndex == -1)
{
context.AvoidCheckTimer += Time.deltaTime;
if (context.AvoidCheckTimer >= context.CurrentAvoidDelay)
{
context.NeedAvoidCheck = false;
CheckAreaHasOtherUnit(owner, targetId);
}
}*/
// 没有目标位置检测避让
if (owner.controlData.targetCellIndex == -1)
{
CheckAreaHasOtherUnit(owner, targetId);
}
}
/// <summary>
/// 判断是否有敌人重合
/// </summary>
private static void CheckAreaHasOtherUnit(GameUnit owner, uint target)
{
try
{
if (owner == null) return;
var cellIndex = owner.transData.cellIndex;
var units = AreaManager.instance.GetUnitsByCellIndex(cellIndex);
var hasOther = false;
foreach (var unit in units)
{
if (unit == owner) continue;
if (!unit.commonData.canClaimStandCellIndex) continue;
if (unit.statusData.isMoveing) continue;
hasOther = true;
break;
}
if (!hasOther) return;
if (target == Constants.INVALID_UINT_ID)
{
MoveToOtherCell(owner);
}
else
{
var enemy = GameUnitManager.instance.infightUnits.Search(target);
if (enemy == null || enemy.statusData.isDead) return;
MoveToCanAttackCell(owner, enemy);
}
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.CheckAreaIsOtherEnemyUnit error: {0}", e);
}
}
/// <summary>
/// 移动至周围可到达格子
/// </summary>
public static void MoveToOtherCell(GameUnit owner, int centerCell = -1, int maxStep = 2)
{
try
{
var map = LevelManager.Instance.CurrentLevel?.Map;
if (map == null || owner == null) return;
var cellIndex = centerCell == -1 ? owner.transData.cellIndex : centerCell;
if (!AreaManager.instance.CheckContainsMoveUnit(cellIndex))
return;
for (var i = 1; i <= maxStep; i++)
{
var minSteps = i - 1;
var maxSteps = i;
var cells = map.TerrainGridSystem.CellGetNeighboursWithinRange(cellIndex, minSteps, maxSteps,
canCrossCheckType: TGS.CanCrossCheckType.IgnoreCanCrossCheckOnAllCells);
foreach (var cell in cells)
{
if (!CheckCellCanSelect(cell)) continue;
if (!PathFinder.CanBasePass(cell, owner)) continue;
if (AreaManager.instance.CheckContainsMoveUnit(cell)) continue;
if (maxSteps > 1)
{
if (CheckPathExits(owner, cell))
{
owner.controlData.targetCellIndex = cell;
return;
}
}
else
{
owner.controlData.targetCellIndex = cell;
return;
}
}
}
// 无处可去
owner.MoveTo(owner.transData.bornCellIndex, true);
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.MoveOtherCell error: {0}", e);
}
}
/// <summary>
/// 移动至可以攻击到目标的周围格子
/// </summary>
private static void MoveToCanAttackCell(GameUnit owner, GameUnit enemy, int maxStep = 2)
{
try
{
var map = LevelManager.Instance.CurrentLevel?.Map;
if (map == null || owner == null || enemy == null) return;
var enemyCellIndex = enemy.transData.cellIndex;
var cellIndex = owner.transData.cellIndex;
if (!AreaManager.instance.CheckContainsMoveUnit(cellIndex))
return;
for (var i = 1; i <= maxStep; i++)
{
var minSteps = i - 1;
var maxSteps = i;
var cells = map.TerrainGridSystem.CellGetNeighboursWithinRange(cellIndex, minSteps, maxSteps,
canCrossCheckType: TGS.CanCrossCheckType.IgnoreCanCrossCheckOnAllCells);
foreach (var cell in cells)
{
if (!CheckCellCanSelect(cell)) continue;
if (!PathFinder.CanBasePass(cell, owner)) continue;
if (AreaManager.instance.CheckContainsMoveUnit(cell)) continue;
// 判断该位置可否攻击目标
var searchArea = AreaManager.instance.attackAreaManager.GetAttackArea(cell,
owner.fightData.attackDistance);
if (!searchArea.canAttackCells.Contains(enemyCellIndex)) continue;
if (maxSteps > 1)
{
if (CheckPathExits(owner, cell))
{
owner.controlData.targetCellIndex = cell;
return;
}
}
else
{
owner.controlData.targetCellIndex = cell;
return;
}
}
}
// 无处可去
owner.MoveTo(owner.transData.bornCellIndex, true);
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.AvoidEnemy error: {0}", e);
}
}
#endregion
#region Normal
/// <summary>
/// 检测位置是否可到达
/// </summary>
public static bool PositionCanReach(GameUnit owner, int targetCellIndex)
{
var map = LevelManager.Instance.CurrentLevel.Map;
if (owner == null || targetCellIndex == -1 || map == null) return false;
if (!CheckCellCanSelect(targetCellIndex)) return false;
var path = PathFinder.FindPath(map, targetCellIndex, owner);
return path != null && path.Count > 0;
}
/// <summary>
/// 获取距离
/// </summary>
public static int GetDistance(int cellIndex1, int cellIndex2)
{
var map = LevelManager.Instance.CurrentLevel.Map;
if (map == null) return -1;
return MapUtils.Distance(map, cellIndex1, cellIndex2);
}
/// <summary>
/// 是否需要停掉AI
/// </summary>
public static bool IsNeedStop(GameUnit owner)
{
return owner.statusData.isRetreat || owner.statusData.isDead || !owner.statusData.isOnFloor ||
owner.statusData.isChaos || owner.statusData.isStun || owner.statusData.isLockPos;
}
/// <summary>
/// 检测目标是否能被攻击
/// </summary>
public static bool CheckCanBeTarget(GameUnit owner, GameUnit enemy)
{
if (owner == null || enemy == null) return false;
return enemy.statusData.CheckCanBeTarget(owner, owner.fightData.attackDistance);
}
/// <summary>
/// 获取格子世界坐标
/// </summary>
public static Vector3 GetCellPosFromIndex(int cellIndex)
{
if (cellIndex == -1) return Vector3.zero;
var map = LevelManager.Instance.CurrentLevel.Map;
if (map == null) return Vector3.zero;
return map.TGSCellIndex2WorldPosition(cellIndex);
}
/// <summary>
/// 检测地格是否可选
/// </summary>
private static bool CheckCellCanSelect(int cellIndex)
{
var map = LevelManager.Instance.CurrentLevel.Map;
if (map == null) return false;
return MapUtils.GetTypeProperty(map, cellIndex) != null &&
MapUtils.GetTypeProperty(map, cellIndex).selectable;
}
/// <summary>
/// 检测地格是否可选
/// </summary>
public static bool CheckCellCanSelect(Vector2Int cellPos)
{
var map = LevelManager.Instance.CurrentLevel.Map;
if (map == null) return false;
var cell = map.BlockPosition2TGSCellIndex(cellPos);
return MapUtils.GetTypeProperty(map, cell) != null &&
MapUtils.GetTypeProperty(map, cell).selectable;
}
/// <summary>
/// 范围限制
/// </summary>
public static int ClampCoord(int value, int min, int max) => Mathf.Clamp(value, min, max);
#endregion
}
}