using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; using RuntimeData; using Logic; using Logic.CrashSight; using TH1_Anim; using TH1_Core.Managers; using TH1_Logic.Core; using TH1_Presentation.Sequencer.Task; using TH1_Renderer; using TH1Resource; using UnityEngine.UI; namespace TH1Renderer { public class MapRenderer { private Main _main; private MapData _mapData; private Transform _unitRenderMap; private Transform _gridRenderMap; public Transform HintRenderMap; //BubbleManager public InGameBubbleManager InGameBubbleManager; Transform _cityInfoRenderMap; public CameraController CameraController; public EffectManager EffectManager; //特效管理器,管理目前所有特效 public ProjectileManager ProjectileManager; private Transform _projectileRenderMap; //存储map对应的主要GameObject public GameObject ROMap; private GameObject _unitPrefab; //承载单位图像的prefab private GameObject _gridPrefab; //承载grid的prefab private GameObject _cityInfoPrefab; //承载城市图形信息、人口条的prefab public Dictionary ROGridMap; public Dictionary ROCityInfoMap; public Dictionary ROUnitMap; bool aniFlagExplorer = false; //用于探险家动画的标识bool GameObject explorerUnit; //用于存储探险家图像 Vector2Int explorerNowPos; //用于存储探险家Data当前的位置 // 临时探索者游戏对象 private GameObject _temporaryExplorer = null; private Vector3 _temporaryExplorerStartPos; private float _temporaryExplorerTime = 0f; private bool _temporaryExplorerActive = false; //-------- 表现层数据map交互部分 --------// //移动和攻击的高亮RenderData,set中存放了所有需要更新的对象 public HashSet HighlightGridIdSet = new HashSet(); public bool HighlightGridIdSetRenderMark = false; public HashSet HighlightUnitIdSet = new HashSet(); public bool HighlightUnitIdSetRenderMark = false; public UnitData SelectUnitData; // 添加移动相关的成员变量 private Vector3 _explorerMoveStartPos; private Vector3 _explorerMoveTargetPos; private float _explorerMoveTime; private float _explorerMoveDuration; private bool _explorerIsMoving; // 添加成员变量来记录上一个位置 private Vector2Int _explorerPreviousPosition = new Vector2Int(-1, -1); // 初始化为无效位置 // 添加成员变量来跟踪移动次数和存储当前目标格子 private int _explorerMoveCount = 0; private int _explorerMaxMoves = 15; private GridData _currentTargetGridData; private static MapRenderer _instance; public static MapRenderer Instance { get { if (_instance == null) { Debug.LogError("MapRenderer not initialized. Call Initialize(main, mapData) first."); } return _instance; } } public static void Initialize(Main main, MapData mapData) { if (_instance != null) { Debug.LogWarning("MapRenderer already initialized. Reinitializing..."); } _instance = new MapRenderer(main, mapData); } public static void Dispose() { _instance = null; } public static void TryUpdate() { if (_instance != null) { _instance.Update(); } } private MapRenderer(Main main, MapData mapData) { _main = main; _mapData = mapData; ROMap = main.ROMapRenderer; //初始化Data->Renderer的dict ROGridMap = new Dictionary(); ROCityInfoMap = new Dictionary(); ROUnitMap = new Dictionary(); // 初始化主要的渲染模块(grid city unit projectileManager) InitGridCityUnitObject(); //初始化相机模块 InitCameraObject(); //初始化BubbleManager InGameBubbleManager = new InGameBubbleManager(); InGameBubbleManager.Init(ROMap.transform.Find("HintMap")); } //当退出游戏或者结束游戏的时候出发,用来清理一些视觉对象 //InGame游戏开始的生命周期 public static void OnMatchStart(Main main, MapData mapData) { Dispose(); Initialize(main,mapData); _instance.InGameBubbleManager.OnGameStart(); } //InGame游戏结束时的生命周期 public void OnMatchEnd() { InGameBubbleManager.OnGameClosed(); } private void ClearAllChildren(Transform parent) { if (parent == null) return; for (int i = parent.childCount - 1; i >= 0; i--) { GameObject.Destroy(parent.GetChild(i).gameObject); } } public bool InitGridCityUnitObject() { //重新建立新的关联 if (ROMap == null) return false; _unitRenderMap = ROMap.transform.Find("UnitMap"); _gridRenderMap = ROMap.transform.Find("GridMap"); _cityInfoRenderMap = ROMap.transform.Find("CityInfoMap")?.GetComponent(); //清理战场,把上一局Grid里面留下的东西清理干净 ClearAllChildren(_unitRenderMap); ClearAllChildren(_gridRenderMap); ClearAllChildren(_cityInfoRenderMap); ClearAllChildren(HintRenderMap); ClearAllChildren(ROMap.transform.Find("ProjectileMap")); _unitPrefab = Resources.Load($"Prefab/unitPrefab"); _gridPrefab = Resources.Load($"Prefab/tilePrefab"); _cityInfoPrefab = Resources.Load($"Prefab/cityInfoMapPrefab"); //建立子模块的manager,目前仅有projectile //TODO 这里要改成ProjectManager依赖注入,然后要上面ClearALL要把Projectilemanager算进去 ProjectileManager = new ProjectileManager(); ProjectileManager.Init(ROMap.transform.Find("ProjectileMap")); return true; } public bool InitCameraObject() { var cameraObject = GameObject.Find("Main Camera"); if (cameraObject == null) return false; CameraController = cameraObject.GetComponent(); return true; } public void Update() { //处理所有飞行道具。这个不会被暂停 ProjectileManager.Update(); //HintManager InGameBubbleManager.Update(); // 处理临时探索者的浮动动画 if (_temporaryExplorerActive && _temporaryExplorer != null) { _temporaryExplorerTime += Time.deltaTime; float yOffset = 0.2f * Mathf.Sin(2f * _temporaryExplorerTime); // 0.2f是浮动幅度,2f是浮动频率 // 如果正在移动,则不应用浮动效果 if (!_explorerIsMoving) { _temporaryExplorer.transform.position = new Vector3( _temporaryExplorerStartPos.x, _temporaryExplorerStartPos.y + yOffset, _temporaryExplorerStartPos.z ); } } //-------- 处理UI数据 ---------// //处理高亮 if (HighlightGridIdSetRenderMark) { HighlightGridIdSetRenderMark = false; foreach (var gid in HighlightGridIdSet) ROGridMap[gid].RenderUpdataHighlight(); HighlightGridIdSet.Clear(); } if (HighlightUnitIdSetRenderMark) { HighlightUnitIdSetRenderMark = false; foreach (var uid in HighlightUnitIdSet) if(ROUnitMap.TryGetValue(uid, out var unit)) unit.RenderUpdataHighlight(); HighlightUnitIdSet.Clear(); } //处理CityMap或者UnitMap新增/删除的city和unit if (_mapData.CityMap.CityMapRenderMark) { _mapData.CityMap.CityMapRenderMark = false; RenderUpdateCityMap(); } //-------- 处理子节点的Update -------// foreach (var roUnit in ROUnitMap.Values) roUnit.Update(); foreach (var roGrid in ROGridMap.Values) roGrid.Update(); foreach (var roCityInfo in ROCityInfoMap.Values) roCityInfo.Update(); } //当gridMap出现新的对象时,新建对象 public void RenderUpdateGridMap() { foreach (var gridData in Main.MapData.GridMap.GridList) { ROGridMap[gridData.Id] = new GridRenderer(_gridPrefab, _gridRenderMap, gridData.Id, Main.MapData, _main); } } //当cityMap出现新的对象时,新建对象 public void RenderUpdateCityMap() { foreach (var cityData in Main.MapData.CityMap.CityList) if(!ROCityInfoMap.ContainsKey(cityData.Id)) { //生成城镇图像 RenderUpdateCityBuildings(cityData.Id); //生成城镇名称和人口条 ROCityInfoMap[cityData.Id] = new CityInfoRenderer(_cityInfoPrefab,_cityInfoRenderMap,cityData.Id,Main.MapData,_main); cityData.SetCityRenderer(Main.MapData); } } public void UpdateCityInfoAllCoinTech() { foreach (var cityInfo in ROCityInfoMap.Values) cityInfo.InstantUpdateCityCoinTech(); } //当unitMap出现新的对象时,新建对象 public void RenderUpdateUnitMap() { foreach (var unitData in Main.MapData.UnitMap.UnitList) if(!ROUnitMap.ContainsKey(unitData.Id)) { //生成单位图像 ROUnitMap[unitData.Id] = new UnitRenderer(_unitPrefab,_unitRenderMap,unitData.Id); //立刻更新每个unit的视觉 ROUnitMap[unitData.Id].InstantUpdateUnit(true); } } //当projectileMap出现新的对象时,新建对象 public void RenderUpdateProjectileMap() { } // 初次渲染地图 public void FirstRenderMap() { //初次渲染grid RenderUpdateGridMap(); //初次渲染city RenderUpdateCityMap(); //初次渲染所有unit RenderUpdateUnitMap(); } public void RenderUpdateBorders() //更新全地图的所有边界 { foreach (var gridData in Main.MapData.GridMap.GridList) ROGridMap[gridData.Id].UpdateBorder(); } private void RenderUpdateCityBuildings(uint cityId) //更新主城建筑的渲染 { Main.MapData.GetGridIdByCityId(cityId, out var gridId); ROGridMap[gridId].RenderUpdateCityBuilding(cityId); } public bool SetUnitAllMoveAttackTargetHighlight(uint uid) //渲染所有可移动位置的高亮,其中可以攻击的位置要标红,如果是自己人或者敌人在移动范围内但是不在攻击范围内,则不能高亮 { //Step #1 计算MoveInfo bool ret = false; Main.UnitLogic.CalcUnitMoveInfo(Main.MapData, uid); Main.MapData.UnitMap.GetUnitDataByUnitId(uid, out var unitData); Main.MapData.GetGridDataByUnitId(uid, out var gridData); Main.MapData.GetPlayerDataByUnitId(uid, out var playerData); //unitLogic.DebugOutputMoveInfo(); int r = Mathf.Max((Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unitData.UnitType,unitData.GiantType,unitData.UnitLevel,out var info)?info.MoveRange:0) * 3 + 1, Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unitData.UnitType,unitData.GiantType,unitData.UnitLevel,out var info2)?info2.AttackRange:0) * 2; //Step #2 遍历所有范围内格子,逐个判定是attack move还是别的 //Step #2 - 1 如果有citytransport或者是allytransport或者moriyaknightmove,要把r设置为全图大小 if(unitData.IsCanTransport()) r = (int)(_mapData.MapConfig.Width > _mapData.MapConfig.Height ? _mapData.MapConfig.Width : _mapData.MapConfig.Height); var targetGridDataList = Main.MapData.GridMap.GetAroundGridDataSet_NOCENTER(r,r,gridData); //Step #2 - 2 特殊处理kanako if (unitData.GetSkill(SkillType.KANAKOBATTLEFIELDPRO, out var _) && unitData.GetSkill(SkillType.KANAKOSITTING, out var _)) { foreach (var tmpunit in Main.MapData.UnitMap.UnitList) if(tmpunit.Player(Main.MapData) == playerData && (tmpunit.GiantType != GiantType.None || tmpunit.UnitType == UnitType.MoriyaHebi)) { if (tmpunit.UnitType == UnitType.MoriyaHebi && tmpunit.UnitLevel < 3) continue; if (tmpunit.Grid(Main.MapData).Feature != TerrainFeature.Mountain) continue; var tmpSet = Main.MapData.GridMap.GetAroundGridDataSet_NOCENTER(1,1,tmpunit.Grid(Main.MapData)); foreach(var tmpGrid in tmpSet) targetGridDataList.Add(tmpGrid); } } foreach(var targetGridData in targetGridDataList) { //如果不在视野 跳过 if (!playerData.Sight.CheckIsInSight(targetGridData.Id)) continue; // --------------------------------------------------------------------------------------------------------别找了 在这呢 -------------------------------------------------------------------------------------------------------- //获得该格子具体的可操作情况(移动?攻击?友方施法?) var sig = Main.UnitLogic.CheckUnitCanMoveOrAttack(Main.MapData, unitData, targetGridData); //如果是移动目标,且unit的MP>0 if ((sig is MoveAttackType.Move or MoveAttackType.MoveToPort or MoveAttackType.MoveAshore or MoveAttackType.MoveTeleport) && unitData.MP > 0) { ret = true; ROGridMap[targetGridData.Id].SetMoveHighlight(true); } //如果是攻击目标,且unit的AP>0 if (sig == MoveAttackType.Attack && unitData.AP > 0 && !unitData.IsLimitSelfAttack(Main.MapData)) { ret = true; if (!Main.MapData.GetUnitDataByGid(targetGridData.Id, out var unitDataB)) continue; //如果可以杀死,做一个提示 if (Table.Instance.CalcDamage(Main.MapData, unitData, unitDataB) >= unitDataB.Health) { targetGridData.Renderer(Main.MapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.DieHint),GridVFXPlayType.Play); } //如果会被杀死 else if(Main.UnitLogic.CanCounter(Main.MapData, unitData, unitDataB) && Table.Instance.CalcCounterDamage(Main.MapData, unitData,unitDataB) >= unitData.Health && unitData.UnitFullType.UnitType != UnitType.Minder) { targetGridData.Renderer(Main.MapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.CounterDieHint),GridVFXPlayType.Play); } ROUnitMap[unitDataB.Id].SetAttackHighlight(true); } //如果是友军目标,且unit的AP>0 if (sig == MoveAttackType.Ally && unitData.AP > 0) { ret = true; if (!Main.MapData.GetUnitDataByGid(targetGridData.Id, out var unitDataB)) continue; ROUnitMap[unitDataB.Id].SetAllyHighlight(true); } //如果是攻击ground目标,且unit的AP>0 if (sig == MoveAttackType.AttackGround && unitData.AP > 0) { ret = true; ROGridMap[targetGridData.Id].SetAttackHighlight(true); } } return ret; } //返回一个Unit是否存在移动或者攻击目标 public bool CheckUnitHasMoveAttackTarget(uint uid) { Main.UnitLogic.CalcUnitMoveInfo(Main.MapData, uid); Main.MapData.UnitMap.GetUnitDataByUnitId(uid, out var unitData); Main.MapData.GetGridDataByUnitId(uid, out var gridData); Main.MapData.GetPlayerDataByUnitId(uid, out var playerData); if (unitData == null || gridData == null || playerData == null) return false; if (unitData.AP <= 0 && unitData.MP <= 0) return false; //unitLogic.DebugOutputMoveInfo(); int r = Mathf.Max((Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unitData.UnitType,unitData.GiantType,unitData.UnitLevel,out var info)?info.MoveRange:0) * 2, Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unitData.UnitType,unitData.GiantType,unitData.UnitLevel,out var info2)?info2.AttackRange:0); var targetGridDataList = Main.MapData.GridMap.GetAroundGridDataSet_NOCENTER(r,r,gridData); foreach(var targetGridData in targetGridDataList) { //如果不在视野 跳过 if (!playerData.Sight.CheckIsInSight(targetGridData.Id)) continue; var sig = Main.UnitLogic.CheckUnitCanMoveOrAttack(Main.MapData, unitData, targetGridData); //如果是移动目标,且unit的MP>0 if ((sig == MoveAttackType.Move || sig == MoveAttackType.MoveToPort || sig == MoveAttackType.MoveAshore) && unitData.MP > 0) return true; //如果是攻击目标,且unit的AP>0 if (sig == MoveAttackType.Attack && unitData.AP > 0 && !unitData.IsLimitSelfAttack(Main.MapData)) return true; //如果是Ally目标,且unit的AP>0 if (sig == MoveAttackType.Ally && unitData.AP > 0 ) return true; //如果是AttackGround,且unit的AP>0 if (sig == MoveAttackType.AttackGround && unitData.AP > 0 ) return true; } return false; } //返回一个Unit是否当前可以占领遗迹/城市/收集starfish public bool CheckUnitHasSpecialUnitActionTarget(uint uid) { if (!_mapData.UnitMap.GetUnitDataByUnitId(uid, out var unit)) return false; if (!_mapData.GetGridDataByUnitId(uid, out var grid)) return false; if (!_mapData.GetPlayerDataByUnitId(uid, out var player)) return false; if (unit.CP <= 0) return false; //宝藏 if (grid.Resource == ResourceType.Treasure) return true; //海星 if (grid.Resource == ResourceType.Starfish && player.TechTree.CheckIfHasTech(TechType.Navigation)) return true; //城市 if (grid.Resource == ResourceType.CityCenter) { _mapData.GetPlayerDataByTerritoryGridId(grid.Id, out var gridPlayer); //村庄或者他国城市 if (gridPlayer == null) return true; if (gridPlayer.Id != player.Id) return true; } return false; } // 创建临时探索者并向视野最少的方向移动 public void CreateTemporaryExplorer(MapData map,GridData gridData, float maxDuration) { // Step #1 鲁棒性判断 if (!Main.MapData.GetPlayerDataByTerritoryGridId(gridData.Id, out var playerData)) { Debug.LogWarning("无法获取玩家数据,无法创建探索者"); return; } // Step #2 如果不是自己的玩家,直接计算10步探索并更新视野,不进行渲染 // 重置移动计数和当前目标格子 _explorerMoveCount = 0; _currentTargetGridData = gridData; _explorerPreviousPosition = new Vector2Int(-1, -1); GameObject spriteObject; //如果是真人玩家,提前设置对象 if (playerData.IsSelfPlayer()) { _temporaryExplorer = new GameObject("TemporaryExplorer"); _temporaryExplorer.transform.position = Table.Instance.GridToWorld(gridData, "isUnit"); _temporaryExplorer.transform.SetParent(_unitRenderMap, false); spriteObject = new GameObject("UnitSprite"); spriteObject.transform.SetParent(_temporaryExplorer.transform, false); SpriteRenderer spriteRenderer = spriteObject.AddComponent(); //把小人图像位置下移一点 spriteObject.transform.localScale = new Vector3(0.8f, 0.8f, 0.8f); var t = spriteObject.transform.localPosition; t.y -= 0.4f; spriteObject.transform.localPosition = t; // 设置explorer的sprite UnitType explorerUnitType = UnitType.Warrior; // 使用战士单位类型 if (Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(explorerUnitType, GiantType.None, 0, out var unitTypeInfo) && Table.Instance.UnitTypeDataAssets.GetUnitSpriteByInfo(unitTypeInfo, playerData, out var sprite)) spriteRenderer.sprite = sprite; } // 直接计算15步探索 for (int i = 0; i < _explorerMaxMoves; i++) { // 如果当前目标格子为空,结束探索 if (_currentTargetGridData == null) break; // 计算下一步移动 Vector2Int currentPos = new Vector2Int(_currentTargetGridData.Pos.X, _currentTargetGridData.Pos.Y); // 保存当前位置作为上一个位置 Vector2Int prevPos = currentPos; _explorerPreviousPosition = prevPos; // 计算下一步移动方向(不渲染,只计算) CalculateNextExplorerMove(map,_currentTargetGridData, playerData, out var nextGridData); //Step #5 处理视觉(只有自己玩家才会做 if (playerData.IsSelfPlayer()) { // 如果无法移动,结束探索 TODO 有一点点风险,之后处理吧 现在无法移动就改成原地移动了 if (nextGridData == null) nextGridData = _currentTargetGridData; bool lastMove = i + 1 == _explorerMaxMoves; FragmentMoveExplorerType moveType = FragmentMoveExplorerType.NormalMove; if(i == 0) moveType = FragmentMoveExplorerType.FirstMove; if (i + 1 == _explorerMaxMoves) moveType = FragmentMoveExplorerType.LastMove; var data = FragmentDataFactory.Create(FragmentType.MoveExplorer,_temporaryExplorer,_currentTargetGridData,nextGridData,moveType); PresentationManager.EnqueueTask(new FragmentSequencerTask(FragmentFactory.Create(FragmentType.MoveExplorer,data))); } //Step #6 更新视野 var radius = 1; if (nextGridData.Feature == TerrainFeature.Mountain) radius = 2; Main.PlayerLogic.UpdateSightByRadius_LogicView(Main.MapData, playerData, nextGridData, radius); // 更新当前目标格子 _currentTargetGridData = nextGridData; _explorerMoveCount++; } // 完成探索后解锁输入 ,只有是玩家才需要解锁输入 if(playerData.Id == Main.MapData.PlayerMap.SelfPlayerId) _main.InputLogic.UnlockInput(); } // 修复GetDirectionIndex方法中的错误 private int GetDirectionIndex(int dx, int dy) { // 将dx和dy归一化为-1, 0, 1 int nx = dx == 0 ? 0 : (dx > 0 ? 1 : -1); int ny = dy == 0 ? 0 : (dy > 0 ? 1 : -1); // 根据归一化后的方向确定索引 switch (nx) { case 1: switch (ny) { case 1: return 1; // 右上 case 0: return 0; // 右 case -1: return 7; // 右下 } break; case 0: switch (ny) { case 1: return 2; // 上 case -1: return 6; // 下 } break; case -1: switch (ny) { case 1: return 3; // 左上 case 0: return 4; // 左 case -1: return 5; // 左下 } break; } // 默认返回右方向 return 0; } // 根据方向索引获取移动方向 private Vector2Int GetMoveDirectionFromIndex(int dirIndex) { switch (dirIndex) { case 0: return new Vector2Int(1, 0); // 右 case 1: return new Vector2Int(1, 1); // 右上 case 2: return new Vector2Int(0, 1); // 上 case 3: return new Vector2Int(-1, 1); // 左上 case 4: return new Vector2Int(-1, 0); // 左 case 5: return new Vector2Int(-1, -1); // 左下 case 6: return new Vector2Int(0, -1); // 下 case 7: return new Vector2Int(1, -1); // 右下 default: return new Vector2Int(0, 0); // 默认不移动 } } // -------------------------- 以下是一组给explorere计算使用的参数 ---------------------------- private bool CheckCanMove(PlayerData player, Vector2Int pos) { if (!Main.MapData.GridMap.GetGridDataByPos(pos.x, pos.y, out var grid)) return false; return Main.UnitLogic.CheckUnitAbleForGrid_OfflineStatus(Main.MapData,player,null, grid); } //返回一个格子的"探索值“,即最近的未探索格子的寻路距离 private int CalcExploreValue(MapData map,PlayerData player,Vector2Int startPos) { int[,] CanMove = new int[map.MapConfig.Width, map.MapConfig.Height]; for(int i = 0;i < map.MapConfig.Width;i++) for (int j = 0; j < map.MapConfig.Height; j++) CanMove[i, j] = -1; // 2. 边界和起始点有效性检查 if (startPos.x < 0 || startPos.x >= 30 || startPos.y < 0 || startPos.y >= 30 || !CheckCanMove(player,startPos)) { return 1000 ; // 起始点不合法,直接返回 } // 3. 初始化队列并将起点加入 var queue = new Queue(); queue.Enqueue(startPos); CanMove[startPos.x, startPos.y] = 0; // 标记起点已访问 // 4. BFS主循环 while (queue.Count > 0) { var current = queue.Dequeue(); // 检查8个方向的邻居 for (int i = 0; i < 8; i++) { var dir = GetMoveDirectionFromIndex(i); var neighbor = current + dir; // 5. 检查邻居的合法性:在界内、之前未访问过、且可以通过CheckCan if (neighbor.x < 0 || neighbor.x >= map.MapConfig.Width || neighbor.y < 0 || neighbor.y >= map.MapConfig.Height) continue; if (CanMove[neighbor.x, neighbor.y] != -1) continue; if (!map.GridMap.GetGridDataByPos(neighbor.x, neighbor.y, out var gridData)) continue; //找到一个没有开视野的格子,返回! if (!player.Sight.CheckIsInSight(gridData.Id)) { return CanMove[current.x, current.y]; } if(CheckCanMove(player, neighbor)) { CanMove[neighbor.x, neighbor.y] = CanMove[current.x, current.y] + 1; // 标记为可达/已访问 queue.Enqueue(neighbor); // 加入队列等待处理 } } } return 999; } // -------------------------- 以上 ---------------------------- // 新增辅助方法:计算下一步移动但不执行渲染 private bool CalculateNextExplorerMove(MapData map,GridData startGridData, PlayerData playerData, out GridData nextGridData) { nextGridData = null; // 当前位置 Vector2Int currentPos = new Vector2Int(startGridData.Pos.X, startGridData.Pos.Y); // 地图尺寸 int mapWidth = (int)Main.MapData.MapConfig.Width; int mapHeight = (int)Main.MapData.MapConfig.Height; // 存储每个方向的最近未探索格子距离 float[] directionMinDistance = new float[8]; for (int i = 0; i < 8; i++) { directionMinDistance[i] = float.MaxValue; // 初始化为最大值 } // 找出未探索格子最近的方向 int minDistance = 1000; List bestDirections = new List(); for (int i = 0; i < 8; i++) { Vector2Int dirVector = GetMoveDirectionFromIndex(i); Vector2Int nextPos = currentPos + dirVector; //Step #1 该方向首先得能走,不能走直接continue if (!Main.MapData.GridMap.GetGridDataByPos(nextPos.x, nextPos.y, out var tempNextGridData)) continue; if(!IsTerrainPassable(tempNextGridData, playerData)) continue; //Step #2 获得改方向的探索值 int v = CalcExploreValue(map, playerData, nextPos); if (v < minDistance) { bestDirections.Clear(); bestDirections.Add(i); minDistance = v; } else if(v == minDistance) { bestDirections.Add(i); } } // 如果没有找到最佳方向,尝试任何可行方向(除了上一个位置) if (bestDirections.Count == 0) { for (int i = 0; i < 8; i++) { // 计算该方向的下一个位置 Vector2Int dirVector2 = GetMoveDirectionFromIndex(i); Vector2Int nextPos = currentPos + dirVector2; // 检查该位置是否在地图范围内 if (nextPos.x < 0 || nextPos.x >= mapWidth || nextPos.y < 0 || nextPos.y >= mapHeight) continue; // 检查该位置是否是上一个位置 /*if (_explorerPreviousPosition.x == nextPos.x && _explorerPreviousPosition.y == nextPos.y) continue;*/ // 检查该位置是否可通行 GridData tempNextGridData; if (!Main.MapData.GridMap.GetGridDataByPos(nextPos.x, nextPos.y, out tempNextGridData)) continue; // 检查地形是否可通行 if (!IsTerrainPassable(tempNextGridData, playerData)) continue; bestDirections.Add(i); } } // 如果仍然没有找到可行方向,返回失败 if (bestDirections.Count == 0) { return false; } // 随机选择一个最佳方向 var value = map.Net.GetRandom(map).Next(0, bestDirections.Count); int selectedDirIndex = bestDirections[value]; // 根据选择的方向确定移动的目标位置 Vector2Int finalMoveDir = GetMoveDirectionFromIndex(selectedDirIndex); Vector2Int targetPos = currentPos + finalMoveDir; // 获取目标格子数据 return Main.MapData.GridMap.GetGridDataByPos(targetPos.x, targetPos.y, out nextGridData); } // 新增方法:检查地形是否可通行 private bool IsTerrainPassable(GridData gridData, PlayerData playerData) { // 检查地形是否可通行 if (gridData.Terrain == TerrainType.DeepSea && !playerData.TechTree.CheckIfHasTechAtom(TechAtom.UnitSkillOCEANMOVE)) return false; if (gridData.Terrain == TerrainType.ShallowSea && !playerData.TechTree.CheckIfHasTechAtom(TechAtom.UnitSkillWATERMOVE)) return false; if (gridData.Feature == TerrainFeature.Mountain && !playerData.TechTree.CheckIfHasTechAtom(TechAtom.UnitSkillMOUNTAINMOVE)) return false; return true; } // 新增方法:使用BFS检查格子是否可达 private bool IsGridReachable(GridData startGrid, GridData targetGrid, PlayerData playerData, int maxSteps) { // 如果起点和终点相同,直接返回true if (startGrid.Id == targetGrid.Id) return true; // 如果目标格子地形不可通行,直接返回false if (!IsTerrainPassable(targetGrid, playerData)) return false; // 使用BFS搜索 Queue queue = new Queue(); HashSet visited = new HashSet(); Dictionary steps = new Dictionary(); queue.Enqueue(startGrid); visited.Add(startGrid.Id); steps[startGrid.Id] = 0; while (queue.Count > 0) { GridData current = queue.Dequeue(); int currentSteps = steps[current.Id]; // 如果已经超过最大步数,跳过 if (currentSteps >= maxSteps) continue; // 获取当前格子的八个方向的相邻格子 Vector2Int currentPos = new Vector2Int(current.Pos.X, current.Pos.Y); for (int i = 0; i < 8; i++) { Vector2Int dirVector = GetMoveDirectionFromIndex(i); Vector2Int nextPos = currentPos + dirVector; // 检查位置是否在地图范围内 if (nextPos.x < 0 || nextPos.x >= Main.MapData.MapConfig.Width || nextPos.y < 0 || nextPos.y >= Main.MapData.MapConfig.Height) continue; // 获取相邻格子数据 if (!Main.MapData.GridMap.GetGridDataByPos(nextPos.x, nextPos.y, out var nextGrid)) continue; // 如果已经访问过,跳过 if (visited.Contains(nextGrid.Id)) continue; // 检查地形是否可通行 if (!IsTerrainPassable(nextGrid, playerData)) continue; // 如果找到目标格子,返回true if (nextGrid.Id == targetGrid.Id) return true; // 将相邻格子加入队列 queue.Enqueue(nextGrid); visited.Add(nextGrid.Id); steps[nextGrid.Id] = currentSteps + 1; } } // 搜索完毕仍未找到目标格子,返回false return false; } public bool UpdateAroundHighlight(MapData mapData, GridData grid) { //如果不是真map,不操作 if (mapData != Main.MapData) return false; var set = mapData.GridMap.GetAroundGridDataSet_NOCENTER(3, 3, grid); foreach (var aroundGrid in set) { //如果是玩家的unit 那么更新高亮 if (mapData.GetUnitDataByGid(aroundGrid.Id, out var unit) && mapData.GetPlayerIdByUnitId(unit.Id,out var pid) && pid == mapData.PlayerMap.SelfPlayerId) unit.Renderer(mapData)?.InstantUpdateUnit(true); } return true; } } }