Files

118 lines
5.4 KiB
C#
Raw Permalink 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 비용 없음).
//
// 폴링 방식:
// 이벤트 구독 대신 매 프레임 Update에서 Health.Ratio를 직접 읽어 반영한다.
// - 구독/해제 생명주기가 없어 OnEnable 타이밍 버그가 원천적으로 없음
// - Update는 항상 모든 Awake 뒤에 실행되므로 Health는 늘 초기화된 상태
// - 비용: 매 프레임 float 비교 1회 + "비율이 바뀐 프레임"에만 시각 갱신
//
// 구성:
// - 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일 때 HP바 숨김 (렌더러만 끔)
[SerializeField] private float _smoothSpeed = 0f; // 0이면 즉시 반영, 0보다 크면 보간 (ratio/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 색상 변경용
private SpriteRenderer[] _renderers; // 숨김 처리용 — Background + Fill 전부
private float _currentRatio = 1f; // 현재 표시 중인 HP 비율 (보간 시 점진적으로 수렴)
private float _appliedRatio = -1f; // 마지막으로 시각에 반영한 비율 (중복 갱신 방지, -1이면 첫 프레임 강제 갱신)
private void Awake()
{
if (_health == null && _autoFindHealthInParent)
_health = GetComponentInParent<Health>();
if (_fill != null)
{
_baseFillScale = _fill.localScale;
_fillRenderer = _fill.GetComponent<SpriteRenderer>();
}
// 숨김 토글 대상: 이 HP바 계층의 모든 SpriteRenderer (Background + Fill).
_renderers = GetComponentsInChildren<SpriteRenderer>(true);
}
// 매 프레임 Health 상태를 폴링해서 반영.
// 비율이 실제로 바뀐 프레임에만 transform/color를 건드린다 (정지 시 비용 거의 0).
private void Update()
{
if (_health == null) return;
float target = _health.Ratio;
if (_smoothSpeed > 0f)
_currentRatio = Mathf.MoveTowards(_currentRatio, target, _smoothSpeed * Time.deltaTime);
else
_currentRatio = target;
if (Mathf.Approximately(_currentRatio, _appliedRatio)) return;
_appliedRatio = _currentRatio;
ApplyScale();
ApplyColor(_currentRatio);
ApplyVisibility();
}
// 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;
if (ratio <= _lowThreshold)
_fillRenderer.color = _lowColor;
else if (ratio <= _midThreshold)
_fillRenderer.color = _midColor;
else
_fillRenderer.color = _highColor;
}
// HP가 풀(1)이거나 사망(0)이면 HP바를 숨김.
// GameObject가 아니라 SpriteRenderer의 enabled만 끈다 — GameObject를 끄면
// Update가 멈춰 폴링이 중단되고 다시 켤 수 없게 되기 때문.
private void ApplyVisibility()
{
if (!_hideWhenFull || _renderers == null) return;
bool visible = _currentRatio > 0f && _currentRatio < 1f;
for (int i = 0; i < _renderers.Length; i++)
{
if (_renderers[i] != null)
_renderers[i].enabled = visible;
}
}
}