143 lines
6.2 KiB
C#
143 lines
6.2 KiB
C#
|
|
using System;
|
|||
|
|
using Cielonos.MainGame.Buffs.Character;
|
|||
|
|
using Cielonos.MainGame.Characters;
|
|||
|
|
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("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;
|
|||
|
|
|
|||
|
|
[Header("Occlusion Settings")]
|
|||
|
|
public LayerMask obstacleLayer; // 遮挡物层级(墙壁、地面等)
|
|||
|
|
public float occlusionTimer = 0f; // 被遮挡的计时器
|
|||
|
|
public float occlusionDelay = 0.5f; // 被遮挡后延迟多久开始消失
|
|||
|
|
private float occlusionFade;
|
|||
|
|
|
|||
|
|
[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;
|
|||
|
|
|
|||
|
|
public void Initialize(CharacterBase character)
|
|||
|
|
{
|
|||
|
|
enemy = character;
|
|||
|
|
UpdateHealth(true);
|
|||
|
|
UpdateEnergy(true);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void Update()
|
|||
|
|
{
|
|||
|
|
UpdatePosition();
|
|||
|
|
canvasGroup.alpha = Mathf.Min(distanceFade, occlusionFade, screenFade);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void UpdatePosition()
|
|||
|
|
{
|
|||
|
|
if(enemy?.bodyPartsSc?.infoUIPoint == null) return;
|
|||
|
|
|
|||
|
|
Vector3 enemyPos = enemy.centerPosition;
|
|||
|
|
Vector3 infoUIPos = enemy.bodyPartsSc.infoUIPoint.position;
|
|||
|
|
Vector3 playerPos = MainGameManager.Player.centerPosition;
|
|||
|
|
Camera playerCamera = MainGameManager.Player.viewSc.playerCamera;
|
|||
|
|
|
|||
|
|
float cameraDistance = Vector3.Distance(playerCamera.transform.position, infoUIPos);
|
|||
|
|
float playerDistance = Vector3.Distance(playerPos, enemyPos);
|
|||
|
|
float midDistance = (minShowDistance + maxShowDistance) / 2f;
|
|||
|
|
// 计算距离衰减,在midDistance处达到最大值,minShowDistance和maxShowDistance处为0
|
|||
|
|
distanceFade = Mathf.InverseLerp(cameraDistance < midDistance ? minShowDistance : maxShowDistance, midDistance, cameraDistance);
|
|||
|
|
distanceFade = Mathf.Min(distanceFade * 4f, 1f); // 在距离范围内,快速达到完全显示
|
|||
|
|
if(distanceFade <= 0f) return; // 距离过近或过远,直接隐藏
|
|||
|
|
|
|||
|
|
Vector3 screenPos = SpaceConverter.WorldPointToScreenPoint(infoUIPos, playerCamera);
|
|||
|
|
Vector3 viewportPos = playerCamera.WorldToViewportPoint(infoUIPos);
|
|||
|
|
bool isBehindCamera = screenPos.z < 0;
|
|||
|
|
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);
|
|||
|
|
/*bool isOffScreen = screenPos.x < 0 || screenPos.x > Screen.width ||
|
|||
|
|
screenPos.y < 0 || screenPos.y > Screen.height;*/
|
|||
|
|
if (isBehindCamera || screenFade <= 0f)
|
|||
|
|
{
|
|||
|
|
screenFade = 0f;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (Physics.Linecast(playerCamera.transform.position, infoUIPos, out RaycastHit hitInfo))
|
|||
|
|
{
|
|||
|
|
if (hitInfo.collider.GetComponentInParent<CharacterBase>() != enemy)
|
|||
|
|
{
|
|||
|
|
occlusionTimer += Time.deltaTime;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
occlusionTimer = 0f;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (occlusionTimer > occlusionDelay)
|
|||
|
|
{
|
|||
|
|
float progress = (occlusionTimer - occlusionDelay) * fadeSpeed;
|
|||
|
|
occlusionFade = Mathf.Lerp(1f, 0f, progress);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
occlusionFade = 1f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
float t = Mathf.InverseLerp(minShowDistance, maxShowDistance, cameraDistance);
|
|||
|
|
float currentScale = Mathf.Lerp(maxScale, minScale, t);
|
|||
|
|
RectTransform areaRect = PlayerCanvas.Instance.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["Health"];
|
|||
|
|
float maximumHealth = enemy.attributeSm["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["Energy"];
|
|||
|
|
float maximumEnergy = enemy.attributeSm["MaximumEnergy"];
|
|||
|
|
energyBar.UpdateFillImage(currentEnergy, maximumEnergy);
|
|||
|
|
if(!isInstant) energyBar.Blink(Color.white);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|