TH1/Unity/Assets/Scripts/TH1Renderer/UnitRenderer.cs
2025-08-15 00:39:22 +08:00

698 lines
27 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.ComponentModel.Design;
using UnityEngine;
using TMPro;
using RuntimeData;
using Logic;
using Animancer;
using ET.Server;
using Logic.Multilingual;
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<Sprite>("ArtResources/TH1UI/Unit/attack");
attackHalfSprite = Resources.Load<Sprite>("ArtResources/TH1UI/Unit/attackhalf");
defenseSprite = Resources.Load<Sprite>("ArtResources/TH1UI/Unit/defense");
defenseHalfSprite = Resources.Load<Sprite>("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<SpriteRenderer>();
_healthText = _ROUnit.transform.Find("Canvas").transform.Find("Health").GetComponent<TextMeshProUGUI>();
_debugText = _ROUnit.transform.Find("Canvas").transform.Find("DebugText").GetComponent<TextMeshProUGUI>();
_unitInfoBGImg = _ROUnit.transform.Find("BG")?.GetComponent<SpriteRenderer>();
_unitInfoName = _ROUnit.transform.Find("Canvas/Name")?.GetComponent<TextMeshProUGUI>();
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<TextMeshProUGUI>();
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);
}
private void UpdateAtkGroup(Transform group, float rawValue, Sprite fullSprite, Sprite halfSprite)
{
int displayCount = Mathf.CeilToInt(rawValue);
bool useHalfForLast = !Mathf.Approximately(rawValue, displayCount);
foreach (Transform row in group)
{
row.gameObject.SetActive(false);
foreach (Transform icon in row)
{
icon.gameObject.SetActive(false);
}
}
if (displayCount <= 0) return;
int shown = 0;
foreach (Transform row in group)
{
bool rowHasVisible = false;
foreach (Transform icon in row)
{
if (shown >= displayCount) break;
icon.gameObject.SetActive(true);
var img = icon.GetComponent<UnityEngine.UI.Image>();
if (img != null)
{
img.sprite = (useHalfForLast && shown == displayCount - 1) ? halfSprite : fullSprite;
}
shown++;
rowHasVisible = true;
}
row.gameObject.SetActive(rowHasVisible);
if (shown >= displayCount) break;
}
}
private void UpdateDefGroup(Transform group, float rawValue, Sprite fullSprite, Sprite halfSprite)
{
int displayCount = Mathf.CeilToInt(rawValue);
bool useHalfForLast = !Mathf.Approximately(rawValue, displayCount);
// Step 1: 关闭所有 DefenseX
foreach (Transform row in group)
{
row.gameObject.SetActive(false);
foreach (Transform icon in row)
{
icon.gameObject.SetActive(false);
}
}
// Step 2: 开启 Defense1 ~ DefenseN
for (int i = 1; i <= displayCount; i++)
{
string iconName = $"Defense{i}";
Transform icon = FindDeepChild(group, iconName);
if (icon == null)
{
Debug.LogWarning($"[UnitRenderer] Missing icon: {iconName}");
continue;
}
var img = icon.GetComponent<UnityEngine.UI.Image>();
if (img != null)
{
img.sprite = (useHalfForLast && i == displayCount) ? halfSprite : fullSprite;
}
icon.gameObject.SetActive(true);
icon.parent.gameObject.SetActive(true); // 激活所在 Row
}
}
private Transform FindDeepChild(Transform parent, string name)
{
foreach (Transform child in parent)
{
if (child.name == name) return child;
var result = FindDeepChild(child, name);
if (result != null) return result;
}
return null;
}
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);
//如果周围有可以攻击的目标,或者可以移动的目标,或者说可以占领城市
if (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);
}
}
}