2024-11-04 15:24:11 +08:00
|
|
|
|
using System;
|
2024-02-06 16:15:49 +08:00
|
|
|
|
using Framework;
|
2025-03-11 13:41:53 +08:00
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using Gameplay.Unit;
|
2024-10-21 15:44:25 +08:00
|
|
|
|
using Gameplay.Area;
|
2023-10-19 15:00:19 +08:00
|
|
|
|
using Gameplay.Level;
|
2025-03-11 13:41:53 +08:00
|
|
|
|
using cfg.CharacterCfg;
|
2024-10-21 15:44:25 +08:00
|
|
|
|
using Gameplay.Unit.Data;
|
2025-03-11 13:41:53 +08:00
|
|
|
|
using Gameplay.Character;
|
|
|
|
|
|
using Gameplay.Area.Around;
|
2025-02-24 19:26:18 +08:00
|
|
|
|
using Gameplay.Vehicle.Impl;
|
2025-03-11 13:41:53 +08:00
|
|
|
|
using System.Collections.Generic;
|
2023-10-19 15:00:19 +08:00
|
|
|
|
using static Gameplay.Level.LevelData;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Gameplay.AI
|
|
|
|
|
|
{
|
2025-03-12 13:05:35 +08:00
|
|
|
|
public static partial class AIUtils
|
2023-10-19 15:00:19 +08:00
|
|
|
|
{
|
2024-10-28 17:22:30 +08:00
|
|
|
|
private static List<GameUnit> inSightEnemies = new List<GameUnit>(16);
|
|
|
|
|
|
private static Dictionary<GameUnit, int> inSightDistanceList = new Dictionary<GameUnit, int>(16);
|
2024-12-06 18:59:22 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
private static List<GameUnit> allFriends = new List<GameUnit>(16);
|
|
|
|
|
|
private static List<GameUnit> friendsInSight = new List<GameUnit>(16);
|
2024-02-06 16:15:49 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
private static Dictionary<int, int> patrolDistanceList = new Dictionary<int, int>(64);
|
2024-12-11 19:15:00 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 寻找锁敌范围内最近的敌人
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static GameUnit FindNearestEnemyInSightDistance(GameUnit owner)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
2023-10-19 15:00:19 +08:00
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
if (owner == null) return null;
|
2023-10-19 15:00:19 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
var map = LevelManager.Instance.CurrentLevel.Map;
|
|
|
|
|
|
if (map == null) return null;
|
|
|
|
|
|
inSightDistanceList.Clear();
|
2025-03-25 13:16:08 +08:00
|
|
|
|
var enemies = GameUnitManager.instance.SearchEnemies(owner);
|
2024-12-05 16:05:12 +08:00
|
|
|
|
var ownerCellIndex = owner.transData?.cellIndex;
|
2025-03-25 13:16:08 +08:00
|
|
|
|
for (var i = 0; i < enemies.Count; i++)
|
2024-02-06 16:15:49 +08:00
|
|
|
|
{
|
2025-03-25 13:16:08 +08:00
|
|
|
|
var enemy = enemies[i];
|
2024-12-05 16:05:12 +08:00
|
|
|
|
if (enemy == null || enemy.statusData.isDead)
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2024-02-06 16:15:49 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
var enemyCellIndex = enemy.transData?.cellIndex;
|
2024-02-06 16:15:49 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
if (!ownerCellIndex.HasValue || !enemyCellIndex.HasValue)
|
2023-10-19 15:00:19 +08:00
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
continue;
|
2023-10-19 15:00:19 +08:00
|
|
|
|
}
|
2024-12-09 19:11:08 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
var distance = MapUtils.Distance(map, ownerCellIndex.Value, enemyCellIndex.Value);
|
2024-12-11 16:37:01 +08:00
|
|
|
|
if (distance <= owner.fightData.findEnemyRange &&
|
2024-12-17 17:01:36 +08:00
|
|
|
|
enemy.statusData.CheckCanBeTarget(owner, (int)owner.fightData.findEnemyRange))
|
2024-12-05 16:05:12 +08:00
|
|
|
|
{
|
|
|
|
|
|
inSightDistanceList.Add(enemy, distance);
|
|
|
|
|
|
}
|
2024-11-04 15:24:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
if (inSightDistanceList.Count <= 0)
|
2024-11-04 15:24:11 +08:00
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
return null;
|
2024-11-04 15:24:11 +08:00
|
|
|
|
}
|
2024-12-05 16:05:12 +08:00
|
|
|
|
else
|
2024-11-04 15:24:11 +08:00
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
var minDistance = float.MaxValue;
|
|
|
|
|
|
GameUnit nearestEnemy = null;
|
|
|
|
|
|
foreach (var pair in inSightDistanceList)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (minDistance > pair.Value)
|
|
|
|
|
|
{
|
|
|
|
|
|
minDistance = pair.Value;
|
|
|
|
|
|
nearestEnemy = pair.Key;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return nearestEnemy;
|
2024-11-04 15:24:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-12-05 16:05:12 +08:00
|
|
|
|
catch (Exception e)
|
2024-11-04 15:24:11 +08:00
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
DebugUtil.LogError("AIUtils.FindNearestEnemyInSightDistance error: {0}", e);
|
2024-11-04 15:24:11 +08:00
|
|
|
|
return null;
|
|
|
|
|
|
}
|
2024-12-05 16:05:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 寻找锁敌范围内的友军
|
|
|
|
|
|
/// </summary>
|
2023-10-19 15:00:19 +08:00
|
|
|
|
public static List<GameUnit> FindFriendsInSightDistance(GameUnit owner)
|
|
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (owner == null) return null;
|
2024-02-06 16:15:49 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
var map = LevelManager.Instance.CurrentLevel.Map;
|
|
|
|
|
|
if (map == null) return null;
|
|
|
|
|
|
friendsInSight.Clear();
|
2025-03-25 13:16:08 +08:00
|
|
|
|
var friends = GameUnitManager.instance.SearchFriends(owner);
|
2024-12-05 16:05:12 +08:00
|
|
|
|
var ownerCellIndex = owner.transData?.cellIndex;
|
2025-03-25 13:16:08 +08:00
|
|
|
|
for (var i = 0; i < friends.Count; i++)
|
2023-10-19 15:00:19 +08:00
|
|
|
|
{
|
2025-03-25 13:16:08 +08:00
|
|
|
|
var friend = friends[i];
|
2024-12-05 16:05:12 +08:00
|
|
|
|
if (friend == null || friend.GetID() == owner.GetID())
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2023-10-19 15:00:19 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
var friendCellIndex = friend.transData?.cellIndex;
|
|
|
|
|
|
if (!ownerCellIndex.HasValue || !friendCellIndex.HasValue)
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2024-02-06 16:15:49 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
var distance = MapUtils.Distance(map, ownerCellIndex.Value, friendCellIndex.Value);
|
|
|
|
|
|
if (distance <= owner.fightData.findEnemyRange)
|
|
|
|
|
|
{
|
|
|
|
|
|
friendsInSight.Add(friend);
|
|
|
|
|
|
}
|
2023-10-19 15:00:19 +08:00
|
|
|
|
}
|
2024-02-06 16:15:49 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
return friendsInSight;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
DebugUtil.LogError("AIUtils.FindFriendsInSightDistance error: {0}", e);
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
2023-10-19 15:00:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 寻找范围内最近的敌方npc
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static GameUnit FindNearestEnemyNpcInShootRange(GameUnit owner)
|
2024-10-21 15:44:25 +08:00
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2024-10-21 15:44:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取距离自己最近的单位
|
|
|
|
|
|
/// </summary>
|
2024-02-06 16:15:49 +08:00
|
|
|
|
public static GameUnit GetNearestGameUnit(GameUnit owner, GameUnit newUnit, GameUnit unit)
|
2024-01-04 18:55:53 +08:00
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (owner == null || newUnit == null || unit == null) return null;
|
2024-02-06 16:15:49 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
var map = LevelManager.Instance.CurrentLevel.Map;
|
|
|
|
|
|
if (map == null) return null;
|
2024-02-06 16:15:49 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
var ownerCellIndex = owner.transData?.cellIndex;
|
|
|
|
|
|
var unitCellIndex = unit.transData?.cellIndex;
|
|
|
|
|
|
var newUnitCellIndex = newUnit.transData?.cellIndex;
|
|
|
|
|
|
if (!ownerCellIndex.HasValue || !unitCellIndex.HasValue || !newUnitCellIndex.HasValue) return null;
|
2024-01-04 18:55:53 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2024-01-04 18:55:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 寻找最近的巡逻点位
|
|
|
|
|
|
/// </summary>
|
2024-01-04 18:55:53 +08:00
|
|
|
|
public static int FindNearestPointInPatrolInfo(GameUnit owner, List<PatrolInfo> patrolInfos)
|
2023-10-19 15:00:19 +08:00
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (owner == null) return Constants.INVALID_ID;
|
2024-02-06 16:15:49 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
var map = LevelManager.Instance.CurrentLevel.Map;
|
|
|
|
|
|
if (map == null) return Constants.INVALID_ID;
|
2023-10-19 15:00:19 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2024-02-06 16:15:49 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
patrolDistanceList.Add(i,
|
|
|
|
|
|
MapUtils.Distance(map, ownerCellIndex.Value, point));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var minDistance = int.MaxValue;
|
|
|
|
|
|
int nearestPoint = 0;
|
|
|
|
|
|
foreach (var pair in patrolDistanceList)
|
2024-02-06 16:15:49 +08:00
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
if (minDistance > pair.Value)
|
|
|
|
|
|
{
|
|
|
|
|
|
minDistance = pair.Value;
|
|
|
|
|
|
nearestPoint = pair.Key;
|
|
|
|
|
|
}
|
2024-02-06 16:15:49 +08:00
|
|
|
|
}
|
2023-10-19 15:00:19 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
return nearestPoint;
|
2023-10-19 15:00:19 +08:00
|
|
|
|
}
|
2024-12-05 16:05:12 +08:00
|
|
|
|
catch (Exception e)
|
2023-10-19 15:00:19 +08:00
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
DebugUtil.LogError("AIUtils.FindNearestPointInPatrolInfo error: {0}", e);
|
|
|
|
|
|
// 异常返回第一个索引点
|
|
|
|
|
|
return 0;
|
2023-10-19 15:00:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 巡逻点位转换为格子索引
|
|
|
|
|
|
/// </summary>
|
2024-11-04 15:24:11 +08:00
|
|
|
|
public static int GetPatrolPointIndexFromVector2Int(Map map, int patrolIndex, List<PatrolInfo> patrolInfos)
|
|
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2024-11-04 15:24:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-20 20:21:05 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 是否停止循环逻辑
|
|
|
|
|
|
/// </summary>
|
2025-02-24 19:26:18 +08:00
|
|
|
|
public static bool AINeedStopUpdate(global::Gameplay.Level.Level level)
|
2024-11-04 15:24:11 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (level == null) return false;
|
|
|
|
|
|
return level.IsPreparing() || level.IsEnd() || Math.Abs(level.GetSpeed() - 0f) < 0.01f;
|
|
|
|
|
|
}
|
2024-12-02 18:53:43 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 激活AI攻击
|
|
|
|
|
|
/// </summary>
|
2024-07-04 12:56:53 +08:00
|
|
|
|
public static void ActiveAIAttack(GameUnit owner)
|
|
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
try
|
2024-11-04 15:24:11 +08:00
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
if (owner == null) return;
|
|
|
|
|
|
if (owner.statusData.isAttackActive) return;
|
|
|
|
|
|
var map = LevelManager.Instance.CurrentLevel.Map;
|
|
|
|
|
|
if (map == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
if (owner.controlData.targetEnemy != null)
|
2024-07-04 12:56:53 +08:00
|
|
|
|
{
|
|
|
|
|
|
owner.statusData.isAttackActive = true;
|
2025-02-17 17:11:38 +08:00
|
|
|
|
owner.statusData.isInFight = true;
|
2024-07-04 12:56:53 +08:00
|
|
|
|
}
|
2024-12-05 16:05:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
DebugUtil.LogError("AIUtils.ActiveAIAttack error: {0}", e);
|
2024-07-04 12:56:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-10-21 15:44:25 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 单位移动
|
|
|
|
|
|
/// </summary>
|
2024-11-04 15:24:11 +08:00
|
|
|
|
public static bool MoveTo(GameUnit owner, int cellIndex, bool cancelFollow = true)
|
|
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
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;
|
2024-12-10 13:07:49 +08:00
|
|
|
|
var blockData = mapData.GetBlockData(cellIndex);
|
2024-12-05 16:05:12 +08:00
|
|
|
|
if (blockData == null) return false;
|
2024-12-10 13:07:49 +08:00
|
|
|
|
var typeProperty = blockData.GetTerrainTypeProperty();
|
|
|
|
|
|
if (typeProperty == null || !typeProperty.selectable) return false;
|
2024-12-05 16:05:12 +08:00
|
|
|
|
|
|
|
|
|
|
owner.MoveTo(cellIndex, cancelFollow);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
DebugUtil.LogError("AIUtils.MoveTo error: {0}", e);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2024-11-04 15:24:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 单位撤退
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
2024-12-02 18:53:43 +08:00
|
|
|
|
public static bool MoveAway(GameUnit owner, GameUnit enemyUnit, bool cancelFollow = true)
|
|
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
try
|
2024-12-02 18:53:43 +08:00
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2024-12-02 18:53:43 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
DebugUtil.LogError("AIUtils.MoveAway error: {0}", e);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2024-12-02 18:53:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-21 15:44:25 +08:00
|
|
|
|
/// <summary>
|
2024-11-04 15:24:11 +08:00
|
|
|
|
/// AI 移动向敌人并检查
|
2024-10-21 15:44:25 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static void CheckAndMove(GameUnit owner, int cellIndex, bool cancelFollow = true)
|
|
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
try
|
2024-10-21 15:44:25 +08:00
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
if (!MoveTo(owner, cellIndex, cancelFollow)) return;
|
2024-10-21 15:44:25 +08:00
|
|
|
|
|
2024-12-17 17:01:36 +08:00
|
|
|
|
if (owner.gameunitType != EGameUnitType.Character &&
|
|
|
|
|
|
owner.gameunitType != EGameUnitType.Vehicle) return;
|
2024-12-19 16:37:23 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
var units = AreaManager.instance.GetUnitsByCellIndex(cellIndex);
|
2024-12-17 17:01:36 +08:00
|
|
|
|
foreach (var unit in units)
|
2024-12-05 16:05:12 +08:00
|
|
|
|
{
|
2024-12-19 16:37:23 +08:00
|
|
|
|
if (unit.commonData.troopId == owner.commonData.troopId)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
2024-10-21 15:44:25 +08:00
|
|
|
|
{
|
2024-12-19 16:37:23 +08:00
|
|
|
|
// 如果是敌人, 则设置敌人目标
|
|
|
|
|
|
if (unit.statusData.CheckCanBeTarget(owner, -1))
|
2024-12-05 16:05:12 +08:00
|
|
|
|
{
|
|
|
|
|
|
owner.controlData.markEnemy = unit;
|
|
|
|
|
|
owner.controlData.forceUpdateTarget = true;
|
2024-12-19 16:37:23 +08:00
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2024-10-21 15:44:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
// 走到这里,说明没有目标,清空标记
|
|
|
|
|
|
owner.controlData.markVehicle = null;
|
2024-12-06 18:59:22 +08:00
|
|
|
|
owner.controlData.markEnemy = null;
|
2024-12-05 16:05:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
DebugUtil.LogError("AIUtils.CheckAndMove error: {0}", e);
|
|
|
|
|
|
}
|
2024-10-21 15:44:25 +08:00
|
|
|
|
}
|
2024-12-02 18:53:43 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2024-12-20 19:56:53 +08:00
|
|
|
|
/// 判断是否有敌人重合
|
2024-12-02 18:53:43 +08:00
|
|
|
|
/// </summary>
|
2024-12-20 19:56:53 +08:00
|
|
|
|
public static void CheckAreaIsOtherEnemyUnit(GameUnit owner, uint target)
|
2024-12-02 18:53:43 +08:00
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
2024-12-20 19:56:53 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2024-12-02 18:53:43 +08:00
|
|
|
|
|
2024-12-20 19:56:53 +08:00
|
|
|
|
if (!hasOther) return;
|
|
|
|
|
|
if (target == Constants.INVALID_UINT_ID)
|
2024-12-05 16:05:12 +08:00
|
|
|
|
{
|
2024-12-20 19:56:53 +08:00
|
|
|
|
MoveOtherCell(owner);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
var enemy = GameUnitManager.instance.infightUnits.Search(target);
|
|
|
|
|
|
if (enemy == null || enemy.statusData.isDead) return;
|
|
|
|
|
|
MoveOtherCanAttackCell(owner, enemy);
|
2024-12-05 16:05:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
2024-12-02 18:53:43 +08:00
|
|
|
|
{
|
2024-12-20 19:56:53 +08:00
|
|
|
|
DebugUtil.LogError("AIUtils.CheckAreaIsOtherEnemyUnit error: {0}", e);
|
2024-12-02 18:53:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-09 19:11:08 +08:00
|
|
|
|
/// <summary>
|
2024-12-20 19:56:53 +08:00
|
|
|
|
/// 移动至周围可到达格子
|
2024-12-09 19:11:08 +08:00
|
|
|
|
/// </summary>
|
2024-12-20 19:56:53 +08:00
|
|
|
|
private static void MoveOtherCell(GameUnit owner, int maxDistance = 2)
|
2024-12-09 19:11:08 +08:00
|
|
|
|
{
|
2024-12-20 19:56:53 +08:00
|
|
|
|
try
|
2024-12-09 19:11:08 +08:00
|
|
|
|
{
|
2024-12-20 19:56:53 +08:00
|
|
|
|
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);
|
2024-12-09 19:11:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-20 19:56:53 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 移动至周围可攻击格子
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private static void MoveOtherCanAttackCell(GameUnit owner, GameUnit enemy, int maxDistance = 2)
|
2024-12-02 18:53:43 +08:00
|
|
|
|
{
|
2024-12-05 16:05:12 +08:00
|
|
|
|
try
|
2024-12-02 18:53:43 +08:00
|
|
|
|
{
|
2024-12-20 19:56:53 +08:00
|
|
|
|
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++)
|
2024-12-02 18:53:43 +08:00
|
|
|
|
{
|
2024-12-20 19:56:53 +08:00
|
|
|
|
var minSteps = i - 1;
|
|
|
|
|
|
var maxSteps = i;
|
|
|
|
|
|
var cells = map.TerrainGridSystem.CellGetNeighboursWithinRange(cellIndex, minSteps, maxSteps,
|
|
|
|
|
|
canCrossCheckType: TGS.CanCrossCheckType.IgnoreCanCrossCheckOnAllCells);
|
|
|
|
|
|
foreach (var cell in cells)
|
2024-12-05 16:05:12 +08:00
|
|
|
|
{
|
2024-12-20 19:56:53 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2024-12-05 16:05:12 +08:00
|
|
|
|
}
|
2024-12-02 18:53:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-05 16:05:12 +08:00
|
|
|
|
// 无处可去
|
|
|
|
|
|
owner.MoveTo(owner.transData.bornCellIndex, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
DebugUtil.LogError("AIUtils.AvoidEnemy error: {0}", e);
|
|
|
|
|
|
}
|
2024-12-02 18:53:43 +08:00
|
|
|
|
}
|
2024-12-04 18:49:20 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 计算当前位置与出生点位置是否超过防守距离
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static bool CheckDistanceIsOver(GameUnit owner)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2024-12-06 18:59:22 +08:00
|
|
|
|
if (owner == null) return true;
|
2024-12-04 18:49:20 +08:00
|
|
|
|
var map = LevelManager.Instance.CurrentLevel.Map;
|
2024-12-06 18:59:22 +08:00
|
|
|
|
if (map == null) return true;
|
2024-12-04 18:49:20 +08:00
|
|
|
|
var curPos = owner.transData.cellIndex;
|
2024-12-17 15:32:55 +08:00
|
|
|
|
var bornPos = owner.unitLevelData.BornPosition;
|
2024-12-04 18:49:20 +08:00
|
|
|
|
var distance = MapUtils.Distance(map, curPos, bornPos);
|
2024-12-06 18:59:22 +08:00
|
|
|
|
return owner.unitLevelData.DefensiveDistance < distance;
|
2024-12-04 18:49:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
DebugUtil.LogError("AIUtils.CheckDistanceIsOver error: {0}", e);
|
2024-12-06 18:59:22 +08:00
|
|
|
|
// 默认超出
|
|
|
|
|
|
return true;
|
2024-12-04 18:49:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-12-11 17:46:21 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2024-12-20 14:52:23 +08:00
|
|
|
|
/// 顺时针获取周围第一个能够到达的一圈格子
|
2024-12-11 17:46:21 +08:00
|
|
|
|
/// </summary>
|
2024-12-20 14:52:23 +08:00
|
|
|
|
public static int GetCanReachCellAround(Map map, GameUnit owner, int centerIndex, int step = 1)
|
2024-12-11 17:46:21 +08:00
|
|
|
|
{
|
2024-12-20 14:52:23 +08:00
|
|
|
|
var cellList = GetAroundCells(centerIndex, step);
|
|
|
|
|
|
foreach (var cellIndex in cellList)
|
|
|
|
|
|
{
|
|
|
|
|
|
var blockData = map.GetBlockData(cellIndex);
|
2024-12-11 17:46:21 +08:00
|
|
|
|
if (blockData == null) continue;
|
|
|
|
|
|
var typeProperty = blockData.GetTerrainTypeProperty();
|
|
|
|
|
|
if (typeProperty == null) continue;
|
2024-12-20 14:52:23 +08:00
|
|
|
|
if (CheckPathExits(owner, cellIndex))
|
2024-12-11 17:46:21 +08:00
|
|
|
|
{
|
2024-12-20 14:52:23 +08:00
|
|
|
|
return cellIndex;
|
2024-12-11 17:46:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-20 14:52:23 +08:00
|
|
|
|
return -1;
|
2024-12-11 17:46:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-17 17:01:36 +08:00
|
|
|
|
/// <summary>
|
2024-12-20 14:52:23 +08:00
|
|
|
|
/// 检测是否存在路径
|
2024-12-17 17:01:36 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static bool CheckPathExits(GameUnit owner, int cellIndex)
|
|
|
|
|
|
{
|
|
|
|
|
|
var map = LevelManager.Instance.CurrentLevel.Map;
|
2024-12-20 14:52:23 +08:00
|
|
|
|
if (owner == null || cellIndex == -1 || map == null) return false;
|
2024-12-17 17:01:36 +08:00
|
|
|
|
var path = PathFinder.FindPath(map, cellIndex, owner);
|
|
|
|
|
|
return path != null && path.Count > 0;
|
|
|
|
|
|
}
|
2024-12-20 14:52:23 +08:00
|
|
|
|
|
2024-12-25 14:51:58 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 设置cellIndex周围的通行级别
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>Dic(格子,原来的通行级别)</returns>
|
2024-12-26 19:39:22 +08:00
|
|
|
|
public static Dictionary<int, int> SetCellCrossLevel(Map map, GameUnit owner, int cellIndex, int step = 1)
|
2024-12-25 14:51:58 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (map == null || owner == null) return null;
|
2024-12-26 19:39:22 +08:00
|
|
|
|
var cells = new Dictionary<int, int>();
|
2024-12-25 14:51:58 +08:00
|
|
|
|
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);
|
2024-12-26 19:39:22 +08:00
|
|
|
|
cells[cell] = oldCrossLevel;
|
|
|
|
|
|
//DebugUtil.LogWarning("设置地格{0}的通行级别为:{1}", cell, crossLevel);
|
2024-12-25 14:51:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return cells;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 设置cellList通行级别
|
|
|
|
|
|
/// </summary>
|
2024-12-26 19:39:22 +08:00
|
|
|
|
public static void SetCellCrossLevel(Map map, Dictionary<int, int> cells)
|
2024-12-25 14:51:58 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (map == null) return;
|
|
|
|
|
|
foreach (var cell in cells)
|
|
|
|
|
|
{
|
2024-12-26 19:39:22 +08:00
|
|
|
|
var blockData = map.GetBlockData(cell.Key);
|
2024-12-25 14:51:58 +08:00
|
|
|
|
if (blockData == null) continue;
|
2024-12-26 19:39:22 +08:00
|
|
|
|
map.GetRuntimeBlockProperty(cell.Key, out var data);
|
|
|
|
|
|
data.crossLevel = cell.Value;
|
|
|
|
|
|
map.SetRuntimeBlockProperty(cell.Key, data);
|
|
|
|
|
|
//DebugUtil.LogError("还原格子:{0}到{1}", cell.Key, cell.Value);
|
2024-12-25 14:51:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-20 14:52:23 +08:00
|
|
|
|
|
2024-12-20 19:56:53 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获得周围格子
|
|
|
|
|
|
/// </summary>
|
2024-12-20 14:52:23 +08:00
|
|
|
|
private static List<int> GetAroundCells(int centerIndex, int step = 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
var cellList = new List<int>();
|
|
|
|
|
|
AroundHelper.GetAround(centerIndex, step, cellList);
|
|
|
|
|
|
return cellList;
|
|
|
|
|
|
}
|
2024-12-20 20:21:05 +08:00
|
|
|
|
|
|
|
|
|
|
/// <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;
|
2025-03-25 13:16:08 +08:00
|
|
|
|
var enemyList = GameUnitManager.instance.SearchEnemies(owner);
|
2024-12-20 20:21:05 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-02-17 17:11:38 +08:00
|
|
|
|
|
2025-02-24 19:26:18 +08:00
|
|
|
|
// -------------------- New -------------------- //
|
2025-03-12 13:05:35 +08:00
|
|
|
|
|
2025-02-24 19:26:18 +08:00
|
|
|
|
#region 寻找敌人
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 寻找视野内的最近敌人 range为-1则为索敌范围
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
2025-02-18 19:14:31 +08:00
|
|
|
|
}
|
2025-02-17 17:11:38 +08:00
|
|
|
|
|
2025-02-18 19:14:31 +08:00
|
|
|
|
/// <summary>
|
2025-03-11 16:09:51 +08:00
|
|
|
|
/// 寻找范围内符合条件的所有敌人
|
2025-02-18 19:14:31 +08:00
|
|
|
|
/// </summary>
|
2025-02-21 19:57:38 +08:00
|
|
|
|
public static Dictionary<GameUnit, int> FindEnemiesInRange(GameUnit owner, int range = -1,
|
|
|
|
|
|
Ejob job = Ejob.Job_Other, uint targetID = 0)
|
2025-02-18 19:14:31 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (owner == null || range == 0) return null;
|
2025-02-17 17:11:38 +08:00
|
|
|
|
|
2025-03-25 13:16:08 +08:00
|
|
|
|
var enemies = GameUnitManager.instance.SearchEnemies(owner);
|
2025-02-18 14:25:54 +08:00
|
|
|
|
|
2025-03-11 16:09:51 +08:00
|
|
|
|
var filter = new EnemyFilter(owner, range, job, targetID);
|
|
|
|
|
|
return AITargetFinder.FindUnits(owner, enemies, filter);
|
2025-02-18 19:14:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-21 19:57:38 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取最近的单位
|
|
|
|
|
|
/// </summary>
|
2025-02-18 19:14:31 +08:00
|
|
|
|
public static GameUnit GetNearestUnit(Dictionary<GameUnit, int> units)
|
|
|
|
|
|
{
|
|
|
|
|
|
var minDistance = float.MaxValue;
|
|
|
|
|
|
GameUnit nearestUnit = null;
|
|
|
|
|
|
foreach (var pair in units)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (minDistance > pair.Value)
|
|
|
|
|
|
{
|
|
|
|
|
|
minDistance = pair.Value;
|
|
|
|
|
|
nearestUnit = pair.Key;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return nearestUnit;
|
|
|
|
|
|
}
|
2025-02-28 14:09:10 +08:00
|
|
|
|
|
2025-02-26 19:11:11 +08:00
|
|
|
|
/// <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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-24 19:26:18 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region 寻找友军
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
2025-02-25 16:02:20 +08:00
|
|
|
|
|
2025-02-25 14:06:22 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 寻找视野内的最近敌人 range为-1则为索敌范围
|
|
|
|
|
|
/// </summary>
|
2025-02-25 16:02:20 +08:00
|
|
|
|
public static GameUnit FindNearestFriend(GameUnit owner, int range = -1, Ejob job = Ejob.Job_Other)
|
2025-02-25 14:06:22 +08:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-02-25 16:02:20 +08:00
|
|
|
|
var inSightDistanceDic = FindFriendInRange(owner, range, job);
|
2025-02-25 14:06:22 +08:00
|
|
|
|
return GetNearestUnit(inSightDistanceDic);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
DebugUtil.LogError("AIUtils.FindNearestFriend error: {0}", e);
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-02-24 19:26:18 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2025-03-11 16:09:51 +08:00
|
|
|
|
/// 寻找范围内符合条件的友军
|
2025-02-24 19:26:18 +08:00
|
|
|
|
/// </summary>
|
2025-02-25 16:02:20 +08:00
|
|
|
|
public static Dictionary<GameUnit, int> FindFriendInRange(GameUnit owner, int range = -1,
|
|
|
|
|
|
Ejob job = Ejob.Job_Other)
|
2025-02-24 19:26:18 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (owner == null || range == 0) return null;
|
|
|
|
|
|
|
2025-03-25 13:16:08 +08:00
|
|
|
|
var friends = GameUnitManager.instance.SearchFriends(owner);
|
2025-02-24 19:26:18 +08:00
|
|
|
|
|
2025-03-21 15:22:32 +08:00
|
|
|
|
var filter = new FriendFilter(owner, range, job);
|
2025-03-11 16:09:51 +08:00
|
|
|
|
return AITargetFinder.FindUnits(owner, friends, filter);
|
2025-02-24 19:26:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <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 寻找随机目标
|
|
|
|
|
|
|
2025-02-25 14:06:22 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-24 19:26:18 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 寻找随机单位
|
|
|
|
|
|
/// </summary>
|
2025-02-21 19:57:38 +08:00
|
|
|
|
public static GameUnit FindRandomTarget(GameUnit owner, uint enemy, int range = -1)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-02-24 19:26:18 +08:00
|
|
|
|
var targetID = enemy;
|
2025-02-21 19:57:38 +08:00
|
|
|
|
var inSightDistanceDic = FindEnemiesInRange(owner, range, targetID: targetID);
|
|
|
|
|
|
|
|
|
|
|
|
return GetRandomUnit(inSightDistanceDic);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
DebugUtil.LogError("AIUtils.FindRandomTarget error: {0}", e);
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-24 19:26:18 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取随机单位
|
|
|
|
|
|
/// </summary>
|
2025-02-21 19:57:38 +08:00
|
|
|
|
private static GameUnit GetRandomUnit(Dictionary<GameUnit, int> units)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (units == null || units.Count == 0)
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
|
|
var unitList = units.Keys.ToList();
|
|
|
|
|
|
var randomUnit = unitList[UnityEngine.Random.Range(0, unitList.Count)];
|
|
|
|
|
|
return randomUnit;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-24 19:26:18 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region 载具相关
|
|
|
|
|
|
|
2025-02-18 14:25:54 +08:00
|
|
|
|
/// <summary>
|
2025-02-24 19:26:18 +08:00
|
|
|
|
/// 寻找周围可以到达的格子 (用于载具下人)
|
2025-02-18 14:25:54 +08:00
|
|
|
|
/// </summary>
|
2025-02-24 19:26:18 +08:00
|
|
|
|
public static int FindCanReachCell(int cellIndex, GameUnit unit, int maxStep = 2)
|
2025-02-18 14:25:54 +08:00
|
|
|
|
{
|
2025-02-24 19:26:18 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2025-02-18 14:25:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-18 19:14:31 +08:00
|
|
|
|
/// <summary>
|
2025-02-24 19:26:18 +08:00
|
|
|
|
/// 寻找最近的载具
|
2025-02-18 19:14:31 +08:00
|
|
|
|
/// </summary>
|
2025-02-24 19:26:18 +08:00
|
|
|
|
public static NormalVehicle FindNearestVehicle(GameUnit owner)
|
2025-02-18 14:25:54 +08:00
|
|
|
|
{
|
2025-02-24 19:26:18 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2025-02-19 13:44:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-24 19:26:18 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region 巡逻相关
|
|
|
|
|
|
|
2025-02-19 13:44:13 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 寻找最近的巡逻点位
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static int FindNearestPatrolInfoIndex(GameUnit owner, List<PatrolInfo> patrolInfos)
|
|
|
|
|
|
{
|
2025-03-12 13:05:35 +08:00
|
|
|
|
return NearestSearchUtils.FindNearestIndex(owner, patrolInfos, patrolInfo =>
|
2025-02-19 13:44:13 +08:00
|
|
|
|
{
|
2025-03-12 13:05:35 +08:00
|
|
|
|
var map = LevelManager.Instance.CurrentLevel?.Map;
|
|
|
|
|
|
return map?.BlockPosition2TGSCellIndex(patrolInfo.patrolPoint) ?? Constants.INVALID_ID;
|
|
|
|
|
|
});
|
2025-02-19 13:44:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 巡逻点位转换为格子索引
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static int GetPatrolPointIndex(int patrolIndex, List<PatrolInfo> patrolInfos)
|
|
|
|
|
|
{
|
2025-03-12 13:05:35 +08:00
|
|
|
|
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);
|
2025-02-19 13:44:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-25 14:06:22 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 寻找最近的路点点位
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static int FindNearestRoadInfoIndex(GameUnit owner, List<int> cells)
|
|
|
|
|
|
{
|
2025-03-12 13:05:35 +08:00
|
|
|
|
return NearestSearchUtils.FindNearestIndex(owner, cells, cellIndex => cellIndex);
|
2025-02-25 14:06:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-19 13:44:13 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取点位等待时间
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static float GetPatrolPointWaitTime(int patrolIndex, List<PatrolInfo> patrolInfos)
|
|
|
|
|
|
{
|
2025-03-12 13:05:35 +08:00
|
|
|
|
if (patrolIndex < 0 || patrolIndex >= patrolInfos.Count) return 0;
|
|
|
|
|
|
return patrolInfos[patrolIndex]?.waitTime ?? 0;
|
2025-02-19 13:44:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-17 17:11:38 +08:00
|
|
|
|
#endregion
|
2025-02-21 17:03:56 +08:00
|
|
|
|
|
|
|
|
|
|
#region 防重合
|
|
|
|
|
|
|
2025-03-11 13:41:53 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 避让参数
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public struct AvoidanceContext
|
|
|
|
|
|
{
|
2025-03-11 16:09:51 +08:00
|
|
|
|
public bool NeedAvoidCheck;
|
|
|
|
|
|
public float AvoidCheckTimer;
|
|
|
|
|
|
public float CurrentAvoidDelay;
|
2025-03-11 13:41:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
// 移动中:准备检测避让
|
2025-03-11 16:09:51 +08:00
|
|
|
|
if (owner.statusData.isMoveing && !context.NeedAvoidCheck)
|
2025-03-11 13:41:53 +08:00
|
|
|
|
{
|
2025-03-11 16:09:51 +08:00
|
|
|
|
context.NeedAvoidCheck = true;
|
|
|
|
|
|
context.CurrentAvoidDelay = UnityEngine.Random.Range(MIN_AVOID_DELAY, MAX_AVOID_DELAY);
|
|
|
|
|
|
context.AvoidCheckTimer = 0f;
|
2025-03-11 13:41:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 移动结束:计时触发检测
|
2025-03-11 16:09:51 +08:00
|
|
|
|
if (context.NeedAvoidCheck && !owner.statusData.isMoveing && owner.controlData.targetCellIndex == -1)
|
2025-03-11 13:41:53 +08:00
|
|
|
|
{
|
2025-03-11 16:09:51 +08:00
|
|
|
|
context.AvoidCheckTimer += Time.deltaTime;
|
2025-03-11 13:41:53 +08:00
|
|
|
|
|
2025-03-11 16:09:51 +08:00
|
|
|
|
if (context.AvoidCheckTimer >= context.CurrentAvoidDelay)
|
2025-03-11 13:41:53 +08:00
|
|
|
|
{
|
2025-03-11 16:09:51 +08:00
|
|
|
|
context.NeedAvoidCheck = false;
|
2025-03-11 13:41:53 +08:00
|
|
|
|
CheckAreaHasOtherUnit(owner, targetId);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-21 17:03:56 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 判断是否有敌人重合
|
|
|
|
|
|
/// </summary>
|
2025-03-11 16:09:51 +08:00
|
|
|
|
private static void CheckAreaHasOtherUnit(GameUnit owner, uint target)
|
2025-02-21 17:03:56 +08:00
|
|
|
|
{
|
|
|
|
|
|
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>
|
2025-02-28 14:09:10 +08:00
|
|
|
|
public static void MoveToOtherCell(GameUnit owner, int centerCell = -1, int maxStep = 2)
|
2025-02-21 17:03:56 +08:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var map = LevelManager.Instance.CurrentLevel?.Map;
|
|
|
|
|
|
if (map == null || owner == null) return;
|
2025-02-28 14:09:10 +08:00
|
|
|
|
var cellIndex = centerCell == -1 ? owner.transData.cellIndex : centerCell;
|
2025-02-21 17:03:56 +08:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-24 19:26:18 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 移动至可以攻击到目标的周围格子
|
|
|
|
|
|
/// </summary>
|
2025-02-21 17:03:56 +08:00
|
|
|
|
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
|
2025-02-24 19:26:18 +08:00
|
|
|
|
|
|
|
|
|
|
#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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-28 14:09:10 +08:00
|
|
|
|
/// <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;
|
|
|
|
|
|
}
|
2025-03-12 14:24:39 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 范围限制
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static int ClampCoord(int value, int min, int max) => Mathf.Clamp(value, min, max);
|
2025-02-28 14:09:10 +08:00
|
|
|
|
|
2025-02-24 19:26:18 +08:00
|
|
|
|
#endregion
|
2023-10-19 15:00:19 +08:00
|
|
|
|
}
|
2024-02-06 16:15:49 +08:00
|
|
|
|
}
|