95 lines
3.6 KiB
C#
95 lines
3.6 KiB
C#
using UnityEngine;
|
||
using IndianOceanAssets.Engine2_5D;
|
||
|
||
namespace IndianOceanAssets.Engine2_5D
|
||
{
|
||
/// <summary>
|
||
/// 敌人头顶血条 —— 挂在敌人 Prefab 上。
|
||
/// 血条可视化物体(BG、Fill)已在 Prefab 中预建,运行时直接引用,无需动态创建。
|
||
///
|
||
/// 解耦改造:直接引用同物体上的 HealthSystem(RequireComponent 保证存在),
|
||
/// 订阅其 OnHealthChanged 事件刷新,彻底移除对私有字段的反射读取。
|
||
/// 注意:不使用"全局共享 IntVariable"承载敌人血量——否则所有敌人血条会显示同一份血量,
|
||
/// 正确的解法是"每个敌人读自己的 HealthSystem"(同物体组件引用,非 Find / 反射 / 跨对象)。
|
||
/// </summary>
|
||
[RequireComponent(typeof(HealthSystem))]
|
||
public class EnemyHealthBar : MonoBehaviour
|
||
{
|
||
[Header("血条引用(Prefab 中预建)")]
|
||
[SerializeField] private Transform barRoot;
|
||
[SerializeField] private SpriteRenderer bgRenderer;
|
||
[SerializeField] private SpriteRenderer fillRenderer;
|
||
|
||
[Header("血条参数")]
|
||
[Tooltip("血条宽度(世界单位)")]
|
||
[SerializeField] private float barWidth = 1f;
|
||
|
||
[Tooltip("血条高度(世界单位)")]
|
||
[SerializeField] private float barHeight = 0.1f;
|
||
|
||
[Header("颜色")]
|
||
[SerializeField] private Color fillColor = new Color(0.8f, 0.1f, 0.1f, 0.9f);
|
||
[SerializeField] private Color lowHealthColor = new Color(1f, 0.3f, 0f, 0.9f);
|
||
|
||
[Tooltip("低于此比例时切换为低血量颜色")]
|
||
[SerializeField] private float lowHealthThreshold = 0.3f;
|
||
|
||
[Header("显示控制")]
|
||
[Tooltip("满血时是否隐藏血条")]
|
||
[SerializeField] private bool hideWhenFull = false;
|
||
|
||
// 同物体上的 HealthSystem(RequireComponent 保证存在;非 Find / 反射 / 跨对象引用)
|
||
private HealthSystem _healthSource;
|
||
|
||
private void Start()
|
||
{
|
||
if (fillRenderer == null || bgRenderer == null)
|
||
{
|
||
enabled = false;
|
||
return;
|
||
}
|
||
|
||
_healthSource = GetComponent<HealthSystem>();
|
||
if (_healthSource != null)
|
||
{
|
||
_healthSource.OnHealthChanged += UpdateBar;
|
||
UpdateBar(_healthSource.CurrentHealth, _healthSource.MaxHealth);
|
||
}
|
||
else
|
||
{
|
||
UpdateBar(1, 1);
|
||
}
|
||
}
|
||
|
||
private void OnDestroy()
|
||
{
|
||
if (_healthSource != null)
|
||
_healthSource.OnHealthChanged -= UpdateBar;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据当前血量更新血条显示(事件驱动,无反射、无每帧 LateUpdate)。
|
||
/// </summary>
|
||
private void UpdateBar(int currentHealth, int maxHealth)
|
||
{
|
||
if (fillRenderer == null || bgRenderer == null) return;
|
||
|
||
if (maxHealth <= 0) maxHealth = 1;
|
||
float ratio = Mathf.Clamp01((float)currentHealth / maxHealth);
|
||
|
||
// 更新填充条缩放(居中对齐)
|
||
fillRenderer.transform.localScale = new Vector3(barWidth * ratio, barHeight, 1f);
|
||
fillRenderer.transform.localPosition = new Vector3(
|
||
-(barWidth * (1f - ratio)) * 0.5f, 0f, 0f);
|
||
|
||
// 低血量变色
|
||
fillRenderer.color = ratio <= lowHealthThreshold ? lowHealthColor : fillColor;
|
||
|
||
// 满血隐藏
|
||
bool visible = !(hideWhenFull && ratio >= 1f);
|
||
bgRenderer.enabled = visible;
|
||
fillRenderer.enabled = visible;
|
||
}
|
||
}
|
||
}
|