1142 lines
48 KiB
C#
1142 lines
48 KiB
C#
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
|
||
using UnityEngine.SceneManagement;
|
||
|
||
using RuntimeData;
|
||
using Logic;
|
||
using Logic.CrashSight;
|
||
using TH1_Logic.Core;
|
||
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;
|
||
|
||
|
||
//HintManager
|
||
public HintManager HintManager;
|
||
|
||
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<uint, GridRenderer> ROGridMap;
|
||
public Dictionary<uint, CityInfoRenderer> ROCityInfoMap;
|
||
public Dictionary<uint, UnitRenderer> 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<uint> HighlightGridIdSet = new HashSet<uint>();
|
||
public bool HighlightGridIdSetRenderMark = false;
|
||
public HashSet<uint> HighlightUnitIdSet = new HashSet<uint>();
|
||
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 = 10;
|
||
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)
|
||
{
|
||
//EventManager.Instance.EventEnter = new ViewEventEnter();
|
||
_main = main;
|
||
_mapData = mapData;
|
||
ROMap = main.ROMapRenderer;
|
||
//初始化Data->Renderer的dict
|
||
ROGridMap = new Dictionary<uint, GridRenderer>();
|
||
ROCityInfoMap = new Dictionary<uint, CityInfoRenderer>();
|
||
ROUnitMap = new Dictionary<uint, UnitRenderer>();
|
||
|
||
// 初始化主要的渲染模块(grid city unit projectileManager)
|
||
InitGridCityUnitObject();
|
||
|
||
//初始化相机模块
|
||
InitCameraObject();
|
||
|
||
//初始化HintManager
|
||
HintManager = new HintManager();
|
||
HintManager.Init(main, mapData);
|
||
}
|
||
|
||
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");
|
||
HintRenderMap = ROMap.transform.Find("HintMap");
|
||
|
||
_cityInfoRenderMap = ROMap.transform.Find("CityInfoMap")?.GetComponent<Transform>();
|
||
|
||
//清理战场,把上一局Grid里面留下的东西清理干净
|
||
ClearAllChildren(_unitRenderMap);
|
||
ClearAllChildren(_gridRenderMap);
|
||
ClearAllChildren(_cityInfoRenderMap);
|
||
ClearAllChildren(HintRenderMap);
|
||
ClearAllChildren(ROMap.transform.Find("ProjectileMap"));
|
||
|
||
_unitPrefab = Resources.Load<GameObject>($"Prefab/unitPrefab");
|
||
_gridPrefab = Resources.Load<GameObject>($"Prefab/tilePrefab");
|
||
_cityInfoPrefab = Resources.Load<GameObject>($"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<CameraController>();
|
||
return true;
|
||
}
|
||
|
||
|
||
public void Update()
|
||
{
|
||
|
||
//处理所有飞行道具。这个不会被暂停
|
||
ProjectileManager.Update();
|
||
|
||
//如果有centerMessage ,停掉一切动画
|
||
//if (_main.UIManager.CenterMessageUI.isShowingNow)return;
|
||
|
||
|
||
//HintManager
|
||
HintManager.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
|
||
);
|
||
}
|
||
}
|
||
|
||
// 处理探索者移动
|
||
if (_explorerIsMoving && _temporaryExplorer != null)
|
||
{
|
||
_explorerMoveTime += Time.deltaTime;
|
||
float t = Mathf.Clamp01(_explorerMoveTime / _explorerMoveDuration);
|
||
_temporaryExplorer.transform.position = Vector3.Lerp(_explorerMoveStartPos, _explorerMoveTargetPos, t);
|
||
|
||
// 移动完成
|
||
if (t >= 1.0f)
|
||
{
|
||
_explorerIsMoving = false;
|
||
_temporaryExplorerStartPos = _explorerMoveTargetPos; // 更新浮动动画的起始位置
|
||
|
||
// 更新视野
|
||
if (_currentTargetGridData != null)
|
||
// 调用UpdateSightByRadius更新视野
|
||
Main.PlayerLogic.UpdateSightByRadius(Main.MapData, Main.MapData.PlayerMap.SelfPlayerData, _currentTargetGridData,
|
||
_currentTargetGridData.Feature == TerrainFeature.Mountain? 2 : 1);
|
||
|
||
// 检查是否需要继续移动
|
||
if (_explorerMoveCount < _explorerMaxMoves && _temporaryExplorer != null)
|
||
{
|
||
// 获取动画延迟时间
|
||
float delayTime = 0.5f; // 默认值
|
||
if (Table.Instance.AnimDataAssets != null)
|
||
{
|
||
// 从AnimDataAssets获取延迟时间
|
||
delayTime = Table.Instance.AnimDataAssets.ExplorerMoveDelay;
|
||
|
||
// 如果没有专门的ExplorerMoveDelay参数,可以使用其他相关参数
|
||
if (delayTime <= 0)
|
||
{
|
||
delayTime = Table.Instance.AnimDataAssets.MoveAnimTime + 0.2f; // 移动时间加一点额外延迟
|
||
}
|
||
}
|
||
|
||
// 应用动画速度调整
|
||
delayTime /= DebugCenter.Instance.AnimationSpeed;
|
||
|
||
// 延迟一小段时间后继续移动
|
||
Timer.Instance.TimerRegister(this, () => {
|
||
MoveExplorerToLeastVisibleDirection(_currentTargetGridData, Main.MapData.PlayerMap.SelfPlayerData);
|
||
}, delayTime,"MapRenderer_Update");
|
||
}
|
||
else if (_explorerMoveCount >= _explorerMaxMoves && _temporaryExplorer != null)
|
||
{
|
||
// 已完成所有移动,销毁探索者
|
||
RemoveTemporaryExplorer();
|
||
// 确保解锁输入
|
||
_main.InputLogic.UnlockInput();
|
||
}
|
||
}
|
||
}
|
||
|
||
//-------- 处理UI数据 ---------//
|
||
//处理高亮
|
||
if (HighlightGridIdSetRenderMark)
|
||
{
|
||
HighlightGridIdSetRenderMark = false;
|
||
foreach (var gid in HighlightGridIdSet)
|
||
ROGridMap[gid].RenderUpdataHighlight();
|
||
HighlightGridIdSet.Clear();
|
||
}
|
||
if (HighlightUnitIdSetRenderMark)
|
||
{
|
||
HighlightUnitIdSetRenderMark = false;
|
||
foreach (var uid in HighlightUnitIdSet)
|
||
ROUnitMap[uid].RenderUpdataHighlight();
|
||
HighlightUnitIdSet.Clear();
|
||
}
|
||
|
||
//处理CityMap或者UnitMap新增/删除的city和unit
|
||
if (_mapData.CityMap.CityMapRenderMark)
|
||
{
|
||
_mapData.CityMap.CityMapRenderMark = false;
|
||
RenderUpdateCityMap();
|
||
}
|
||
|
||
if (_mapData.UnitMap.UnitMapRenderMark)
|
||
{
|
||
_mapData.UnitMap.UnitMapRenderMark = false;
|
||
//检查有无单位销毁
|
||
//var t = ROUnitMap;
|
||
List<uint> toRemove = new List<uint>();
|
||
foreach (var roUnitId in ROUnitMap.Keys)
|
||
{
|
||
if (!Main.MapData.UnitMap.GetUnitDataByUnitId(roUnitId, out var unitData))
|
||
toRemove.Add(roUnitId);
|
||
}
|
||
|
||
foreach (var roUnitId in toRemove)
|
||
{
|
||
ROUnitMap[roUnitId].Die();
|
||
ROUnitMap.Remove(roUnitId);
|
||
}
|
||
//更新新增单位
|
||
RenderUpdateUnitMap();
|
||
}
|
||
|
||
|
||
//-------- 处理子节点的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.CityInfoRenderMark = true;
|
||
}
|
||
}
|
||
|
||
//当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,Main.MapData,_main);
|
||
}
|
||
}
|
||
|
||
//当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) //渲染所有可移动位置的高亮,其中可以攻击的位置要标红,如果是自己人或者敌人在移动范围内但是不在攻击范围内,则不能高亮
|
||
{
|
||
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) * 2,
|
||
Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unitData.UnitType,unitData.GiantType,unitData.UnitLevel,out var info2)?info2.AttackRange:0);
|
||
//如果有citytransport或者是allytransport,要把r设置为全图大小
|
||
if (unitData.UnitType == UnitType.Giant)
|
||
{
|
||
bool transport = false;
|
||
foreach(var skill in unitData.Skills)
|
||
if (skill.GetSkillType() == SkillType.ALLYTRANSPORT ||
|
||
skill.GetSkillType() == SkillType.CITYTRANSPORT)
|
||
{
|
||
transport = true;
|
||
break;
|
||
}
|
||
if(transport)
|
||
r = (int)(_mapData.MapConfig.Width > _mapData.MapConfig.Height ? _mapData.MapConfig.Width : _mapData.MapConfig.Height);
|
||
}
|
||
|
||
var targetGridDataList = Main.MapData.GridMap.GetAroundGridDataSet(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)
|
||
{
|
||
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.VFXRenderMarkDieHintStart = true;
|
||
//0代表这是杀死提示
|
||
targetGridData.VFXRenderMarkDieHintStartParam = 0;
|
||
}
|
||
else if(Main.UnitLogic.CanCounter(Main.MapData, unitData, unitDataB) &&
|
||
Table.Instance.CalcDamage(Main.MapData, unitData, unitDataB,true) >= unitData.Health)
|
||
{
|
||
targetGridData.VFXRenderMarkDieHintStart = true;
|
||
//1代表这是被杀死提示
|
||
targetGridData.VFXRenderMarkDieHintStartParam = 1;
|
||
}
|
||
ROUnitMap[unitDataB.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.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(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;
|
||
}
|
||
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(GridData gridData, float maxDuration)
|
||
{
|
||
// 获取玩家数据 - 使用领土所属关系获取
|
||
if (!Main.MapData.GetPlayerDataByTerritoryGridId(gridData.Id, out var playerData))
|
||
{
|
||
Debug.LogWarning("无法获取玩家数据,无法创建探索者");
|
||
return;
|
||
}
|
||
|
||
// 检查是否是自己的玩家
|
||
bool isSelfPlayer = playerData.Id == Main.MapData.PlayerMap.SelfPlayerData.Id;
|
||
|
||
// 如果不是自己的玩家,直接计算10步探索并更新视野,不进行渲染
|
||
if (!isSelfPlayer)
|
||
{
|
||
//Debug.Log("非自己玩家的探索者,直接计算探索路径");
|
||
|
||
// 重置移动计数和当前目标格子
|
||
_explorerMoveCount = 0;
|
||
_currentTargetGridData = gridData;
|
||
_explorerPreviousPosition = new Vector2Int(-1, -1);
|
||
|
||
// 直接计算10步探索
|
||
for (int i = 0; i < _explorerMaxMoves; i++)
|
||
{
|
||
// 如果当前目标格子为空,结束探索
|
||
if (_currentTargetGridData == null)
|
||
break;
|
||
|
||
// 计算下一步移动
|
||
Vector2Int currentPos = new Vector2Int(_currentTargetGridData.Pos.X, _currentTargetGridData.Pos.Y);
|
||
|
||
// 更新视野
|
||
Main.PlayerLogic.UpdateSightByRadius(Main.MapData, playerData, _currentTargetGridData, 1);
|
||
|
||
// 保存当前位置作为上一个位置
|
||
Vector2Int prevPos = currentPos;
|
||
_explorerPreviousPosition = prevPos;
|
||
|
||
// 计算下一步移动方向(不渲染,只计算)
|
||
CalculateNextExplorerMove(_currentTargetGridData, playerData, out var nextGridData);
|
||
|
||
// 如果无法移动,结束探索
|
||
if (nextGridData == null)
|
||
break;
|
||
|
||
// 更新当前目标格子
|
||
_currentTargetGridData = nextGridData;
|
||
_explorerMoveCount++;
|
||
}
|
||
|
||
// 完成探索后解锁输入 ,只有是玩家才需要解锁输入
|
||
if(playerData.Id == Main.MapData.PlayerMap.SelfPlayerId)
|
||
_main.InputLogic.UnlockInput();
|
||
return;
|
||
}
|
||
|
||
// 以下是原有的渲染逻辑,只有自己的玩家才会执行
|
||
// 如果已经有临时探索者,先销毁
|
||
if (_temporaryExplorer != null)
|
||
{
|
||
GameObject.Destroy(_temporaryExplorer);
|
||
_temporaryExplorer = null;
|
||
}
|
||
|
||
// 重置移动计数
|
||
_explorerMoveCount = 0;
|
||
_currentTargetGridData = gridData;
|
||
|
||
// 重置上一个位置为无效位置
|
||
_explorerPreviousPosition = new Vector2Int(-1, -1);
|
||
|
||
// 获取城市位置的世界坐标
|
||
Vector3 worldPos = Table.Instance.GridToWorld(gridData, "isUnit");
|
||
|
||
// 创建一个简单的游戏对象作为临时探索者
|
||
_temporaryExplorer = new GameObject("TemporaryExplorer");
|
||
_temporaryExplorer.transform.position = worldPos;
|
||
_temporaryExplorer.transform.SetParent(_unitRenderMap, false);
|
||
|
||
// 创建一个子对象来持有SpriteRenderer
|
||
GameObject spriteObject = new GameObject("UnitSprite");
|
||
spriteObject.transform.SetParent(_temporaryExplorer.transform, false);
|
||
|
||
// 添加精灵渲染器到子对象
|
||
SpriteRenderer spriteRenderer = spriteObject.AddComponent<SpriteRenderer>();
|
||
|
||
// 获取UnitTypeInfo
|
||
UnitType explorerUnitType = UnitType.Warrior; // 使用战士单位类型
|
||
if (Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(explorerUnitType, GiantType.None,0, out var unitTypeInfo))
|
||
{
|
||
// 使用GetUnitSpriteByInfo方法获取精灵
|
||
if (Table.Instance.UnitTypeDataAssets.GetUnitSpriteByInfo(Main.MapData, unitTypeInfo, playerData, out var sprite))
|
||
{
|
||
spriteRenderer.sprite = sprite;
|
||
// 确保精灵渲染器设置正确
|
||
spriteRenderer.sortingLayerName = "Units";
|
||
spriteRenderer.sortingOrder = 10; // 确保显示在其他单位之上
|
||
|
||
Debug.Log($"临时探索者创建成功,使用精灵: {sprite.name}");
|
||
|
||
// 计算八个方向的视野情况并选择视野最少的方向移动
|
||
MoveExplorerToLeastVisibleDirection(gridData, playerData);
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("GetUnitSpriteByInfo方法获取精灵失败");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("获取UnitTypeInfo失败");
|
||
}
|
||
|
||
// 设置浮动动画参数
|
||
_temporaryExplorerStartPos = worldPos;
|
||
_temporaryExplorerTime = 0f;
|
||
_temporaryExplorerActive = true;
|
||
}
|
||
|
||
// 计算八个方向的视野情况并选择视野最少的方向移动
|
||
private void MoveExplorerToLeastVisibleDirection(GridData startGridData, PlayerData playerData)
|
||
{
|
||
// 当前位置
|
||
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 searchRadius = 10;
|
||
|
||
// 在搜索半径内查找未探索的格子
|
||
for (int x = Mathf.Max(0, currentPos.x - searchRadius); x < Mathf.Min(mapWidth, currentPos.x + searchRadius); x++)
|
||
{
|
||
for (int y = Mathf.Max(0, currentPos.y - searchRadius); y < Mathf.Min(mapHeight, currentPos.y + searchRadius); y++)
|
||
{
|
||
// 跳过当前位置
|
||
if (x == currentPos.x && y == currentPos.y)
|
||
continue;
|
||
|
||
// 获取格子数据
|
||
if (Main.MapData.GridMap.GetGridDataByPos(x, y, out var gridData))
|
||
{
|
||
// 检查是否在视野中
|
||
if (!playerData.Sight.CheckIsInSight(gridData.Id))
|
||
{
|
||
// 检查该格子是否可达
|
||
if (!IsGridReachable(startGridData, gridData, playerData, 20))
|
||
continue;
|
||
|
||
// 计算相对位置
|
||
int dx = x - currentPos.x;
|
||
int dy = y - currentPos.y;
|
||
|
||
// 计算距离
|
||
float distance = Mathf.Sqrt(dx * dx + dy * dy);
|
||
|
||
// 确定方向区块
|
||
int dirIndex = GetDirectionIndex(dx, dy);
|
||
|
||
// 更新该方向的最小距离
|
||
if (distance < directionMinDistance[dirIndex])
|
||
{
|
||
directionMinDistance[dirIndex] = distance;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 找出未探索格子最近的方向,排除会导致回到上一个位置的方向
|
||
float minDistance = float.MaxValue;
|
||
List<int> bestDirections = new List<int>();
|
||
|
||
for (int i = 0; i < 8; i++)
|
||
{
|
||
// 如果该方向没有未探索格子,跳过
|
||
if (directionMinDistance[i] == float.MaxValue)
|
||
continue;
|
||
|
||
// 计算该方向的下一个位置
|
||
Vector2Int dirVector = GetMoveDirectionFromIndex(i);
|
||
Vector2Int nextPos = currentPos + dirVector;
|
||
|
||
// 检查该位置是否在地图范围内
|
||
if (nextPos.x < 0 || nextPos.x >= mapWidth || nextPos.y < 0 || nextPos.y >= mapHeight)
|
||
continue;
|
||
|
||
// 检查该位置是否是上一个位置
|
||
if (_explorerPreviousPosition.x == nextPos.x && _explorerPreviousPosition.y == nextPos.y)
|
||
continue;
|
||
|
||
// 检查该位置是否可通行
|
||
if (!Main.MapData.GridMap.GetGridDataByPos(nextPos.x, nextPos.y, out var nextGridData))
|
||
continue;
|
||
|
||
// 检查地形是否可通行
|
||
if (!IsTerrainPassable(nextGridData, playerData))
|
||
continue;
|
||
|
||
if (directionMinDistance[i] < minDistance)
|
||
{
|
||
minDistance = directionMinDistance[i];
|
||
bestDirections.Clear();
|
||
bestDirections.Add(i);
|
||
}
|
||
else if (directionMinDistance[i] == 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;
|
||
|
||
// 检查该位置是否可通行
|
||
if (!Main.MapData.GridMap.GetGridDataByPos(nextPos.x, nextPos.y, out var nextGridData))
|
||
continue;
|
||
|
||
// 检查地形是否可通行
|
||
if (!IsTerrainPassable(nextGridData, playerData))
|
||
continue;
|
||
|
||
bestDirections.Add(i);
|
||
}
|
||
}
|
||
|
||
// 如果仍然没有找到可行方向,结束探索
|
||
if (bestDirections.Count == 0)
|
||
{
|
||
RemoveTemporaryExplorer();
|
||
Debug.LogWarning("YIWAI 3 UNLOCK!!!!!");
|
||
_main.InputLogic.UnlockInput();
|
||
return;
|
||
}
|
||
|
||
// 随机选择一个最佳方向
|
||
int selectedDirIndex = bestDirections[UnityEngine.Random.Range(0, bestDirections.Count)];
|
||
|
||
// 根据选择的方向确定移动的目标位置
|
||
Vector2Int finalMoveDir = GetMoveDirectionFromIndex(selectedDirIndex);
|
||
Vector2Int targetPos = currentPos + finalMoveDir;
|
||
|
||
// 获取目标格子数据
|
||
if (Main.MapData.GridMap.GetGridDataByPos(targetPos.x, targetPos.y, out var targetGridData))
|
||
{
|
||
// 保存上一个位置
|
||
_explorerPreviousPosition = currentPos;
|
||
|
||
// 更新视野
|
||
Main.PlayerLogic.UpdateSightByRadius(Main.MapData, playerData, targetGridData, 1);
|
||
|
||
// 获取移动动画时间
|
||
float moveTime = 0.5f;
|
||
if (Table.Instance.AnimDataAssets != null)
|
||
{
|
||
moveTime = Table.Instance.AnimDataAssets.MoveAnimTime;
|
||
}
|
||
|
||
// 应用动画速度调整
|
||
moveTime /= DebugCenter.Instance.AnimationSpeed;
|
||
|
||
if(!_temporaryExplorer)
|
||
{
|
||
LogSystem.LogError($"_temporaryExplorer is null, cannot move explorer.");
|
||
return;
|
||
}
|
||
// 设置移动参数
|
||
Vector3 targetWorldPos = Table.Instance.GridToWorld(targetGridData, "isUnit");
|
||
_explorerMoveStartPos = _temporaryExplorer.transform.position;
|
||
_explorerMoveTargetPos = targetWorldPos;
|
||
_explorerMoveTime = 0f;
|
||
_explorerMoveDuration = moveTime;
|
||
_explorerIsMoving = true;
|
||
|
||
// 增加移动计数
|
||
_explorerMoveCount++;
|
||
|
||
// 存储当前目标格子,用于移动完成后更新视野
|
||
_currentTargetGridData = targetGridData;
|
||
}
|
||
}
|
||
|
||
// 修复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); // 默认不移动
|
||
}
|
||
}
|
||
|
||
// 移除临时探索者
|
||
public void RemoveTemporaryExplorer()
|
||
{
|
||
if (_temporaryExplorer != null)
|
||
{
|
||
GameObject.Destroy(_temporaryExplorer);
|
||
_temporaryExplorer = null;
|
||
Debug.Log("临时探索者已移除");
|
||
}
|
||
_temporaryExplorerActive = false;
|
||
}
|
||
|
||
// 新增辅助方法:计算下一步移动但不执行渲染
|
||
private bool CalculateNextExplorerMove(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 searchRadius = 10;
|
||
|
||
// 在搜索半径内查找未探索的格子
|
||
for (int x = Mathf.Max(0, currentPos.x - searchRadius); x < Mathf.Min(mapWidth, currentPos.x + searchRadius); x++)
|
||
{
|
||
for (int y = Mathf.Max(0, currentPos.y - searchRadius); y < Mathf.Min(mapHeight, currentPos.y + searchRadius); y++)
|
||
{
|
||
// 跳过当前位置
|
||
if (x == currentPos.x && y == currentPos.y)
|
||
continue;
|
||
|
||
// 获取格子数据
|
||
if (Main.MapData.GridMap.GetGridDataByPos(x, y, out var gridData))
|
||
{
|
||
// 检查是否在视野中
|
||
if (!playerData.Sight.CheckIsInSight(gridData.Id))
|
||
{
|
||
// 检查该格子是否可达
|
||
if (!IsGridReachable(startGridData, gridData, playerData, 20))
|
||
continue;
|
||
|
||
// 计算相对位置
|
||
int dx = x - currentPos.x;
|
||
int dy = y - currentPos.y;
|
||
|
||
// 计算距离
|
||
float distance = Mathf.Sqrt(dx * dx + dy * dy);
|
||
|
||
// 确定方向区块
|
||
int dirIndex = GetDirectionIndex(dx, dy);
|
||
|
||
// 更新该方向的最小距离
|
||
if (distance < directionMinDistance[dirIndex])
|
||
{
|
||
directionMinDistance[dirIndex] = distance;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 找出未探索格子最近的方向
|
||
float minDistance = float.MaxValue;
|
||
List<int> bestDirections = new List<int>();
|
||
|
||
for (int i = 0; i < 8; i++)
|
||
{
|
||
// 如果该方向没有未探索格子,跳过
|
||
if (directionMinDistance[i] == float.MaxValue)
|
||
continue;
|
||
|
||
// 计算该方向的下一个位置
|
||
Vector2Int dirVector = GetMoveDirectionFromIndex(i);
|
||
Vector2Int nextPos = currentPos + dirVector;
|
||
|
||
// 检查该位置是否在地图范围内
|
||
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;
|
||
|
||
if (directionMinDistance[i] < minDistance)
|
||
{
|
||
minDistance = directionMinDistance[i];
|
||
bestDirections.Clear();
|
||
bestDirections.Add(i);
|
||
}
|
||
else if (directionMinDistance[i] == 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;
|
||
}
|
||
|
||
// 随机选择一个最佳方向
|
||
int selectedDirIndex = bestDirections[UnityEngine.Random.Range(0, bestDirections.Count)];
|
||
|
||
// 根据选择的方向确定移动的目标位置
|
||
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.CheckIfHasTech(TechType.Sailing))
|
||
return false;
|
||
if (gridData.Terrain == TerrainType.ShallowSea && !playerData.TechTree.CheckIfHasTech(TechType.Fishing))
|
||
return false;
|
||
if (gridData.Feature == TerrainFeature.Mountain && !playerData.TechTree.CheckIfHasTech(TechType.Climbing))
|
||
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<GridData> queue = new Queue<GridData>();
|
||
HashSet<uint> visited = new HashSet<uint>();
|
||
Dictionary<uint, int> steps = new Dictionary<uint, int>();
|
||
|
||
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 UpdateAroundAfterUnitDie(MapData mapData, GridData grid)
|
||
{
|
||
//如果不是真map,不操作
|
||
if (mapData != Main.MapData) return false;
|
||
var set = mapData.GridMap.GetAroundGridDataSet(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.RenderMark = true;
|
||
}
|
||
return true;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
} |