using cfg.AreaCfg; using Framework; using Gameplay.Level; using System; using System.Collections.Generic; using Gameplay.Area.Around; using Gameplay.Area.AttackArea; using Gameplay.Area.Ready; using Gameplay.Character; using Gameplay.Common; using Gameplay.Skill; using Gameplay.Unit; using Gameplay.Unit.Data; using UnityEngine; namespace Gameplay.Area { using Skill; public class AreaManager { public static bool ENABLE_LOG = false; private static AreaManager _instance; public static AreaManager instance { get { return _instance; } } public static void CreateInstance(Map map) { if (_instance != null) { DebugUtil.LogError("请勿重复创建AreaManager"); return; } _instance = new AreaManager(map); } private readonly Dictionary> _areaDict; // public readonly SkillAreaSelector skillAreaSelector; public readonly SkillAreaSelector2 skillAreaSelector2; public readonly PlayerSkillAreaSelector playerSkillAreaSelector; internal readonly Dictionary> _cellIndex2Units; private Map _map; public readonly AttackAreaView attackArea; public readonly ReadyAreaManager readyAreaManager; public readonly AttackAreaManager attackAreaManager; public readonly MoveAreaView moveAreaView; // public SkillAreaSelecter skillAreaSelecter => _skillAreaSelecter; private Dictionary _aroundCellDatas = new Dictionary(); private Dictionary _cellIndexToWorldPosCache = new Dictionary(); public bool isInSkillPreview { get { return skillAreaSelector2.isInPreview || playerSkillAreaSelector.isInPreview; } } public ParentGroup parentGroup = new ParentGroup(); private readonly IEventHandler _eventHandler; private Queue> _cellIndexListRing; private Dictionary _distanceCache; private AreaManager(Map map) { _areaDict = new Dictionary>(); skillAreaSelector2 = new SkillAreaSelector2(); playerSkillAreaSelector = new PlayerSkillAreaSelector(); _cellIndex2Units = new Dictionary>(); attackArea = new AttackAreaView(); _map = map; readyAreaManager = new ReadyAreaManager(_map); attackAreaManager = new AttackAreaManager(); moveAreaView = new MoveAreaView(); _eventHandler = new AreaEventHandler(this); } public void Init() { _InitMathV3(); parentGroup.Init(); moveAreaView.Init(_map); attackArea.Init(); readyAreaManager.Init(); attackAreaManager.Init(); _eventHandler.RegisterEvents(); _cellIndexListRing = new Queue>(); for (int i = 0; i < 10; ++i) { var list = new List(); _cellIndexListRing.Enqueue(list); } _distanceCache = new Dictionary(); } public void Dispose() { readyAreaManager.Dispose(); attackAreaManager.Dispose(); moveAreaView.Dispose(); playerSkillAreaSelector.Dispose(); parentGroup.Dispose(); _aroundCellDatas.Clear(); _eventHandler.Dispose(); _instance = null; } internal void _AddUnitToCellIndex(int cellIndex, GameUnit unit) { if (_cellIndex2Units.TryGetValue(cellIndex, out var unitList)) { unitList.Add(unit); } else { unitList = new List(); unitList.Add(unit); _cellIndex2Units.Add(cellIndex, unitList); } if (CodeDefine.ENABLE_FIGHT_NORMAL_ATTACK_LOG) { _Log($"add unit:{unit.GetID()} to cellIndex:{cellIndex}"); } } internal void _OnUnitRemove(GameUnit unit) { var cellIndex = unit.transData.cellIndex; if (_cellIndex2Units.TryGetValue(cellIndex, out var unitList)) { unitList.Remove(unit); } } private static void _Log(string str) { if (!ENABLE_LOG) return; DebugUtil.Log("[AreaManager] {0}", str); } public DataArea GetConfigById(int areaId) { return TableManager.Instance.Tables.AreaConfig.Get(areaId); } private List _emptyList = new List(); public List GetCellIndexsAround(int centerCellIndex, int distance, bool containsCenter = true) { var key = AroundCellData.GetKey(centerCellIndex, distance, containsCenter); if (_aroundCellDatas.TryGetValue(key, out var data)) { return data.aroundCellIndexs; } var list = _GetCellIndexAround(centerCellIndex, distance, containsCenter); var aroundData = new AroundCellData(centerCellIndex, distance, containsCenter); aroundData.aroundCellIndexs = list; _aroundCellDatas.Add(key, aroundData); return list; } private List _GetCellIndexAround(int centerCellIndex, int distance, bool containsCenter = true) { if (distance <= 0) { if (containsCenter) { return new List() { centerCellIndex }; } else { return _emptyList; } } var list = new List(); if (containsCenter) { list.Insert(0, centerCellIndex); } AroundHelper.GetAround(centerCellIndex, distance, list); return list; } public bool IsCellEmpty(int cellIndex) { var unitList = GetUnitsByCellIndex(cellIndex); return unitList.Count == 0; } public int GetDistance(int cellIndex1, int cellIndex2) { if (cellIndex1 == cellIndex2) { return 0; } var key = cellIndex1 * 10000 + cellIndex2; if (_distanceCache.TryGetValue(key, out var distance)) { return distance; } var newDistance = _map.TerrainGridSystem.CellGetHexagonDistance(cellIndex1, cellIndex2); _distanceCache.Add(key, newDistance); var invertedKey = cellIndex2 * 10000 + cellIndex1; _distanceCache.Add(invertedKey, newDistance); return newDistance; // 实测TGS的方法性能更好 0.66 vs 0.8 // var mathPoint1 = AroundHelper.GetMathPosV3FromTable(cellIndex1); // var mathPoint2 = AroundHelper.GetMathPosV3FromTable(cellIndex2); // var offsetX = Mathf.Abs(mathPoint1.x - mathPoint2.x); // var offsetY = Mathf.Abs(mathPoint1.y - mathPoint2.y); // var offsetZ = Mathf.Abs(mathPoint1.z - mathPoint2.z); // return Mathf.Max(offsetX, Mathf.Max(offsetY, offsetZ)); // return _map.TerrainGridSystem.CellGetHexagonDistance(cellIndex1, cellIndex2); } public Vector3 GetWorldPosByIndex(int cellIndex) { // Check if the position is already cached if (_cellIndexToWorldPosCache.TryGetValue(cellIndex, out Vector3 cachedPos)) { return cachedPos; } // Calculate and cache the position if not already present var getPos = _map.TerrainGridSystem.CellGetPosition(cellIndex); _cellIndexToWorldPosCache[cellIndex] = getPos; return getPos; } public int GetCellIndexByPos(Vector3 pos) { if (_map.WorldPosition2TGSCellIndex(pos, out var result)) { return result; } DebugUtil.LogError("无法获取到CellIndex:" + pos); return -1; } /// /// 注意: 返回的list 是循环使用的 /// /// /// /// public List GetCellIndexs(int centerCellIndex, int areaId) { var getList = _cellIndexListRing.Dequeue(); _cellIndexListRing.Enqueue(getList); getList.Clear(); GetCellIndexs(centerCellIndex, areaId, getList); return getList; } public void GetCellIndexs(int centerCellIndex, int areaId, List list) { var map = LevelManager.Instance.CurrentLevel.Map; var posVec = map.TGSCellIndex2BlockPosition(centerCellIndex); var offsetList = _GetOffsetList(areaId); for (var i = 0; i < offsetList.Count; ++i) { var offset = offsetList[i]; var calcPos = posVec + offset; if (posVec.x % 2 == 1 && calcPos.x % 2 == 0) { calcPos.y += 1; } var cellIndex = map.BlockPosition2TGSCellIndex(calcPos); if (cellIndex == -1) { continue; } var getSuccess = map.GetRuntimeBlockProperty(cellIndex, out var blockData); if (!getSuccess) { continue; } if (blockData.selectable == false) { continue; } list.Add(cellIndex); } } private List _GetOffsetList(int areaId) { if (_areaDict.TryGetValue(areaId, out var offsetList)) { return offsetList; } else { // 不存在,现场进行加载 var areaData = GetConfigById(areaId); if (areaData == null) { DebugUtil.LogError("AreaId:{0}不存在", areaId); return null; } offsetList = new List(); var intList = areaData.Points; for (int i = 0; i < intList.Count; i += 2) { offsetList.Add(new Vector2Int(intList[i], intList[i + 1])); } _areaDict.Add(areaId, offsetList); return offsetList; } } public int SearchNearCanClaimCellIndexWithAttack(GameUnit searchUnit, int maxDistance = 2) { if (searchUnit.controlData.targetEnemy == null) { return SearchNearCanClaimCellIndex(searchUnit, maxDistance); } var cellIndex = searchUnit.transData.cellIndex; if (!CheckContainsMoveUnit(cellIndex)) { return cellIndex; } var enemy = searchUnit.controlData.targetEnemy; var enemyCellIndex = enemy.transData.cellIndex; for (int i = 1; i <= maxDistance; i++) { var minSteps = i - 1; var maxSteps = i; var list = _map.TerrainGridSystem.CellGetNeighboursWithinRange(cellIndex, minSteps, maxSteps, canCrossCheckType: TGS.CanCrossCheckType.IgnoreCanCrossCheckOnAllCells); for (int j = 0; j < list.Count; j++) { // 先判断是否可到达 var checkIndex = list[j]; if (!CheckMapCellAvailable(checkIndex)) { continue; } if (!PathFinder.CanBasePass(cellIndex, searchUnit)) { continue; } if (!CheckContainsMoveUnit(checkIndex)) { // 在判断该位置是否可攻击 var searchArea = attackAreaManager.GetAttackArea(checkIndex, searchUnit.fightData.attackDistance); if (searchArea.canAttackCells.Contains(enemyCellIndex)) { if (maxSteps > 1) { // 大于1之后需要额外判断一下是否有路径 var path = PathFinder.FindPath(_map, checkIndex, searchUnit); if (path.Count > 0) { return checkIndex; } } else { return checkIndex; } } } } } DebugUtil.LogG("没有找到可用的位置,返回中心位置"); // 没有找到,返回原来的 return cellIndex; } public int SearchNearCanClaimCellIndex(GameUnit searchUnit, int maxDistance = 2) { var cellIndex = searchUnit.transData.cellIndex; if (!CheckContainsMoveUnit(cellIndex)) { return cellIndex; } cellIndex = SearchNearCanClaimCellIndex(cellIndex, searchUnit, maxDistance); // 没有找到,返回原来的 return cellIndex; } public int SearchNearCanClaimCellIndex(int startCellIndex, GameUnit searchUnit, int maxDistance = 2) { var cellIndex = startCellIndex; for (int i = 1; i <= maxDistance; i++) { var minSteps = i - 1; var maxSteps = i; var list = _map.TerrainGridSystem.CellGetNeighboursWithinRange(cellIndex, minSteps, maxSteps, canCrossCheckType: TGS.CanCrossCheckType.IgnoreCanCrossCheckOnAllCells); for (int j = 0; j < list.Count; j++) { var checkIndex = list[j]; if (!CheckMapCellAvailable(checkIndex)) { continue; } if (!PathFinder.CanBasePass(cellIndex, searchUnit)) { continue; } if (CheckContainsMoveUnit(checkIndex)) { continue; } if (maxSteps > 1) { // 大于1之后需要额外判断一下是否有路径 var path = PathFinder.FindPath(_map, checkIndex, searchUnit); if (path.Count > 0) { return cellIndex; } } else { return checkIndex; } } } DebugUtil.LogG("没有找到可用的位置,返回初始位置"); return cellIndex; } public bool CheckContainsMoveEnemy(int cellIndex) { var allUnits = GameUnitManager.instance.infightUnits.unitList; for (int i = 0; i < allUnits.Count; i++) { var unit = allUnits[i]; if (!unit.statusData.isOnFloor) continue; if (unit.commonData.troopId == ETroopsId.Self) continue; if (unit.commonData.canClaimStandCellIndex && unit.transData.cellIndex == cellIndex) { return true; } } return false; } public bool CheckContainsMoveUnit(int cellIndex) { var allUnits = GameUnitManager.instance.infightUnits.unitList; for (int i = 0; i < allUnits.Count; i++) { var unit = allUnits[i]; if (!unit.statusData.isOnFloor) continue; if (unit.commonData.canClaimStandCellIndex && unit.transData.cellIndex == cellIndex) { return true; } if (unit.commonData.canClaimTargetCellIndex && unit.controlData.targetCellIndex == cellIndex) { return true; } } return false; } public List GetUnitsByCellIndex(int cellIndex) { if (_cellIndex2Units.TryGetValue(cellIndex, out var result)) { return result; } else { var newList = new List(); _cellIndex2Units.Add(cellIndex, newList); return newList; } } public TGS.Cell GetCellByIndex(int cellIndex) { return _map.TerrainGridSystem.cells[cellIndex]; } public bool CheckMapCellAvailable(int cellIndex) { var mapCellData = LevelManager.Instance.CurrentLevel.Map.GetBlockData(cellIndex); if (mapCellData == null) { return false; } var propData = mapCellData.GetTerrainTypeProperty(); if (propData == null) { return false; } if (!propData.selectable) { return false; } return true; } private void _InitMathV3() { AroundHelper.ClearCache(); var cellList = _map.TerrainGridSystem.cells; for (int i = 0; i < cellList.Count; i++) { var cell = cellList[i]; var mathPos = AroundHelper.GetMathPosV3(cell.index); cell.mathPointV3 = mathPos; } } public void LogicUpdate(float dt) { // skillAreaSelector.LogicUpdate(dt); attackArea.LogicUpdate(dt); readyAreaManager.LogicUpdate(dt); attackAreaManager.LogicUpdate(dt); moveAreaView.LogicUpdate(); } public void FrameClear() { } } }