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

416 lines
14 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 System.Linq;
using Gameplay.Unit;
using Gameplay.Area;
using Gameplay.Level;
using cfg.CharacterCfg;
using Gameplay.Character;
using Gameplay.Pool;
using System.Collections.Generic;
namespace Gameplay.AI
{
public static partial class AIUtils
{
/// <summary>
/// 对象池 - 用于缓存单位距离字典
/// </summary>
private static readonly Dictionary<GameUnit, int> UnitsDistanceCache = new (64);
/// <summary>
/// 临时列表,用于避免在 GetNearestUnit/GetRandomUnit 中创建新列表
/// </summary>
private static readonly List<KeyValuePair<GameUnit, int>> TempPairList = new (64);
/// <summary>
/// 通过GUID获取单位
/// </summary>
public static GameUnit FindUnit(uint id)
{
if (GameUnitManager.instance.deadUnits.Contains(id))
return null;
return GameUnitManager.instance.infightUnits.Contains(id)
? GameUnitManager.instance.infightUnits.Search(id)
: null;
}
/// <summary>
/// 获取符合条件的单位及距离
/// </summary>
/// <param name="owner">自身</param>
/// <param name="candidates">需要筛选的对象</param>
/// <param name="filter">条件</param>
/// <returns></returns>
private static Dictionary<GameUnit, int> FindUnits(GameUnit owner, List<GameUnit> candidates,
IAITargetFilter filter)
{
// 清空临时字典以便重用
UnitsDistanceCache.Clear();
var ownerCell = owner.transData?.cellIndex;
if (!ownerCell.HasValue) return UnitsDistanceCache;
foreach (var unit in candidates)
{
var cell = unit.transData?.cellIndex;
if (!cell.HasValue) continue;
if (filter.IsValid(unit))
{
int distance = GetDistance(ownerCell.Value, cell.Value);
UnitsDistanceCache[unit] = distance;
}
}
return UnitsDistanceCache;
}
#region 寻找敌人
/// <summary>
/// 寻找视野内的最近敌人
/// </summary>
/// <param name="owner">自身</param>
/// <param name="range">指定范围 -1则为索敌范围</param>
/// <param name="job">指定职业</param>
/// <returns></returns>
public static GameUnit FindNearestTarget(GameUnit owner, int range = -1, Ejob job = Ejob.Job_Other)
{
try
{
var inSightDistanceDic = FindEnemiesInRange(owner, range, job);
return GetNearestUnit(inSightDistanceDic);
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.FindNearestTarget error: {0}", e);
return null;
}
}
/// <summary>
/// 寻找范围内符合条件的所有敌人
/// </summary>
public static Dictionary<GameUnit, int> FindEnemiesInRange(GameUnit owner, int range = -1,
Ejob job = Ejob.Job_Other, uint targetID = 0)
{
if (owner == null || range == 0) return null;
var enemies = GameUnitManager.instance.SearchEnemies(owner);
// 使用对象池获取EnemyFilter
var filter = ObjPoolManager.instance.Get<EnemyFilter>();
filter.Init(owner, range, job, targetID);
var result = FindUnits(owner, enemies, filter);
// 使用完毕后返回对象池
ObjPoolManager.instance.Return(filter);
// 归还临时列表
GameUnitManager.instance.ReturnTempList(enemies);
return result;
}
/// <summary>
/// 获取最近的单位
/// </summary>
public static GameUnit GetNearestUnit(Dictionary<GameUnit, int> units)
{
if (units == null || units.Count == 0)
return null;
var minDistance = float.MaxValue;
GameUnit nearestUnit = null;
foreach (var pair in units)
{
if (minDistance > pair.Value)
{
minDistance = pair.Value;
nearestUnit = pair.Key;
}
}
return nearestUnit;
}
/// <summary>
/// 获取更近的攻击来源单位
/// </summary>
public static GameUnit GetNearestAttackGameUnit(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;
}
}
#endregion
#region 寻找友军
/// <summary>
/// 通过MonsterID寻找友军 只返回第一个Monster
/// </summary>
public static GameUnit FindFriendByMonsterID(int id)
{
foreach (var unit in GameUnitManager.instance.totalUnits.unitList)
{
if (unit.unitLevelData != null && !unit.statusData.isDead && unit.unitLevelData.monsterId == id)
{
return unit;
}
}
return null;
}
/// <summary>
/// 寻找视野内的最近友军
/// </summary>
/// <param name="owner">自身</param>
/// <param name="range">指定距离 -1为锁敌距离</param>
/// <param name="job">指定职业</param>
public static GameUnit FindNearestFriend(GameUnit owner, int range = -1, Ejob job = Ejob.Job_Other)
{
try
{
var inSightDistanceDic = FindFriendsWithDistance(owner, range, job);
return GetNearestUnit(inSightDistanceDic);
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.FindNearestFriend error: {0}", e);
return null;
}
}
/// <summary>
/// 寻找锁敌范围内的友军列表
/// </summary>
public static List<GameUnit> FindFriendsInSightDistance(GameUnit owner)
{
try
{
var friends = FindFriendsWithDistance(owner);
if (friends == null || friends.Count == 0) return new List<GameUnit>();
// 避免创建新列表,使用单位字典的键集合
var result = new List<GameUnit>(friends.Keys);
return result;
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.FindFriendsInSightDistance error: {0}", e);
return null;
}
}
/// <summary>
/// 寻找符合条件的友军及距离字典
/// </summary>
public static Dictionary<GameUnit, int> FindFriendsWithDistance(GameUnit owner, int range = -1,
Ejob job = Ejob.Job_Other)
{
if (owner == null || range == 0) return null;
var friends = GameUnitManager.instance.SearchFriends(owner);
// 使用对象池获取FriendFilter
var filter = ObjPoolManager.instance.Get<FriendFilter>();
filter.Init(owner, range, job);
var result = FindUnits(owner, friends, filter);
// 使用完毕后返回对象池
ObjPoolManager.instance.Return(filter);
// 归还临时列表
GameUnitManager.instance.ReturnTempList(friends);
return result;
}
/// <summary>
/// 检测某个地格是否有友军单位
/// </summary>
public static GameUnit FindFriendByCellIndex(int cellIndex)
{
var units = AreaManager.instance.GetUnitsByCellIndex(cellIndex);
foreach (var unit in units)
{
if (unit.commonData.troopId == ETroopsId.Enemy)
{
return unit;
}
}
return null;
}
#endregion
#region 寻找随机目标
public static int FindRandomCell(GameUnit owner, int maxStep = 0)
{
try
{
var map = LevelManager.Instance.CurrentLevel?.Map;
if (map == null || owner == null) return -1;
var cellIndex = owner.transData.cellIndex;
if (!AreaManager.instance.CheckContainsMoveUnit(cellIndex))
return -1;
var randomCells = new List<int>();
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))
{
randomCells.Add(cell);
}
}
else
{
randomCells.Add(cell);
}
}
}
if (randomCells.Count == 0)
return -1;
var randomIndex = UnityEngine.Random.Range(0, randomCells.Count);
return randomCells[randomIndex];
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.FindRandomCell error: {0}", e);
return -1;
}
}
/// <summary>
/// 寻找随机单位
/// </summary>
public static GameUnit FindRandomTarget(GameUnit owner, uint enemy, int range = -1)
{
try
{
var targetID = enemy;
var inSightDistanceDic = FindEnemiesInRange(owner, range, targetID: targetID);
return GetRandomUnit(inSightDistanceDic);
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.FindRandomTarget error: {0}", e);
return null;
}
}
/// <summary>
/// 获取随机单位
/// </summary>
private static GameUnit GetRandomUnit(Dictionary<GameUnit, int> units)
{
if (units == null || units.Count == 0)
return null;
// 使用复用的临时列表避免创建新列表
TempPairList.Clear();
foreach (var pair in units)
{
TempPairList.Add(new KeyValuePair<GameUnit, int>(pair.Key, pair.Value));
}
if (TempPairList.Count == 0)
return null;
var randomIndex = UnityEngine.Random.Range(0, TempPairList.Count);
return TempPairList[randomIndex].Key;
}
#endregion
#region 组内单位 寻找 判断
public static bool GroupTargetInRange(GameUnit owner, List<int> group)
{
var inRange = false;
var groupFriends = FindGroupUnits(owner, group);
foreach (var groupFriend in groupFriends)
{
var targetID = groupFriend.unitLevelData.AIBattleInfo.Value.targetID;
if (targetID == Constants.INVALID_UINT_ID) continue;
var target = GameUnitManager.instance.infightUnits.Search(targetID);
var distance = GetDistance(owner.transData.cellIndex, target.transData.cellIndex);
if (distance <= groupFriend.fightData.trackRange)
{
inRange = true;
break;
}
}
return inRange;
}
public static List<GameUnit> FindGroupUnits(GameUnit owner, List<int> group)
{
var groupUnits = new List<GameUnit>();
if (owner == null || group == null || group.Count == 0)
return groupUnits;
var friends = GameUnitManager.instance.SearchFriends(owner);
foreach (var friend in friends)
{
if (friend.unitLevelData == null || friend.unitLevelData.AIBattleInfo == null) continue;
var unitGroups = friend.unitLevelData.AIBattleInfo.Value.groupInfo;
if (unitGroups == null || unitGroups.Count <= 0) continue;
// 检查当前单位的组ID列表与传入的组ID列表是否有交集
foreach (var groupId in unitGroups)
{
if (group.Contains(groupId))
{
groupUnits.Add(friend);
break; // 找到一个匹配就可以添加该单位无需继续检查其他组ID
}
}
}
return groupUnits;
}
#endregion
}
}