using UnityEngine; using UnityEngine.UI; // Screen-space HP bar for the player HUD. // Enemy HP bars can keep using the SpriteRenderer-based HpBar. public class PlayerHpBarUI : MonoBehaviour { [SerializeField] private Health _health; [SerializeField] private Image _fillImage; [SerializeField] private bool _autoFindPlayerHealth = true; [SerializeField] private bool _autoFindFillImageInChildren = true; [SerializeField] private bool _configureAsFilledImage = true; [SerializeField] private float _smoothSpeed = 0f; [Header("Color Thresholds")] [SerializeField] private Color _highColor = new Color(0.2f, 0.9f, 0.3f, 1f); [SerializeField] private Color _midColor = new Color(1f, 0.85f, 0.2f, 1f); [SerializeField] private Color _lowColor = new Color(0.95f, 0.25f, 0.25f, 1f); [SerializeField, Range(0f, 1f)] private float _midThreshold = 0.5f; [SerializeField, Range(0f, 1f)] private float _lowThreshold = 0.2f; private float _currentRatio = 1f; private float _targetRatio = 1f; private void Awake() { ResolveFillImage(); ResolveHealth(); ConfigureFillImage(); } private void OnEnable() { ResolveFillImage(); ResolveHealth(); ConfigureFillImage(); if (_health == null) return; _health.OnHealthChanged += HandleHealthChanged; HandleHealthChanged(_health.CurrentHealth, _health.MaxHealth); } private void OnDisable() { if (_health != null) _health.OnHealthChanged -= HandleHealthChanged; } private void Start() { ResolveFillImage(); ResolveHealth(); ConfigureFillImage(); if (_health != null) HandleHealthChanged(_health.CurrentHealth, _health.MaxHealth); } private void Update() { if (_fillImage == null || _smoothSpeed <= 0f) return; if (Mathf.Approximately(_currentRatio, _targetRatio)) return; _currentRatio = Mathf.MoveTowards(_currentRatio, _targetRatio, _smoothSpeed * Time.deltaTime); ApplyFill(); } public void Bind(Health health) { if (_health == health) return; if (isActiveAndEnabled && _health != null) _health.OnHealthChanged -= HandleHealthChanged; _health = health; if (isActiveAndEnabled && _health != null) { _health.OnHealthChanged += HandleHealthChanged; HandleHealthChanged(_health.CurrentHealth, _health.MaxHealth); } } private void HandleHealthChanged(int current, int max) { _targetRatio = max > 0 ? Mathf.Clamp01((float)current / max) : 0f; if (_smoothSpeed <= 0f) { _currentRatio = _targetRatio; ApplyFill(); } ApplyColor(_targetRatio); } private void ApplyFill() { if (_fillImage == null) return; _fillImage.fillAmount = Mathf.Clamp01(_currentRatio); } private void ApplyColor(float ratio) { if (_fillImage == null) return; if (ratio <= _lowThreshold) _fillImage.color = _lowColor; else if (ratio <= _midThreshold) _fillImage.color = _midColor; else _fillImage.color = _highColor; } private void ResolveHealth() { if (_health != null || !_autoFindPlayerHealth) return; PlayerController player = FindFirstObjectByType(); if (player != null) _health = player.GetComponent(); } private void ResolveFillImage() { if (_fillImage != null || !_autoFindFillImageInChildren) return; Transform fill = FindChildByName(transform, "Fill"); if (fill != null) _fillImage = fill.GetComponent(); } private Transform FindChildByName(Transform parent, string childName) { if (parent == null) return null; for (int i = 0; i < parent.childCount; i++) { Transform child = parent.GetChild(i); if (child.name == childName) return child; Transform nested = FindChildByName(child, childName); if (nested != null) return nested; } return null; } private void ConfigureFillImage() { if (_fillImage == null || !_configureAsFilledImage) return; _fillImage.type = Image.Type.Filled; _fillImage.fillMethod = Image.FillMethod.Horizontal; _fillImage.fillOrigin = (int)Image.OriginHorizontal.Left; } }