Files
WhiteMan_Unity2D/Assets/02_Scripts/UI/HpBar.cs
2026-05-19 10:51:56 +09:00

126 lines
5.2 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 UnityEngine;
// ============================================================================
// HpBar
// ----------------------------------------------------------------------------
// SpriteRenderer 기반 HP바. Canvas보다 가벼움 (Canvas Rebuild 비용 없음).
// 부모(또는 Inspector 할당)의 Health 컴포넌트 이벤트 OnHealthChanged 구독.
//
// 동작:
// - Background와 Fill 두 SpriteRenderer로 구성
// - Fill의 스프라이트 피벗은 LEFT (0, 0.5) 여야 X 스케일 변경 시 왼쪽부터 줄어듦
// - HP 비율에 따라 _fill.localScale.x 조정
// - 임계값 기반으로 색상도 자동 변경 (높음/중간/낮음)
// ============================================================================
public class HpBar : MonoBehaviour
{
[SerializeField] private Health _health; // 비워두면 _autoFindHealthInParent로 자동 검색
[SerializeField] private Transform _fill; // 채움 sprite transform (스케일 조정 대상)
[SerializeField] private bool _autoFindHealthInParent = true; // _health가 null이면 부모에서 Health 자동 검색
[SerializeField] private bool _hideWhenFull = true; // HP 풀이거나 0일 때 HpBar 자동 숨김
[SerializeField] private float _smoothSpeed = 0f; // 0이면 즉시 반영, 0보다 크면 보간 (units/sec)
// ─── 임계값별 색상 ──────────────────────────────────────────────────
// ratio > _midThreshold → _highColor (정상)
// _lowThreshold < ratio <= _midThreshold → _midColor (주의)
// ratio <= _lowThreshold → _lowColor (위험)
[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 Vector3 _baseFillScale; // 풀체력 시 Fill 스케일 (Awake에 캐싱, 이후 ratio 곱해서 사용)
private SpriteRenderer _fillRenderer; // Fill의 색상 변경용 SpriteRenderer
private float _currentRatio = 1f; // 현재 표시된 HP 비율 (보간 진행 시 _targetRatio로 수렴)
private float _targetRatio = 1f; // 도달해야 할 HP 비율
private void Awake()
{
if (_health == null && _autoFindHealthInParent)
_health = GetComponentInParent<Health>();
if (_fill != null)
{
_baseFillScale = _fill.localScale;
_fillRenderer = _fill.GetComponent<SpriteRenderer>();
}
}
// 활성/비활성 토글 시 자동 구독·해제 (메모리 누수 방지).
// 활성 시 즉시 한 번 갱신해서 현재 HP 상태 반영.
private void OnEnable()
{
if (_health == null) return;
_health.OnHealthChanged += HandleHealthChanged;
HandleHealthChanged(_health.CurrentHealth, _health.MaxHealth);
}
private void OnDisable()
{
if (_health != null)
_health.OnHealthChanged -= HandleHealthChanged;
}
// 보간 모드일 때만 매 프레임 스케일 갱신.
private void Update()
{
if (_smoothSpeed <= 0f) return;
if (Mathf.Approximately(_currentRatio, _targetRatio)) return;
_currentRatio = Mathf.MoveTowards(_currentRatio, _targetRatio, _smoothSpeed * Time.deltaTime);
ApplyScale();
}
// Health.OnHealthChanged 이벤트 콜백. ratio 계산 → 스케일/색상 갱신 → 숨김 처리.
private void HandleHealthChanged(int current, int max)
{
_targetRatio = max > 0 ? (float)current / max : 0f;
// 즉시 모드면 바로 스케일 반영. 보간 모드면 Update에서 점진적으로.
if (_smoothSpeed <= 0f)
{
_currentRatio = _targetRatio;
ApplyScale();
}
ApplyColor(_targetRatio);
// 풀체력(1)이거나 사망(0)이면 HpBar 자체를 숨김 (UI 정리 + GameObject 부하 감소).
if (_hideWhenFull && _fill != null)
{
bool shouldShow = _targetRatio < 1f && _targetRatio > 0f;
if (gameObject.activeSelf != shouldShow)
gameObject.SetActive(shouldShow);
}
}
// Fill의 X 스케일 = baseScale.x × ratio. 피벗이 LEFT여야 왼쪽부터 채워짐.
private void ApplyScale()
{
if (_fill == null) return;
Vector3 scale = _baseFillScale;
scale.x = _baseFillScale.x * Mathf.Clamp01(_currentRatio);
_fill.localScale = scale;
}
// 비율 구간 매핑으로 색상 결정. 임계값 교차 시 즉시 바뀜 (보간 안 함).
private void ApplyColor(float ratio)
{
if (_fillRenderer == null) return;
Color color;
if (ratio <= _lowThreshold)
color = _lowColor;
else if (ratio <= _midThreshold)
color = _midColor;
else
color = _highColor;
_fillRenderer.color = color;
}
}