using UnityEngine; using IndianOceanAssets.Engine2_5D; namespace IndianOceanAssets.Engine2_5D { /// /// 敌人头顶血条 —— 挂在敌人 Prefab 上。 /// 血条可视化物体(BG、Fill)已在 Prefab 中预建,运行时直接引用,无需动态创建。 /// /// 解耦改造:直接引用同物体上的 HealthSystem(RequireComponent 保证存在), /// 订阅其 OnHealthChanged 事件刷新,彻底移除对私有字段的反射读取。 /// 注意:不使用"全局共享 IntVariable"承载敌人血量——否则所有敌人血条会显示同一份血量, /// 正确的解法是"每个敌人读自己的 HealthSystem"(同物体组件引用,非 Find / 反射 / 跨对象)。 /// [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(); if (_healthSource != null) { _healthSource.OnHealthChanged += UpdateBar; UpdateBar(_healthSource.CurrentHealth, _healthSource.MaxHealth); } else { UpdateBar(1, 1); } } private void OnDestroy() { if (_healthSource != null) _healthSource.OnHealthChanged -= UpdateBar; } /// /// 根据当前血量更新血条显示(事件驱动,无反射、无每帧 LateUpdate)。 /// 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; } } }