using System.ComponentModel.Design; using UnityEngine; using TMPro; using RuntimeData; using Logic; using Animancer; using ET.Server; using Logic.Multilingual; using TH1_Logic.Core; using TH1Resource; using UnityEngine.UI; namespace TH1Renderer { public class UnitRenderer { private MapData _mapData; private Main _main; private uint _unitId; private GameObject _ROUnit; private SpriteRenderer _spriteRenderer; private TextMeshProUGUI _healthText; private GameObject _RODebugText; private TextMeshProUGUI _debugText; private SpriteRenderer _unitInfoBGImg; private TextMeshProUGUI _unitInfoName; //------ defense和attack值 显示相关的资源 ------// private Sprite attackSprite; private Sprite attackHalfSprite; private Sprite defenseSprite; private Sprite defenseHalfSprite; //------- 表现层RenderData ---------// public bool IsAttackHighlight = false; public bool IsSelectHighlight = false; //------- Update缓存数据 -------// private UnitData _unitData; private GridData _gridData; private PlayerData _playerData; private Vector3 originalPosition; // 记录初始位置 public Material shadowMaterial; // 指向你创建的阴影 Material public Vector2 shadowOffset = new Vector2(0.1f, -0.1f); // 阴影偏移 public float shadowSoftness = 0.5f; // 阴影柔和程度 SpriteEffectController effectController; //------bounce相关参数-------- bool _needBounce = false, _isBounceDown = true; Vector3 bounceUpPos, bounceDownPos; private float _bounceWaitTime = 0f; float bounceTime = 0f; float bounceDownFullTime = 0.05f; float bounceUpFullTime = 0.1f; bool _needMove = false; Vector3 moveStartPos, moveEndPos; float moveTime = 0f; float moveFullTime = 0.3f; bool renderVeteran = false; private bool _needAttack = false; private AttackAnimType _attackAnimType = AttackAnimType.None; private float _attackTime = 0f; private bool _isAttackGo = false; private bool _isAttackBack = false; private bool _isAttackArrow = false; private bool _isAttackBomb = false; private bool _isAttackRemilia = false; private bool _isAttackPatchouli = false; private Vector3 _attackTargetPos, _attackBackPos; private bool _needBack; private float _attackGoFullTime = 0.15f; private float _attackBackFullTime = 0.15f; private GameObject _attackHighlight; private GameObject _selectHighlight; public UnitRenderer(GameObject prefab,Transform father, uint uid, MapData mapData, Main main) { _mapData = mapData; _main = main; _unitId = uid; _mapData.UnitMap.GetUnitDataByUnitId(uid,out _unitData); _mapData.GetGridDataByUnitId(uid,out _gridData); _mapData.GetPlayerDataByUnitId(uid,out _playerData); attackSprite = Resources.Load("ArtResources/TH1UI/Unit/attack"); attackHalfSprite = Resources.Load("ArtResources/TH1UI/Unit/attackhalf"); defenseSprite = Resources.Load("ArtResources/TH1UI/Unit/defense"); defenseHalfSprite = Resources.Load("ArtResources/TH1UI/Unit/defensehalf"); Vector3 tpos = Table.Instance.GridToWorld(_gridData,"isUnit"); _ROUnit = GameObject.Instantiate(prefab, tpos, Quaternion.identity, father); _RODebugText = _ROUnit.transform.Find("Canvas").transform.Find("DebugText").gameObject; _attackHighlight = _ROUnit.transform.Find("AttackHighlight").gameObject; _selectHighlight = _ROUnit.transform.Find("SelectHighlight").gameObject; _spriteRenderer = _ROUnit.transform.Find("UnitSprite").transform.GetComponent(); _healthText = _ROUnit.transform.Find("Canvas").transform.Find("Health").GetComponent(); _debugText = _ROUnit.transform.Find("Canvas").transform.Find("DebugText").GetComponent(); _unitInfoBGImg = _ROUnit.transform.Find("BG")?.GetComponent(); _unitInfoName = _ROUnit.transform.Find("Canvas/Name")?.GetComponent(); RenderUpdateUnitAll(); } public GameObject GetROUnit() { return _ROUnit; } public void Die() { GameObject.Destroy(_ROUnit.gameObject); } public void CheckUpgradeOrAnimate() { if (_unitData.Veteran && !renderVeteran) renderVeteran = true; TextMeshProUGUI tmptext = _ROUnit.transform.Find("Canvas").transform.Find("Health").GetComponent(); tmptext.text = _unitData.Health.ToString(); } public void PlayBounceAnim() { _needBounce = true; _isBounceDown = true; bounceTime = 0f; bounceUpPos = _ROUnit.transform.position; bounceDownPos = new Vector3(_ROUnit.transform.position.x, _ROUnit.transform.position.y - 0.8f, _ROUnit.transform.position.z); } public void PlayMoveAnim(GridData endGridData, float fullTime) { //Debug.Log($"Unit {_unitId} PlayMoveAnim called: from {Table.Instance.WorldToGrid(_ROUnit.transform.position, "isUnit")} to {new Vector2Int((int)endGridData.Pos.X, (int)endGridData.Pos.Y)}, fullTime={fullTime}"); _needMove = true; moveFullTime = fullTime * DebugCenter.Instance.AnimationSpeed; moveEndPos = Table.Instance.GridToWorld(endGridData,"isUnit"); moveStartPos = _ROUnit.transform.position; moveTime = 0f; // 获取起始和结束的网格坐标 Vector2Int startPos = Table.Instance.WorldToGrid(moveStartPos, "isUnit"); Vector2Int endPos = new Vector2Int((int)endGridData.Pos.X, (int)endGridData.Pos.Y); // 计算移动方向并设置朝向 int dx = startPos.x - endPos.x; int dy = startPos.y - endPos.y; // 获取UnitSprite组件 Transform unitSprite = _ROUnit.transform.Find("UnitSprite"); if (unitSprite != null) { // 如果向左上方移动(y1-y2 > x1-x2),翻转朝向 if (dy > dx) { var v3 = unitSprite.localScale; v3.x = Mathf.Abs(v3.x); unitSprite.localScale = v3; } // 如果向右上方移动(y1-y2 < x1-x2),保持默认朝向 else if(dy < dx) { var v3 = unitSprite.localScale; v3.x = -Mathf.Abs(v3.x); unitSprite.localScale = v3; } } } public void Update() { //没有任何需要变化的情况,直接return if (!_needBounce && !_needMove && !_needAttack &&!_unitData.RenderMark && !_unitData.AttackRenderMark && !_unitData.BounceRenderMark) return; GridData curGridData = _gridData; _mapData.UnitMap.GetUnitDataByUnitId(_unitId,out _unitData); _mapData.GetGridDataByUnitId(_unitId,out _gridData); _mapData.GetPlayerDataByUnitId(_unitId,out _playerData); //先处理_unitData的各种renderMark if (_unitData.BounceRenderMark) { _unitData.BounceRenderMark = false; SetBounceAnim(_unitData.BounceRenderMakrWaitTime); _unitData.BounceRenderMakrWaitTime = 0f; } if (_unitData.RenderMark) { _unitData.RenderMark = false; //如果已经死亡 if (!_unitData.Alive) _ROUnit.SetActive(false); else { //如果需要移动 if (_gridData != curGridData) { bool startSight = _mapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(curGridData.Id); bool endSight = _mapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(_gridData.Id); //如果移动开始和结束的位置没有视野,直接重置位置 if (!startSight && !endSight) _ROUnit.transform.position = Table.Instance.GridToWorld(_gridData,"isUnit"); //如果开始没视野,结束有视野,先setActive true else if (!startSight && endSight) { _ROUnit.SetActive(true); PlayMoveAnim(_gridData, Table.Instance.AnimDataAssets.MoveAnimTime); } //如果开始有视野,结束没视野,或者开始结束都有视野 ,交给move结束的时候判断,结束时会根据有没有视野的情况设置自己的active else if(startSight) { _ROUnit.SetActive(true); PlayMoveAnim(_gridData, Table.Instance.AnimDataAssets.MoveAnimTime); } } RenderUpdateUnitAll(); } } else if (_unitData.AttackRenderMark) { _unitData.AttackRenderMark = false; SetAttackAnim(_unitData.AttackRenderMarkAttackAnimType,_unitData.AttackRenderMarkTargetPos,_unitData.AttackRenderMarkNeedBack); } // 添加调试输出 if (_gridData != curGridData) { //Debug.Log($"Unit {_unitId} grid changed: {curGridData.Id}->{_gridData.Id}, needMove={_needMove}, RenderMark={_unitData.RenderMark}"); } //处理attack bounce 和move 动画,优先move,然后attack 最后再bounce 三者冲突,必须用if else 独立处理 if (_needMove) { //Debug.Log($"Moving from {moveStartPos} to {moveEndPos}" ); moveTime += Time.deltaTime / moveFullTime; _ROUnit.transform.position = Vector3.Lerp(moveStartPos, moveEndPos, moveTime); if (moveTime >= 1f) { //Debug.Log($"Unit {_unitId} move completed. In sight: {_mapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(_gridData.Id)}"); _needMove = false; Vector2Int tt = Table.Instance.WorldToGrid(_ROUnit.transform.position, "isUnit"); if (!_mapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(_gridData.Id)) _ROUnit.SetActive(false); } } else if (_needAttack) { //如果处在攻击对方状态 if (_isAttackGo) { _attackTime += Time.deltaTime / _attackGoFullTime; if(_needBack) _ROUnit.transform.position = Vector3.Lerp(_attackBackPos, _attackTargetPos, _attackTime * 0.7f); else _ROUnit.transform.position = Vector3.Lerp(_attackBackPos, _attackTargetPos, _attackTime); if (_attackTime >= 1f) { _isAttackGo = false; if (_needBack) { _isAttackBack = true; _attackTime = 0f; } else _needAttack = false; } } //如果已经攻击完毕正在回程 else if (_isAttackBack) { _attackTime += Time.deltaTime / _attackBackFullTime; _ROUnit.transform.position = Vector3.Lerp(_attackTargetPos, _attackBackPos, _attackTime * 0.7f + 0.3f); if (_attackTime >= 1f) { _isAttackBack = false; _needAttack = false; } } //如果远程arrow else if (_isAttackArrow) { MapRenderer.Instance.ProjectileManager.CreateProjectile(_attackBackPos,_attackTargetPos,ProjectileType.Arrow,ProjectileMoveType.Parabola); _isAttackArrow = false; _needAttack = false; } else if (_isAttackBomb) { MapRenderer.Instance.ProjectileManager.CreateProjectile(_attackBackPos,_attackTargetPos,ProjectileType.Bomb,ProjectileMoveType.Parabola); _isAttackBomb = false; _needAttack = false; } else if (_isAttackRemilia) { MapRenderer.Instance.ProjectileManager.CreateProjectile(_attackBackPos,_attackTargetPos,ProjectileType.RemiliaAttack,ProjectileMoveType.Straight); _isAttackRemilia = false; _needAttack = false; } else if (_isAttackPatchouli) { MapRenderer.Instance.ProjectileManager.CreateProjectile(_attackBackPos,_attackTargetPos,ProjectileType.PatchouliAttack,ProjectileMoveType.Straight); _isAttackPatchouli = false; _needAttack = false; } } else if (_needBounce) { if (_bounceWaitTime > 0){ _bounceWaitTime -= Time.deltaTime; } else if (_isBounceDown) { bounceTime += Time.deltaTime / bounceDownFullTime; _ROUnit.transform.position = Vector3.Lerp(bounceUpPos, bounceDownPos, bounceTime); if (bounceTime >= 1f) { _isBounceDown = false; bounceTime = 0f; } } else { bounceTime += Time.deltaTime / bounceUpFullTime; _ROUnit.transform.position = Vector3.Lerp(bounceDownPos, bounceUpPos, bounceTime); if (bounceTime >= 1f) _needBounce = false; } } } public void RenderUpdateUnitAll() { renderVeteran = _unitData.Veteran; RenderUpdateUnitInfo(); //更新图像 RenderUpdateUnitSrpite(); //更新防御显示信息 RenderUpdateUnitDefense(); //更新高亮 RenderUpdateUnitGlow(); //初拉单位和selfplayer的视野关系 RenderUpdateSelfPlayerSight(); //DebugRender RenderUpdateDebug(); } public void RenderUpdateUnitInfo() { _healthText.text = _unitData.Health.ToString(); //处理血量的颜色。如果血量<一半且<5,那么赋予红色,否则白色 if(_unitData.Health < 5 && _unitData.Health < _unitData.GetMaxHealth() / 2) _healthText.color = Color.red; else _healthText.color = Color.white; //根据敌我情况更新infoBG的颜色 _unitInfoBGImg.sprite = (_playerData == _mapData.PlayerMap.SelfPlayerData) ? ResourceCache.Instance.SpriteCache.UnitInfoSelf : ResourceCache.Instance.SpriteCache.UnitInfoEnemy; //更改兵种显示文字 if(Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(_unitData.UnitType,_unitData.GiantType,_unitData.UnitLevel,out var info)) MultilingualManager.Instance.SetUIText(_unitInfoName,info.Name); } public void RenderUpdateDebug() { if (DebugCenter.Instance.DebugMode) { if(!_RODebugText.activeSelf) _RODebugText.SetActive(true); } else { _RODebugText.SetActive(false); return; } _debugText.text = ""; _debugText.text += $"ID{_unitId} AP{_unitData.AP} MP{_unitData.MP} CP{_unitData.MP}\n"; //如果不是我方单位,显示军团及unit的战略 _debugText.text += $"Lid={_unitData.LegionId}\n"; if(!_mapData.CheckUnitIdBelongPlayerId(_unitData.Id,_mapData.PlayerMap.SelfPlayerData.Id)) if (MainEditor.Instance.Data != null) { MainEditor.Instance.GetUnitStrategy(_unitId, _unitData.LegionId, _playerData.Id, out var st, out var tar, out var type); _debugText.text += $"ST:{st} TAR:{tar} TYPE:{type}"; } } public void RenderUpdateSelfPlayerSight() { if (_needMove) return; if (!_mapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(_gridData.Id)) _ROUnit.SetActive(false); //如果不在玩家视野,就暂时隐藏这个单位的显示 else _ROUnit.SetActive(true); } public void RenderUpdateUnitGlow() { bool isSelfPlayer = (_playerData == _mapData.PlayerMap.SelfPlayerData); //如果MP>0 或者周围有可以攻击的目标,或者可以移动的目标,或者说可以占领城市 if (_unitData.MP > 0 || MapRenderer.Instance.CheckUnitHasMoveAttackTarget(_unitId) || MapRenderer.Instance.CheckUnitHasSpecialUnitActionTarget(_unitId)) { Sprite sprite; if (!Table.Instance.UnitTypeDataAssets.GetUnitSprite(_mapData, _unitData, out sprite)) return; _spriteRenderer.sprite = sprite; _spriteRenderer.material = ResourceCache.Instance.MatCache.TH1URPShaders_Default; if (isSelfPlayer) _spriteRenderer.material = ResourceCache.Instance.MatCache.TH1URPShaders_Sprite_Glow; } else { if (!Table.Instance.UnitTypeDataAssets.GetUnitSprite(_mapData, _unitData, out var sprite)) return; _spriteRenderer.sprite = sprite; _spriteRenderer.material = ResourceCache.Instance.MatCache.TH1URPShaders_Sprite_WhiteOverlay; } } public void RenderUpdataHighlight() { _attackHighlight.SetActive(IsAttackHighlight); _selectHighlight.SetActive(IsSelectHighlight); } public void SetSelectHighlight(bool v) { if (IsSelectHighlight == v) return; IsSelectHighlight = v; MapRenderer.Instance.HighlightUnitIdSet.Add(_unitId); MapRenderer.Instance.HighlightUnitIdSetRenderMark = true; } public void SetAttackHighlight(bool v) { if (IsAttackHighlight == v) return; IsAttackHighlight = v; MapRenderer.Instance.HighlightUnitIdSet.Add(_unitId); MapRenderer.Instance.HighlightUnitIdSetRenderMark = true; } public void RenderUpdateUnitSrpite() { if (!Table.Instance.UnitTypeDataAssets.GetUnitSprite(_mapData, _unitData, out var sprite)) return; _spriteRenderer.sprite = sprite; } public void RenderUpdateUnitDefense() { float defenseBonus = _unitData.GetExtraDefense(_mapData); switch (defenseBonus) { case 1.0f: _ROUnit.transform.Find("Defense").gameObject.SetActive(false); _ROUnit.transform.Find("SuperDefense").gameObject.SetActive(false); break; case 1.5f: _ROUnit.transform.Find("Defense").gameObject.SetActive(true); _ROUnit.transform.Find("SuperDefense").gameObject.SetActive(false); break; case 4.0f: _ROUnit.transform.Find("Defense").gameObject.SetActive(false); _ROUnit.transform.Find("SuperDefense").gameObject.SetActive(true); break; } } public void SetAttackAnim(AttackAnimType attackAnimType, Vector3 targetPos,bool needBack = true) { //如果有其他动画还没播放完,先复位 if (_needAttack) { _ROUnit.transform.position = _attackBackPos; _needAttack = false; } if (_needBounce) { _ROUnit.transform.position = bounceUpPos; _needBounce = false; } if (_needMove) { _ROUnit.transform.position = moveEndPos; _needMove = false; } _needAttack = true; _attackAnimType = attackAnimType; _needBack = needBack; _attackTime = 0f; if (attackAnimType == AttackAnimType.Melee) { _isAttackGo = true; _isAttackBack = false; } else if (attackAnimType == AttackAnimType.Arrow) { _isAttackArrow = true; _isAttackBack = false; } else if (attackAnimType == AttackAnimType.Bomb) { _isAttackBomb = true; _isAttackBack = false; } else if (attackAnimType == AttackAnimType.RemiliaAttack) { _isAttackRemilia = true; _isAttackBack = false; } else if (attackAnimType == AttackAnimType.PatchouliAttack) { _isAttackPatchouli = true; _isAttackBack = false; } _attackTargetPos = targetPos; _attackBackPos = _ROUnit.transform.position; // 获取UnitSprite组件 Transform unitSprite = _ROUnit.transform.Find("UnitSprite"); if (unitSprite != null) { Debug.Log(_attackBackPos + " : " + targetPos); if (_attackBackPos.x < targetPos.x) { var v3 = unitSprite.localScale; v3.x = Mathf.Abs(v3.x); unitSprite.localScale = v3; } else if(_attackBackPos.x > targetPos.x) { var v3 = unitSprite.localScale; v3.x = -Mathf.Abs(v3.x); unitSprite.localScale = v3; } } } public void SetBounceAnim(float waitTime = 0f) { if (_needBounce) return; _bounceWaitTime = waitTime; _needBounce = true; _isBounceDown = true; bounceTime = 0f; bounceUpPos = _ROUnit.transform.position; bounceDownPos = new Vector3(_ROUnit.transform.position.x, _ROUnit.transform.position.y - 0.8f, _ROUnit.transform.position.z); } } }