2025-06-24 00:34:20 +08:00

646 lines
25 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 UnityEngine;
using TMPro;
using RuntimeData;
using Logic;
using Animancer;
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;
//------ 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 float _needWaitTime = 0f;
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>();
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)
{
unitSprite.localScale = new Vector3(1, 1, 1);
}
// 如果向右上方移动(y1-y2 < x1-x2),保持默认朝向
else if(dy < dx)
{
unitSprite.localScale = new Vector3(-1, 1, 1);
}
}
}
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,_unitData.AttackRenderMarkWaitTime);
}
// 添加调试输出
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 (_needWaitTime > 0)
{
_needWaitTime -= Time.deltaTime;
}
//如果处在攻击对方状态
else 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)
{
_main.MapRenderer.ProjectileManager.CreateProjectile(_main,_mapData,_attackBackPos,_attackTargetPos,ProjectileType.Arrow,ProjectileMoveType.Parabola);
_needAttack = false;
}
else if (_isAttackBomb)
{
_main.MapRenderer.ProjectileManager.CreateProjectile(_main,_mapData,_attackBackPos,_attackTargetPos,ProjectileType.Bomb,ProjectileMoveType.Parabola);
_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;
_healthText.text = _unitData.Health.ToString();
//处理血量的颜色。如果血量<一半且<5那么赋予红色否则白色
if(_unitData.Health < 5 && _unitData.Health < _unitData.GetMaxHealth() / 2)
_healthText.color = Color.red;
else
_healthText.color = Color.white;
//更新图像
RenderUpdateUnitSrpite();
//更新防御显示信息
RenderUpdateUnitDefense();
//更新高亮
RenderUpdateUnitGlow();
//更新攻击防御的显示
//RenderUpdateAtkDfs();
//初拉单位和selfplayer的视野关系
RenderUpdateSelfPlayerSight();
//DebugRender
if (DebugCenter.Instance.DebugMode)
RenderUpdateDebug();
}
public void RenderUpdateAtkDfs()
{
_mapData.UnitMap.GetUnitDataByUnitId(_unitId, out var unitData);
float attackRaw = unitData.GetAttackValue(_mapData);
float defenseRaw = unitData.GetDefenseValue(_mapData);
//Debug.Log($"[RenderUpdateAtkDfs] Unit {_unitId} | Attack = {attackRaw}");
//Debug.Log($"[RenderUpdateAtkDfs] Unit {_unitId} | Defense = {defenseRaw}");
UpdateAtkGroup(
_ROUnit.transform.Find("Canvas/AttackGroup"),
attackRaw,
attackSprite,
attackHalfSprite
);
UpdateDefGroup(
_ROUnit.transform.Find("Canvas/DefenseGroup"),
defenseRaw,
defenseSprite,
defenseHalfSprite
);
}
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(!_RODebugText.activeSelf)
_RODebugText.SetActive(true);
_debugText.text = _unitData.Id.ToString();
}
public void RenderUpdateSelfPlayerSight()
{
if (_needMove)
return;
if (!_mapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(_gridData.Id))
_ROUnit.SetActive(false); //如果不在玩家视野,就暂时隐藏这个单位的显示
else
_ROUnit.SetActive(true);
}
public void RenderUpdateUnitGlow() //0=gray 1=glow 2=origin
{
if (_playerData != _mapData.PlayerMap.SelfPlayerData)
return;
if ((_unitData.AP > 0 && Main.UnitLogic.AttackTargetCount(_mapData,_unitData) > 0)
|| _unitData.MP > 0
|| (_unitData.CP > 0))
{
if (!Table.Instance.UnitTypeDataAssets.GetUnitSpriteGlow(_mapData, _unitData, out var sprite))
return;
_spriteRenderer.sprite = sprite;
}
else
{
if (!Table.Instance.UnitTypeDataAssets.GetUnitSprite(_mapData, _unitData, out var sprite))
return;
_spriteRenderer.sprite = sprite;
}
}
public void RenderUpdataHighlight()
{
_attackHighlight.SetActive(IsAttackHighlight);
_selectHighlight.SetActive(IsSelectHighlight);
}
public void SetSelectHighlight(bool v)
{
if (IsSelectHighlight == v)
return;
IsSelectHighlight = v;
_main.MapRenderer.HighlightUnitIdSet.Add(_unitId);
_main.MapRenderer.HighlightUnitIdSetRenderMark = true;
}
public void SetAttackHighlight(bool v)
{
if (IsAttackHighlight == v)
return;
IsAttackHighlight = v;
_main.MapRenderer.HighlightUnitIdSet.Add(_unitId);
_main.MapRenderer.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, float needWaitTime = 0f)
{
//如果有其他动画还没播放完,先复位
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;
_needWaitTime = needWaitTime;
}
else if (attackAnimType == AttackAnimType.Arrow)
{
_isAttackArrow = true;
_isAttackBack = false;
_needWaitTime = needWaitTime;
}
else if (attackAnimType == AttackAnimType.Bomb)
{
_isAttackBomb = true;
_isAttackBack = false;
_needWaitTime = needWaitTime;
}
_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)
{
unitSprite.localScale = new Vector3(1, 1, 1);
}
else if(_attackBackPos.x > targetPos.x)
{
unitSprite.localScale = new Vector3(-1, 1, 1);
}
}
}
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);
}
}
}