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

612 lines
22 KiB
C#

using System;
using Framework;
using Gameplay.Area;
using Gameplay.Unit;
using Gameplay.Level;
using Gameplay.Unit.Data;
using System.Collections.Generic;
using static Gameplay.Level.LevelData;
namespace Gameplay.AI
{
public static 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> enemies = new List<GameUnit>(16);
private static List<GameUnit> inAttackEnemies = new List<GameUnit>(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;
inSightEnemies.Clear();
inSightDistanceList.Clear();
GameUnitManager.instance.characterManager.SearchEnemies(owner, inSightEnemies);
var ownerCellIndex = owner.transData?.cellIndex;
for (var i = 0; i < inSightEnemies.Count; i++)
{
var enemy = inSightEnemies[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)
{
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 GameUnit FindNearestEnemyInShootRange(GameUnit owner)
{
try
{
if (owner == null) return null;
var map = LevelManager.Instance.CurrentLevel.Map;
if (map == null) return null;
inSightEnemies.Clear();
inSightDistanceList.Clear();
GameUnitManager.instance.characterManager.SearchEnemies(owner, inSightEnemies);
var ownerCellIndex = owner.transData?.cellIndex;
for (var i = 0; i < inSightEnemies.Count; i++)
{
var enemy = inSightEnemies[i];
if (enemy == null)
{
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.normalAttackDistance)
{
inSightDistanceList.Add(enemy, distance);
}
}
if (inSightDistanceList.Count <= 0)
{
return null;
}
else
{
owner.statusData.isAttackActive = true;
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.FindNearestEnemyInShootRange 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;
allFriends.Clear();
friendsInSight.Clear();
GameUnitManager.instance.characterManager.SearchFriends(owner, allFriends);
var ownerCellIndex = owner.transData?.cellIndex;
for (var i = 0; i < allFriends.Count; i++)
{
var friend = allFriends[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>
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 = new List<GameUnit>();
GameUnitManager.instance.characterManager.SearchEnemies(owner, enemyList);
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;
}
}
/// <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;
}
}
public static bool IsNeedStop(GameUnit owner)
{
return owner.statusData.isOnVehicle || owner.statusData.isChaos || owner.statusData.isStun
|| owner.statusData.isLockPos || owner.statusData.isRetreat || owner.statusData.isDead;
}
public static bool AINeedStopUpdate(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;
}
else
{
var target = FindNearestEnemyInShootRange(owner);
if (target != null)
{
owner.controlData.targetEnemy = target;
owner.statusData.isAttackActive = 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 = map.MapData.GetBlockData(cellIndex).GetTerrainTypeProperty();
if (blockData == null) return false;
if (!blockData.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);
for (int i = 0; i < units.Count; i++)
{
var unit = units[i];
if (unit.commonData.troopId == owner.commonData.troopId)
{
/*if (owner.gameunitType == EGameUnitType.Vehicle)
{
// 载具不能上载具
return;
}
// 点击的己方单位, 这里必然是载具
var vehicle = unit as NormalVehicle;
if (vehicle == null)
{
DebugUtil.LogError("不是载具");
return;
}
if (vehicle.CheckCanUp(owner))
{
owner.controlData.markVehicle = vehicle;
}
return;*/
}
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>
/// 普通撤退AI 特殊处理 避让角色
/// </summary>
public static void AvoidEnemyTarget(GameUnit owner, EventDataStartMove data)
{
try
{
if (!owner.statusData.isMoveing || data == null) return;
if (data.sender == null || data.sender.commonData.troopId == owner.commonData.troopId) return;
if (data.targetCellIndex == owner.controlData.targetCellIndex)
{
DebugUtil.Log($"AI[{owner.GetID()}] 需要再次逃离");
AvoidEnemy(owner, data.targetCellIndex);
}
}
catch (Exception e)
{
DebugUtil.LogError("AIUtils.AvoidEnemyTarget error: {0}", e);
}
}
private static void AvoidEnemy(GameUnit owner, int cellIndex)
{
try
{
var map = LevelManager.Instance.CurrentLevel.Map;
if (map == null) return;
var height = map.MapData.Height;
var width = map.MapData.Width;
var target = cellIndex;
var newArea = new List<int>()
{
-1, width, width + 1, 1, 1 - width, -width
};
foreach (var value in newArea)
{
var sum = target + value;
if (sum >= 0 && sum < height * width && PathFinder.CanPass(sum, owner))
{
DebugUtil.Log($"AI[{owner.GetID()}] 向周围移动");
owner.MoveTo(sum, true);
return;
}
}
// 无处可去
DebugUtil.Log($"AI[{owner.GetID()}] 需要回到出生点");
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 = map.BlockPosition2TGSCellIndex(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;
}
}
}
}