Files
Cielonos/Assets/Scripts/MainGame/UI/PlayerUI/EnemyInfo/EnemyInfoUnitBase.cs
SoulliesOfficial 9a9e48f8a5
2026-06-27 12:52:03 -04:00

190 lines
8.1 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;
using Cielonos.MainGame.Buffs.Character;
using Cielonos.MainGame.Characters;
using Lean.Pool;
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;
[TitleGroup("Bars")]
public AttributeBarBase stanceBar;
[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) 时的最小缩放比例
private float distanceFade = 1f;
[Header("Occlusion Settings")]
public LayerMask obstacleLayer; // 遮挡物层级(墙壁、地面等)
public float occlusionTimer = 0f; // 被遮挡的计时器
public float occlusionDelay = 0.5f; // 被遮挡后延迟多久开始消失
private float occlusionFade = 1f;
[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% 完全消失
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);
public void Initialize(CharacterBase character)
{
enemy = character;
distanceFade = 1f;
occlusionFade = 1f;
screenFade = 1f;
UpdateHealth(true);
UpdateEnergy(true);
UpdateStance(true);
}
private void Update()
{
UpdatePosition();
UpdateHealth(false);
UpdateEnergy(false);
UpdateStance(false);
canvasGroup.alpha = Mathf.Min(distanceFade, occlusionFade, screenFade);
}
public void UpdatePosition()
{
if(enemy == null || enemy.statusSm.isDead) LeanPool.Despawn(gameObject);
if(enemy?.bodyPartsSc?.infoUIPoint == null) return;
Vector3 enemyPos = enemy.CenterPosition;
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);
bool isBehindCamera = viewportPos.z < 0;
if (isBehindCamera)
{
screenFade = 0f;
}
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
if (Physics.Linecast(playerCamera.transform.position, infoUIPos, out RaycastHit hitInfo))
{
if (hitInfo.collider.GetComponentInParent<CharacterBase>() != enemy)
{
occlusionTimer += Time.deltaTime;
}
else
{
occlusionTimer = 0f;
}
}
else
{
// Nothing between camera and enemy — clear line of sight
occlusionTimer = 0f;
}
if (occlusionTimer > occlusionDelay)
{
float progress = (occlusionTimer - occlusionDelay) * fadeSpeed;
occlusionFade = Mathf.Lerp(1f, 0f, progress);
}
else
{
occlusionFade = 1f;
}
// Always update position and scale, even when fading out,
// so the UI is in the correct place when it becomes visible again.
float t = Mathf.InverseLerp(minShowDistance, maxShowDistance, cameraDistance);
float currentScale = Mathf.Lerp(maxScale, minScale, t);
RectTransform areaRect = PlayerCanvas.EnemyInfoUIArea.rectTransform;
Vector2 enemyScreenPos = SpaceConverter.WorldPointToUILocalPoint(areaRect, infoUIPos, playerCamera, null);
rectTransform.localScale = Vector3.one * currentScale;
rectTransform.anchoredPosition = enemyScreenPos;
}
public void UpdateHealth(bool isInstant = false)
{
float currentHealth = enemy.attributeSm[CharacterAttribute.Health];
float maximumHealth = enemy.attributeSm[CharacterAttribute.MaximumHealth];
//float ratio = currentHealth / maximumHealth;
healthBar.UpdateFillImage(currentHealth, maximumHealth);
//if(!isInstant) healthBar.Blink(Color.white);
}
public void UpdateEnergy(bool isInstant = false)
{
float currentEnergy = enemy.attributeSm[CharacterAttribute.Energy];
float maximumEnergy = enemy.attributeSm[CharacterAttribute.MaximumEnergy];
energyBar.UpdateFillImage(currentEnergy, maximumEnergy);
//if(!isInstant) energyBar.Blink(Color.white);
}
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);
}
}
}
}