Files
Cielonos/Assets/Scripts/MainGame/UI/PlayerUI/EnemyInfo/EnemyInfoUnitBase.cs

190 lines
8.1 KiB
C#
Raw Normal View History

2026-03-20 12:07:44 -04:00
using System;
using Cielonos.MainGame.Buffs.Character;
using Cielonos.MainGame.Characters;
2026-06-02 12:55:39 -04:00
using Lean.Pool;
2026-03-20 12:07:44 -04:00
using Sirenix.OdinInspector;
using SLSUtilities.General;
using SLSUtilities.UI;
using UnityEngine;
namespace Cielonos.MainGame.UI
{
public class EnemyInfoUnitBase : UIElementBase
{
[HideInInspector]
public CharacterBase enemy;
[TitleGroup("Bars")]
public RectTransform barContainer;
[TitleGroup("Bars")]
public AttributeBarBase healthBar;
[TitleGroup("Bars")]
public AttributeBarBase energyBar;
2026-06-27 12:52:03 -04:00
[TitleGroup("Bars")]
public AttributeBarBase stanceBar;
2026-03-20 12:07:44 -04:00
[TitleGroup("Buffs")]
public RectTransform buffIconContainer;
[TitleGroup("Distance & Scale Settings")]
public float minShowDistance = 5f; // 距离小于此值血条消失过近4
[TitleGroup("Distance & Scale Settings")]
public float maxShowDistance = 30f; // 距离大于此值,血条消失(过远)
[TitleGroup("Distance & Scale Settings")]
public float maxScale = 1f; // 距离最近 (minShowDistance) 时的最大缩放比例
[TitleGroup("Distance & Scale Settings")]
public float minScale = 0.4f; // 距离最远 (maxShowDistance) 时的最小缩放比例
2026-06-27 12:52:03 -04:00
private float distanceFade = 1f;
2026-03-20 12:07:44 -04:00
[Header("Occlusion Settings")]
public LayerMask obstacleLayer; // 遮挡物层级(墙壁、地面等)
public float occlusionTimer = 0f; // 被遮挡的计时器
public float occlusionDelay = 0.5f; // 被遮挡后延迟多久开始消失
2026-06-27 12:52:03 -04:00
private float occlusionFade = 1f;
2026-03-20 12:07:44 -04:00
[Header("Alpha & Edge Fade Settings")]
public float fadeSpeed = 8f; // 透明度渐变速度 (越大消失越快)
[Tooltip("从中心到边缘,开始衰减的百分比 (0~1)")]
public float fadeStartPct = 0.80f; // 80% 开始消失
[Tooltip("从中心到边缘,完全透明的百分比 (0~1)")]
public float fadeEndPct = 0.95f; // 95% 完全消失
2026-06-27 12:52:03 -04:00
private float screenFade = 1f;
[TitleGroup("Colors")]
public Color normalStanceColor = new Color(1f, 0.78f, 0.58f);
[TitleGroup("Colors")]
public Color paralyzedStanceColor = new Color(1f, 0.3f, 0.3f);
2026-03-20 12:07:44 -04:00
public void Initialize(CharacterBase character)
{
enemy = character;
2026-06-27 12:52:03 -04:00
distanceFade = 1f;
occlusionFade = 1f;
screenFade = 1f;
2026-03-20 12:07:44 -04:00
UpdateHealth(true);
UpdateEnergy(true);
2026-06-27 12:52:03 -04:00
UpdateStance(true);
2026-03-20 12:07:44 -04:00
}
private void Update()
{
UpdatePosition();
2026-06-02 12:55:39 -04:00
UpdateHealth(false);
UpdateEnergy(false);
2026-06-27 12:52:03 -04:00
UpdateStance(false);
2026-03-20 12:07:44 -04:00
canvasGroup.alpha = Mathf.Min(distanceFade, occlusionFade, screenFade);
}
public void UpdatePosition()
{
2026-06-02 12:55:39 -04:00
if(enemy == null || enemy.statusSm.isDead) LeanPool.Despawn(gameObject);
2026-03-20 12:07:44 -04:00
if(enemy?.bodyPartsSc?.infoUIPoint == null) return;
2026-06-05 04:21:00 -04:00
Vector3 enemyPos = enemy.CenterPosition;
2026-03-20 12:07:44 -04:00
Vector3 infoUIPos = enemy.bodyPartsSc.infoUIPoint.position;
Camera playerCamera = MainGameManager.Player.viewSc.playerCamera;
float cameraDistance = Vector3.Distance(playerCamera.transform.position, infoUIPos);
float midDistance = (minShowDistance + maxShowDistance) / 2f;
// 计算距离衰减在midDistance处达到最大值minShowDistance和maxShowDistance处为0
distanceFade = Mathf.InverseLerp(cameraDistance < midDistance ? minShowDistance : maxShowDistance, midDistance, cameraDistance);
distanceFade = Mathf.Min(distanceFade * 4f, 1f); // 在距离范围内,快速达到完全显示
Vector3 viewportPos = playerCamera.WorldToViewportPoint(infoUIPos);
2026-06-27 12:52:03 -04:00
bool isBehindCamera = viewportPos.z < 0;
if (isBehindCamera)
2026-03-20 12:07:44 -04:00
{
screenFade = 0f;
}
2026-06-27 12:52:03 -04:00
else
{
float normalizedDistX = Mathf.Abs(viewportPos.x - 0.5f) * 2f;
float normalizedDistY = Mathf.Abs(viewportPos.y - 0.5f) * 2f;
float maxNormalizedDist = Mathf.Max(normalizedDistX, normalizedDistY);
screenFade = Mathf.InverseLerp(fadeEndPct, fadeStartPct, maxNormalizedDist);
}
// Occlusion check
2026-03-20 12:07:44 -04:00
if (Physics.Linecast(playerCamera.transform.position, infoUIPos, out RaycastHit hitInfo))
{
if (hitInfo.collider.GetComponentInParent<CharacterBase>() != enemy)
{
occlusionTimer += Time.deltaTime;
}
else
{
occlusionTimer = 0f;
}
}
2026-06-27 12:52:03 -04:00
else
{
// Nothing between camera and enemy — clear line of sight
occlusionTimer = 0f;
}
2026-03-20 12:07:44 -04:00
if (occlusionTimer > occlusionDelay)
{
float progress = (occlusionTimer - occlusionDelay) * fadeSpeed;
occlusionFade = Mathf.Lerp(1f, 0f, progress);
}
else
{
occlusionFade = 1f;
}
2026-06-27 12:52:03 -04:00
// Always update position and scale, even when fading out,
// so the UI is in the correct place when it becomes visible again.
2026-03-20 12:07:44 -04:00
float t = Mathf.InverseLerp(minShowDistance, maxShowDistance, cameraDistance);
float currentScale = Mathf.Lerp(maxScale, minScale, t);
2026-05-10 11:47:55 -04:00
RectTransform areaRect = PlayerCanvas.EnemyInfoUIArea.rectTransform;
2026-03-20 12:07:44 -04:00
Vector2 enemyScreenPos = SpaceConverter.WorldPointToUILocalPoint(areaRect, infoUIPos, playerCamera, null);
rectTransform.localScale = Vector3.one * currentScale;
rectTransform.anchoredPosition = enemyScreenPos;
}
public void UpdateHealth(bool isInstant = false)
{
2026-06-02 12:55:39 -04:00
float currentHealth = enemy.attributeSm[CharacterAttribute.Health];
float maximumHealth = enemy.attributeSm[CharacterAttribute.MaximumHealth];
2026-03-20 12:07:44 -04:00
//float ratio = currentHealth / maximumHealth;
healthBar.UpdateFillImage(currentHealth, maximumHealth);
2026-06-02 12:55:39 -04:00
//if(!isInstant) healthBar.Blink(Color.white);
2026-03-20 12:07:44 -04:00
}
public void UpdateEnergy(bool isInstant = false)
{
2026-06-02 12:55:39 -04:00
float currentEnergy = enemy.attributeSm[CharacterAttribute.Energy];
float maximumEnergy = enemy.attributeSm[CharacterAttribute.MaximumEnergy];
2026-03-20 12:07:44 -04:00
energyBar.UpdateFillImage(currentEnergy, maximumEnergy);
2026-06-02 12:55:39 -04:00
//if(!isInstant) energyBar.Blink(Color.white);
2026-03-20 12:07:44 -04:00
}
2026-06-27 12:52:03 -04:00
public void UpdateStance(bool isInstant = false)
{
if (stanceBar == null) return;
if (!enemy.attributeSm.Has(CharacterAttribute.MaximumStance) || enemy.attributeSm[CharacterAttribute.MaximumStance] <= 0)
{
if (stanceBar.gameObject.activeSelf) stanceBar.gameObject.SetActive(false);
return;
}
if (!stanceBar.gameObject.activeSelf) stanceBar.gameObject.SetActive(true);
if (enemy.buffSm.TryGetBuff(out ElectronicParalysis buff) && buff.independentStackSubmodule.totalStackAmount > 0)
{
float remainingTime = buff.independentStackSubmodule.LongestUnit.remainingTime;
float maximumTime = buff.independentStackSubmodule.LongestUnit.duration;
stanceBar.UpdateFillColor(paralyzedStanceColor, true);
float elapsed = maximumTime - remainingTime;
stanceBar.UpdateFillImage(elapsed, maximumTime);
}
else
{
float currentStance = enemy.attributeSm[CharacterAttribute.Stance];
float maximumStance = enemy.attributeSm[CharacterAttribute.MaximumStance];
stanceBar.UpdateFillColor(normalStanceColor, true);
stanceBar.UpdateFillImage(currentStance, maximumStance);
}
}
2026-03-20 12:07:44 -04:00
}
}