562 lines
21 KiB
C#
562 lines
21 KiB
C#
using System.Collections.Generic;
|
||
using Logic;
|
||
using Logic.Multilingual;
|
||
using Logic.Skill;
|
||
using RuntimeData;
|
||
using TH1_Anim.UnitAtomAnim;
|
||
using TH1_Logic.Core;
|
||
using TH1Renderer;
|
||
using TH1Resource;
|
||
using TMPro;
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
|
||
namespace TH1_Renderer
|
||
{
|
||
public class UnitRenderer
|
||
{
|
||
private uint _unitId;
|
||
private UnitData _unitData;
|
||
private PlayerData _playerData;
|
||
private GridData _gridData;
|
||
private GameObject _ROUnit;
|
||
private UnitMono _unitMono;
|
||
|
||
//------- 表现层RenderData ---------//
|
||
public bool IsAttackHighlight = false;
|
||
public bool IsSelectHighlight = false;
|
||
public bool IsAllyHighlight = false;
|
||
|
||
//------- Status Area ---------//
|
||
private UnitStatusArea _statusArea;
|
||
|
||
/// <summary>
|
||
/// 需要显示为单位负面状态的技能类型列表
|
||
/// </summary>
|
||
private readonly List<SkillType> _negativeStatusSkillTypes = new List<SkillType>
|
||
{
|
||
SkillType.KomeijiFear, // 古明地恐惧
|
||
SkillType.SkillBan, // 技能封印
|
||
};
|
||
|
||
//--------------- AnimManager ----------------
|
||
public UnitAnimManager AnimManager;
|
||
|
||
|
||
private Vector3 originalPosition; // 记录初始位置
|
||
|
||
SpriteEffectController effectController;
|
||
|
||
//------bounce相关参数--------
|
||
bool _needBounce = false, _isBounceDown = true;
|
||
Vector3 bounceUpPos, bounceDownPos;
|
||
private float _bounceWaitTime = 0f;
|
||
float bounceTime = 0f;
|
||
|
||
|
||
bool _needMove = false;
|
||
Vector3 moveStartPos, moveEndPos;
|
||
float moveTime = 0f;
|
||
float moveFullTime = 0.3f;
|
||
bool renderVeteran = false;
|
||
private bool _isGlow = false;
|
||
private bool _isHideState = 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 bool _isAttackMokou = false;
|
||
private bool _isAttackReisen = false;
|
||
private bool _isAttackKaguya = false;
|
||
|
||
|
||
private Vector3 _attackTargetPos, _attackBackPos;
|
||
private bool _needBack;
|
||
private float _attackGoFullTime = 0.15f;
|
||
private float _attackBackFullTime = 0.15f;
|
||
|
||
public UnitRenderer(GameObject prefab,Transform father, uint uid)
|
||
{
|
||
_unitId = uid;
|
||
Main.MapData.UnitMap.GetUnitDataByUnitId(uid,out _unitData);
|
||
Main.MapData.GetPlayerDataByUnitId(uid, out _playerData);
|
||
Main.MapData.GetGridDataByUnitId(uid,out _gridData);
|
||
|
||
AnimManager = new UnitAnimManager();
|
||
|
||
Vector3 tpos = Table.Instance.GridToWorld(_gridData,"isUnit");
|
||
_ROUnit = GameObject.Instantiate(prefab, tpos, Quaternion.identity, father);
|
||
_unitMono = _ROUnit?.GetComponent<UnitMono>();
|
||
|
||
// 初始化 StatusArea
|
||
if (_unitMono?.StatusAreaContainer != null && _unitMono?.StatusIconPrefab != null)
|
||
{
|
||
_statusArea = new UnitStatusArea(_unitId, _unitMono.StatusAreaContainer, _unitMono.StatusIconPrefab);
|
||
}
|
||
|
||
//InstantUpdateUnit(false);
|
||
|
||
}
|
||
|
||
public void Die()
|
||
{
|
||
//_ROUnit.gameObject.SetActive(false);
|
||
//Debug.Log("Delete !!!" + _unitData.Id + " name = " + _ROUnit.name + "; ID = " + _ROUnit.GetInstanceID());
|
||
//Debug.Log(_ROUnit.transform.Find("UnitSprite").GetComponent<SpriteRenderer>().sprite);
|
||
//_unitMono = null;
|
||
//Debug.Break();
|
||
|
||
// 清理状态区域
|
||
_statusArea?.ClearAllStatus();
|
||
|
||
GameObject.Destroy(_ROUnit.gameObject);
|
||
|
||
if(MapRenderer.Instance.ROUnitMap.TryGetValue(_unitId,out var _))
|
||
MapRenderer.Instance.ROUnitMap.Remove(_unitId);
|
||
_unitData = null;
|
||
}
|
||
|
||
#region [-------------------- Status Area Management --------------------]
|
||
|
||
/// <summary>
|
||
/// 添加或更新单位的负面状态显示
|
||
/// </summary>
|
||
/// <param name="skillType">技能类型</param>
|
||
/// <param name="level">技能等级/层数</param>
|
||
public void AddOrUpdateStatus(SkillType skillType, int level = 1)
|
||
{
|
||
if (_statusArea == null) return;
|
||
if (!Table.Instance.SkillDataAssets.GetSkillInfo(skillType, out var skillInfo)) return;
|
||
|
||
_statusArea.AddOrUpdateStatus(skillType, level, skillInfo.SkillIcon);
|
||
_statusArea.RefreshLayout();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 移除指定状态显示
|
||
/// </summary>
|
||
public void RemoveStatus(SkillType skillType)
|
||
{
|
||
_statusArea?.RemoveStatus(skillType);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清除所有状态显示
|
||
/// </summary>
|
||
public void ClearAllStatus()
|
||
{
|
||
_statusArea?.ClearAllStatus();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 同步单位的技能数据到状态显示区域
|
||
/// 只显示 _negativeStatusSkillTypes 列表中定义的负面技能
|
||
/// </summary>
|
||
public void SyncStatusWithUnitSkills()
|
||
{
|
||
if (_statusArea == null || _unitData == null) return;
|
||
|
||
_statusArea.SyncWithUnitSkills(_unitData, _negativeStatusSkillTypes);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查是否正在显示指定状态
|
||
/// </summary>
|
||
public bool HasStatusDisplay(SkillType skillType)
|
||
{
|
||
return _statusArea?.HasStatus(skillType) ?? false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置状态区域是否可见
|
||
/// </summary>
|
||
public void SetStatusAreaVisible(bool visible)
|
||
{
|
||
_statusArea?.SetAreaVisible(visible);
|
||
}
|
||
|
||
#endregion
|
||
|
||
public void InstantDisappear()
|
||
{
|
||
_ROUnit.SetActive(false);
|
||
}
|
||
|
||
public void InstantShow()
|
||
{
|
||
_ROUnit.SetActive(true);
|
||
}
|
||
|
||
public void Update()
|
||
{
|
||
AnimManager?.Update(_unitMono);
|
||
}
|
||
|
||
public void RenderUpdateUnitDefense()
|
||
{
|
||
if (_unitData == null || !_unitData.IsAlive()) return;
|
||
_unitMono.UpdateUnitDefense(_unitData.GetDefenseMultiplicationParamOnlyForDefenseShow(Main.MapData));
|
||
}
|
||
|
||
public void RenderUpdateUnitImage()
|
||
{
|
||
if (_unitData == null ) return;
|
||
|
||
RenderUpdateUnitInfo();
|
||
|
||
//更新图像
|
||
RenderUpdateUnitSprite();
|
||
//更新防御显示信息
|
||
RenderUpdateUnitDefense();
|
||
//更新高亮
|
||
RenderUpdateUnitGlow();
|
||
//同步负面状态显示
|
||
SyncStatusWithUnitSkills();
|
||
//更新隐身透明度
|
||
RenderUpdateHideState();
|
||
//更新周围隐身提示
|
||
RenderUpdateHideAround();
|
||
//DebugRender
|
||
RenderUpdateDebug();
|
||
}
|
||
|
||
//这个会同时处理非player mapData的情况
|
||
//瞬间更新unit的视觉(不包括pos,不包括die,但是包括showoff) showoff开关,如果=true,先判断是否显隐,更新显隐,再决定更新image 如果=false,仅更新image,不处理显隐
|
||
//如果unit死了,不能直接die!!要等动画那边主动凋起才可以die
|
||
public bool InstantUpdateUnit(bool showoff)
|
||
{
|
||
//如果要做显隐更新,先判断显隐,显的情况下,再更新image
|
||
//如果不做显隐更新,直接更新image
|
||
if ((showoff && RenderUpdateUnitShowOff())
|
||
|| !showoff)
|
||
RenderUpdateUnitImage();
|
||
return _unitData != null && _unitData.InMainSight();
|
||
|
||
}
|
||
|
||
//瞬间更新unit的 die的情况
|
||
public bool InstantUpdateTryDie()
|
||
{
|
||
if (_unitData == null || !_unitData.IsAlive())
|
||
{
|
||
Die();
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
//瞬间更新unit的pos到最新 最正确的位置
|
||
public void InstantUpdateUnitPos()
|
||
{
|
||
RenderUpdateUnitPosition();
|
||
}
|
||
|
||
public void RenderUpdateUnitInfo()
|
||
{
|
||
if (!_unitMono?.HealthText || !_unitMono?.UnitInfoBG) return;
|
||
_unitMono.HealthText.text = _unitData.Health.ToString();
|
||
//处理血量的颜色。如果血量<一半且<5,那么赋予红色,否则白色
|
||
if(_unitData.Health < 5 && _unitData.Health < _unitData.GetMaxHealth() / 2)
|
||
_unitMono.HealthText.color = _unitMono.UnitLowHealth;
|
||
else
|
||
_unitMono.HealthText.color = Color.white;
|
||
|
||
Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(_unitData.UnitFullType, out var unitInfo);
|
||
var chessType = unitInfo.ChessType;
|
||
if (chessType == ChessType.None)
|
||
{
|
||
if(Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(_unitData.CarryUnitFullType, out var carryUnitInfo))
|
||
chessType = carryUnitInfo.ChessType;
|
||
}
|
||
if (chessType != ChessType.None)
|
||
{
|
||
Table.Instance.UnitTypeDataAssets.GetChessTypeInfo(chessType ,out var chessInfo);
|
||
_unitMono.ChessImg.sprite = chessInfo.ChessSprite;
|
||
}
|
||
|
||
|
||
//根据敌我情况更新infoBG的颜色
|
||
//_unitInfoBGImg.sprite = (Main.MapData.SameUnion(_playerData.Id , Main.MapData.PlayerMap.SelfPlayerId)) ? ResourceCache.Instance.SpriteCache.UnitInfoSelf :
|
||
if (Main.MapData == null) return;
|
||
if (!Main.MapData.GetPlayerDataByUnitId(_unitData.Id, out var playerData)) return;
|
||
var col = _unitMono.UnitBGBlue;
|
||
if (playerData.Id != Main.MapData.PlayerMap.SelfPlayerId)
|
||
col =(Main.MapData.SameUnion(playerData.Id, Main.MapData.PlayerMap.SelfPlayerId))
|
||
? _unitMono.UnitBGGreen
|
||
: _unitMono.UnitBGRed;
|
||
_unitMono.ChessBG.color = col;
|
||
_unitMono.UnitInfoBG.color = col;
|
||
|
||
|
||
|
||
|
||
|
||
|
||
//如果是盟友,额外显示盟友标记
|
||
|
||
var showUnionIcon = Main.MapData.SameUnion(playerData.Id, Main.MapData.PlayerMap.SelfPlayerId) &&
|
||
playerData.Id != Main.MapData.PlayerMap.SelfPlayerId;
|
||
_unitMono.UnionBG.SetActive(showUnionIcon);
|
||
|
||
//更改兵种显示文字
|
||
if(Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(_unitData.UnitType,_unitData.GiantType,_unitData.UnitLevel,out var info))
|
||
MultilingualManager.Instance.SetUIText(_unitMono.UnitInfoName,info.Name);
|
||
}
|
||
|
||
public void RenderUpdateDebug()
|
||
{
|
||
if (DebugCenter.Instance.DebugMode)
|
||
{
|
||
if(!_unitMono.RODebugText.activeSelf)
|
||
_unitMono.RODebugText.SetActive(true);
|
||
}
|
||
else
|
||
{
|
||
_unitMono.RODebugText.SetActive(false);
|
||
return;
|
||
}
|
||
|
||
_unitMono.DebugText.text = "";
|
||
_unitMono.DebugText.text += $"ID{_unitId} GetActionPoint(ActionPointType.Attack){_unitData.GetActionPoint(ActionPointType.Attack)} GetActionPoint(ActionPointType.Move){_unitData.GetActionPoint(ActionPointType.Move)} GetActionPoint(ActionPointType.Capture){_unitData.GetActionPoint(ActionPointType.Move)}\n";
|
||
|
||
//如果不是我方单位,显示军团及unit的战略
|
||
_unitMono.DebugText.text += $"Lid={_unitData.LegionId}\n";
|
||
if(!Main.MapData.CheckUnitIdBelongPlayerId(_unitData.Id,Main.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);
|
||
_unitMono.DebugText.text += $"ST:{st} TAR:{tar} TYPE:{type}";
|
||
}
|
||
}
|
||
public bool RenderUpdateUnitShowOff()
|
||
{
|
||
if (_unitData == null || _unitMono == null)
|
||
return false;
|
||
bool ret = _unitData.InMainSight();
|
||
//如果在视野内但是敌方隐身单位,对当前玩家不可见
|
||
if (ret && _unitData.IsHideAndCantSee(Main.MapData, Main.MapData.PlayerMap.SelfPlayerData))
|
||
ret = false;
|
||
_unitMono.gameObject.SetActive(ret); //如果不在玩家视野,就暂时隐藏这个单位的显示
|
||
return ret;
|
||
}
|
||
public void RenderUpdateUnitGlow()
|
||
{
|
||
var player = _unitData.Player(Main.MapData);
|
||
if (player == null) return;
|
||
Sprite sprite;
|
||
if (!Table.Instance.UnitTypeDataAssets.GetUnitSprite(Main.MapData, _unitData, out sprite))
|
||
return;
|
||
_unitMono.SpriteRenderer.sprite = sprite;
|
||
_unitMono.SpriteRenderer.material = ResourceCache.Instance.MatCache.TH1URPShaders_Default;
|
||
_isGlow = false;
|
||
|
||
//首先处理玩家(判断是否置灰或者高亮)
|
||
if (player.IsSelfPlayer())
|
||
{
|
||
//如果MP>0 或者周围有可以攻击的目标,或者可以移动的目标,或者说可以占领城市
|
||
if (_unitData.GetActionPoint(ActionPointType.Move) > 0
|
||
|| MapRenderer.Instance.CheckUnitHasMoveAttackTarget(_unitId)
|
||
|| MapRenderer.Instance.CheckUnitHasSpecialUnitActionTarget(_unitId))
|
||
{
|
||
_unitMono.SpriteRenderer.material = ResourceCache.Instance.MatCache.TH1URPShaders_Sprite_Glow;
|
||
_isGlow = true;
|
||
|
||
}
|
||
else
|
||
_unitMono.SpriteRenderer.material = ResourceCache.Instance.MatCache.TH1URPShaders_Sprite_WhiteOverlay;
|
||
}
|
||
//然后处理AI(主要就判断是否置灰)
|
||
else
|
||
{
|
||
//如果是正在行动的AI
|
||
if (Main.MapData.CurPlayer == player)
|
||
{
|
||
if(_unitData.GetActionPoint(ActionPointType.Move) == 0 && _unitData.GetActionPoint(ActionPointType.Capture) == 0 && _unitData.GetActionPoint(ActionPointType.Attack) == 0 && _unitData.GetActionPoint(ActionPointType.Move) == 0)
|
||
_unitMono.SpriteRenderer.material = ResourceCache.Instance.MatCache.TH1URPShaders_Sprite_WhiteOverlay;
|
||
}
|
||
else
|
||
//如果是还没行动的AI
|
||
if(Main.MapData.GetPlayerHasActedInBigTurn(player))
|
||
{
|
||
//啥都不做
|
||
}
|
||
//否则是已经行动过的AI
|
||
else
|
||
{
|
||
_unitMono.SpriteRenderer.material = ResourceCache.Instance.MatCache.TH1URPShaders_Sprite_WhiteOverlay;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
}
|
||
|
||
public void RenderUpdateHideState()
|
||
{
|
||
bool hideState = _unitData.IsHideState(Main.MapData);
|
||
bool isSelfOrAlly = Main.MapData.SameUnion(_playerData.Id, Main.MapData.PlayerMap.SelfPlayerId);
|
||
|
||
// 状态未变时的处理:确保显示状态正确
|
||
if (hideState == _isHideState)
|
||
{
|
||
if (hideState)
|
||
{
|
||
if (isSelfOrAlly)
|
||
{
|
||
var color = _unitMono.SpriteRenderer.color;
|
||
if (!Mathf.Approximately(color.a, 0.5f))
|
||
{
|
||
color.a = 0.5f;
|
||
_unitMono.SpriteRenderer.color = color;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 敌方隐身单位:确保保持隐藏(防止被ShowOff重新显示)
|
||
InstantDisappear();
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
_isHideState = hideState;
|
||
|
||
if (hideState)
|
||
{
|
||
// 进入HideState
|
||
if (isSelfOrAlly)
|
||
{
|
||
// 己方/盟友:显示但半透明(0.5f)
|
||
InstantShow();
|
||
var color = _unitMono.SpriteRenderer.color;
|
||
color.a = 0.5f;
|
||
_unitMono.SpriteRenderer.color = color;
|
||
}
|
||
else
|
||
{
|
||
// 敌方:完全隐藏(SetActive(false))
|
||
InstantDisappear();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 退出HideState:恢复显示(视野系统会决定最终显隐)
|
||
InstantShow();
|
||
var color = _unitMono.SpriteRenderer.color;
|
||
color.a = 1f;
|
||
_unitMono.SpriteRenderer.color = color;
|
||
}
|
||
}
|
||
|
||
public void RenderUpdateHideAround()
|
||
{
|
||
if (_unitMono.HideAround == null) return;
|
||
bool show = false;
|
||
//只对当前玩家自己的单位显示
|
||
var curGrid = _unitData.Grid(Main.MapData);
|
||
if (_playerData != null && _playerData.IsSelfPlayer() && curGrid != null)
|
||
{
|
||
var arounds = Main.MapData.GridMap.GetAroundGridData(1, 1, curGrid);
|
||
foreach (var around in arounds)
|
||
{
|
||
if (around == curGrid) continue;
|
||
if (!around.RealUnit(Main.MapData, out var nearUnit)) continue;
|
||
if (!nearUnit.IsHideState(Main.MapData)) continue;
|
||
//排除自己的单位和同盟单位
|
||
if (Main.MapData.SameUnionByUnitId(_unitData.Id, nearUnit.Id)) continue;
|
||
show = true;
|
||
break;
|
||
}
|
||
}
|
||
_unitMono.HideAround.gameObject.SetActive(show);
|
||
}
|
||
|
||
#region [-------------------- Damage Preview --------------------]
|
||
|
||
public void ShowDamagePreview(int damage, Color color)
|
||
{
|
||
if (_unitMono?.DamagePreviewText == null) return;
|
||
_unitMono.DamagePreviewText.gameObject.SetActive(true);
|
||
_unitMono.DamagePreviewText.text = "-" + damage;
|
||
_unitMono.DamagePreviewText.color = color;
|
||
}
|
||
|
||
public void HideDamagePreview()
|
||
{
|
||
if (_unitMono?.DamagePreviewText == null) return;
|
||
_unitMono.DamagePreviewText.gameObject.SetActive(false);
|
||
}
|
||
|
||
#endregion
|
||
|
||
public void RenderUpdataHighlight()
|
||
{
|
||
_unitMono.AttackHighlight.SetActive(IsAttackHighlight);
|
||
_unitMono.SelectHighlight.SetActive(IsSelectHighlight);
|
||
_unitMono.AllyHighlight.SetActive(IsAllyHighlight);
|
||
}
|
||
|
||
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 SetAllyHighlight(bool v)
|
||
{
|
||
if (IsAllyHighlight == v)
|
||
return;
|
||
IsAllyHighlight = v;
|
||
MapRenderer.Instance.HighlightUnitIdSet.Add(_unitId);
|
||
MapRenderer.Instance.HighlightUnitIdSetRenderMark = true;
|
||
}
|
||
|
||
public void RenderUpdateUnitSprite()
|
||
{
|
||
if (!Table.Instance.UnitTypeDataAssets.GetUnitSprite(Main.MapData, _unitData, out var sprite))
|
||
return;
|
||
_unitMono.SpriteRenderer.sprite = sprite;
|
||
//RenderUpdateUnitSpecialSprite();
|
||
|
||
}
|
||
|
||
|
||
public void RenderUpdateUnitPosition()
|
||
{
|
||
var t = _unitData.Grid(Main.MapData)?.Pos;
|
||
if (t == null) return;
|
||
_unitMono.transform.position = Table.Instance.GridPosToWorld(new Vector2Int(t.X,t.Y),"isUnit");
|
||
}
|
||
|
||
public Vector3 GetPosition()
|
||
{
|
||
return _ROUnit.transform.position;
|
||
}
|
||
|
||
public bool isGlow()
|
||
{
|
||
return _isGlow;
|
||
}
|
||
|
||
}
|
||
} |