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

1062 lines
40 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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
{
private static List<GameUnit> inSightEnemies = new List<GameUnit>(16);
private static Dictionary<GameUnit, int> inSightDistanceList = new Dictionary<GameUnit, int>(16);
private static List<GameUnit> allFriends = new List<GameUnit>(16);
private static List<GameUnit> friendsInSight = new List<GameUnit>(16);
private static Dictionary<int, int> patrolDistanceList = new Dictionary<int, int>(64);
/// <summary>
/// 寻找锁敌范围内最近的敌人
/// </summary>
public static GameUnit FindNearestEnemyInSightDistance(GameUnit owner)
{
try
{
if (owner == null) return null;
var map = LevelManager.Instance.CurrentLevel.Map;
if (map == null) return null;
inSightDistanceList.Clear();
var enemies = GameUnitManager.instance.SearchEnemies(owner);
var ownerCellIndex = owner.transData?.cellIndex;
for (var i = 0; i < enemies.Count; i++)
{
var enemy = enemies[i];
if (enemy == null || enemy.statusData.isDead)
{
continue;
}
var enemyCellIndex = enemy.transData?.cellIndex;
if (!ownerCellIndex.HasValue || !enemyCellIndex.HasValue)
{
continue;
}
var distance = MapUtils.Distance(map, ownerCellIndex.Value, enemyCellIndex.Value);
if (distance <= owner.fightData.findEnemyRange &&
enemy.statusData.CheckCanBeTarget(owner, (int)owner.fightData.findEnemyRange))
{
inSightDistanceList.Add(enemy, distance);
}
}
if (inSightDistanceList.Count <= 0)
{
return null;
}
else
{
var minDistance = float.MaxValue;
GameUnit nearestEnemy = null;
foreach (var pair in inSightDistanceList)
{
if (minDistance > pair.Value)
{
minDistance = pair.Value;
nearestEnemy = pair.Key;
}
}
return nearestEnemy;
}
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.FindNearestEnemyInSightDistance error: {0}", e);
return null;
}
}
/// <summary>
/// 寻找锁敌范围内的友军
/// </summary>
public static List<GameUnit> FindFriendsInSightDistance(GameUnit owner)
{
try
{
if (owner == null) return null;
var map = LevelManager.Instance.CurrentLevel.Map;
if (map == null) return null;
friendsInSight.Clear();
var friends = GameUnitManager.instance.SearchFriends(owner);
var ownerCellIndex = owner.transData?.cellIndex;
for (var i = 0; i < friends.Count; i++)
{
var friend = friends[i];
if (friend == null || friend.GetID() == owner.GetID())
{
continue;
}
var friendCellIndex = friend.transData?.cellIndex;
if (!ownerCellIndex.HasValue || !friendCellIndex.HasValue)
{
continue;
}
var distance = MapUtils.Distance(map, ownerCellIndex.Value, friendCellIndex.Value);
if (distance <= owner.fightData.findEnemyRange)
{
friendsInSight.Add(friend);
}
}
return friendsInSight;
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.FindFriendsInSightDistance error: {0}", e);
return null;
}
}
/// <summary>
/// 寻找范围内最近的敌方npc
/// </summary>
public static GameUnit FindNearestEnemyNpcInShootRange(GameUnit owner)
{
try
{
var enemyNpc = FindEnemyNpcInSight(owner);
if (enemyNpc == null || enemyNpc.Count <= 0) return null;
GameUnit nearestEnemy = null;
var minDistance = float.MaxValue;
foreach (var pair in enemyNpc)
{
if (minDistance > pair.Value)
{
minDistance = pair.Value;
nearestEnemy = pair.Key;
}
}
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
{
if (owner == null) return Constants.INVALID_ID;
var map = LevelManager.Instance.CurrentLevel.Map;
if (map == null) return Constants.INVALID_ID;
patrolDistanceList.Clear();
var ownerCellIndex = owner.transData?.cellIndex;
for (var i = 0; i < patrolInfos.Count; i++)
{
var point = map.BlockPosition2TGSCellIndex(patrolInfos[i].patrolPoint);
if (!ownerCellIndex.HasValue || point == Constants.INVALID_ID)
{
continue;
}
patrolDistanceList.Add(i,
MapUtils.Distance(map, ownerCellIndex.Value, point));
}
var minDistance = int.MaxValue;
int nearestPoint = 0;
foreach (var pair in patrolDistanceList)
{
if (minDistance > pair.Value)
{
minDistance = pair.Value;
nearestPoint = pair.Key;
}
}
return nearestPoint;
}
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>
public static bool MoveTo(GameUnit owner, int cellIndex, bool cancelFollow = true)
{
try
{
if (owner == null || cellIndex == -1) return false;
var map = LevelManager.Instance.CurrentLevel.Map;
if (map == null) return false;
var mapData = map.MapData;
if (mapData == null) return false;
var blockData = mapData.GetBlockData(cellIndex);
if (blockData == null) return false;
var typeProperty = blockData.GetTerrainTypeProperty();
if (typeProperty == null || !typeProperty.selectable) return false;
owner.MoveTo(cellIndex, cancelFollow);
return true;
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.MoveTo error: {0}", e);
return false;
}
}
/// <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 (!MoveTo(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 void CheckAreaIsOtherEnemyUnit(GameUnit owner, uint target)
{
try
{
if (owner == null || owner.statusData.isMoveing) 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)
{
MoveOtherCell(owner);
}
else
{
var enemy = GameUnitManager.instance.infightUnits.Search(target);
if (enemy == null || enemy.statusData.isDead) return;
MoveOtherCanAttackCell(owner, enemy);
}
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.CheckAreaIsOtherEnemyUnit error: {0}", e);
}
}
/// <summary>
/// 移动至周围可到达格子
/// </summary>
private static void MoveOtherCell(GameUnit owner, int maxDistance = 2)
{
try
{
var map = LevelManager.Instance.CurrentLevel?.Map;
if (map == null || owner == null) return;
var cellIndex = owner.transData.cellIndex;
if (!AreaManager.instance.CheckContainsMoveUnit(cellIndex))
return;
for (var i = 1; i <= maxDistance; 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 MoveOtherCanAttackCell(GameUnit owner, GameUnit enemy, int maxDistance = 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 <= maxDistance; 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);
}
}
/// <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.unitLevelData.BornPosition;
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;
}
/// <summary>
/// 寻找锁敌范围内的敌方npc
/// </summary>
private static Dictionary<GameUnit, int> FindEnemyNpcInSight(GameUnit owner)
{
try
{
var enemyNpc = new Dictionary<GameUnit, int>();
var level = LevelManager.Instance.CurrentLevel;
if (level == null) return enemyNpc;
var map = LevelManager.Instance.CurrentLevel.Map;
if (map == null || level.SelfNpcLocalID == null || level.SelfNpcLocalID.Count <= 0) return enemyNpc;
var enemyList = GameUnitManager.instance.SearchEnemies(owner);
foreach (var enemy in enemyList)
{
if (enemy.unitLevelData == null) continue;
if (level.SelfNpcLocalID.Contains(enemy.unitLevelData.LocalId))
{
var distance = MapUtils.Distance(map, enemy.transData.cellIndex, owner.transData.cellIndex);
if (distance <= owner.fightData.findEnemyRange)
enemyNpc.Add(enemy, distance);
}
}
return enemyNpc;
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.FindEnemyNpcInSight error: {0}", e);
return null;
}
}
// -------------------- 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 防重合
/// <summary>
/// 避让参数
/// </summary>
public struct AvoidanceContext
{
public bool NeedAvoidCheck;
public float AvoidCheckTimer;
public float CurrentAvoidDelay;
}
private const float MIN_AVOID_DELAY = 0f;
private const float MAX_AVOID_DELAY = 1f;
/// <summary>
/// Update驱动的避让逻辑
/// </summary>
/// <param name="owner">单位</param>
/// <param name="context">避让上下文数据结构ref传入</param>
/// <param name="targetId">目标ID</param>
public static void TryAvoidOtherUnit(GameUnit owner, ref AvoidanceContext context, uint targetId)
{
if (IsNeedStop(owner)) return;
// 移动中:准备检测避让
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);
}
}
}
/// <summary>
/// 判断是否有敌人重合
/// </summary>
private static void CheckAreaHasOtherUnit(GameUnit owner, uint target)
{
try
{
if (owner == null || owner.statusData.isMoveing) 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
}
}