Files
Northbound/Assets/Scripts/UnitHealthBar.cs
dal4segno 17b3cf6746 체력바 추가
플레이어는 상시 표시, 나머지는 체력 변경 시 표시
2026-02-25 15:15:29 +09:00

202 lines
6.7 KiB
C#

using UnityEngine;
using UnityEngine.UI;
using TMPro;
namespace Northbound
{
/// <summary>
/// 유닛(플레이어, 적, 건물 등) 위에 표시되는 체력바
/// IHealthProvider 인터페이스를 구현하는 모든 유닛에서 사용 가능
/// </summary>
public class UnitHealthBar : MonoBehaviour
{
[Header("UI References")]
[Tooltip("체력바 채우기 이미지 (Filled 타입 권장)")]
public Image fillImage;
[Tooltip("체력 텍스트 (선택사항)")]
public TextMeshProUGUI healthText;
[Tooltip("체력바 전체 컨테이너")]
public GameObject barContainer;
[Header("Settings")]
[Tooltip("유닛 위쪽으로의 높이 오프셋")]
public float heightOffset = 2f;
[Tooltip("체력이 가득 찼을 때 체력바 숨김 여부")]
public bool hideWhenFull = true;
[Tooltip("체력이 가득 찬 후 숨기까지의 지연 시간")]
public float hideDelay = 3f;
[Tooltip("초기 표시 지속 시간")]
public float initialShowDuration = 2f;
[Header("Colors")]
public Color fullHealthColor = Color.green;
public Color mediumHealthColor = Color.yellow;
public Color lowHealthColor = Color.red;
[Range(0f, 1f)] public float mediumHealthThreshold = 0.6f;
[Range(0f, 1f)] public float lowHealthThreshold = 0.3f;
private IHealthProvider _healthProvider;
private Camera _mainCamera;
private float _lastShowTime;
private Canvas _canvas;
private bool _initialized = false;
private Transform _targetTransform; // 따라갈 타겟
private void Awake()
{
// Canvas 설정
_canvas = GetComponent<Canvas>();
if (_canvas == null)
{
_canvas = gameObject.AddComponent<Canvas>();
}
_canvas.renderMode = RenderMode.WorldSpace;
// Canvas Scaler 설정
var scaler = GetComponent<CanvasScaler>();
if (scaler == null)
{
scaler = gameObject.AddComponent<CanvasScaler>();
}
scaler.dynamicPixelsPerUnit = 10f;
}
private void Start()
{
_mainCamera = Camera.main;
}
/// <summary>
/// 체력바 초기화
/// </summary>
/// <param name="healthProvider">체력 정보를 제공하는 유닛</param>
public void Initialize(IHealthProvider healthProvider)
{
_healthProvider = healthProvider;
_targetTransform = (healthProvider as Component)?.transform;
// 부모와의 관계를 끊고 독립적으로 이동
transform.SetParent(null);
// 카메라 방향으로 한 번만 회전 설정
if (_mainCamera == null)
{
_mainCamera = Camera.main;
}
if (_mainCamera != null)
{
// 카메라의 회전을 가져와서 체력바가 카메라를 향하도록 설정
// Y축 회전만 사용하고, X축 90도로 Canvas가 수평이 되도록 함
float cameraYRotation = _mainCamera.transform.eulerAngles.y;
transform.rotation = Quaternion.Euler(90, cameraYRotation, 0);
}
// 초기 체력바 표시
ShowBar(initialShowDuration);
_initialized = true;
// 초기 체력 업데이트
UpdateHealth();
}
/// <summary>
/// 체력바를 지정된 시간 동안 표시
/// </summary>
public void ShowBar(float duration = 0f)
{
if (barContainer != null)
{
barContainer.SetActive(true);
_lastShowTime = Time.time;
// duration이 0보다 크면 그 시간 동안만 표시
if (duration > 0)
{
_lastShowTime = Time.time + duration - hideDelay;
}
}
}
/// <summary>
/// 체력바 즉시 숨김
/// </summary>
public void HideBar()
{
if (barContainer != null)
{
barContainer.SetActive(false);
}
}
/// <summary>
/// 체력 정보 업데이트 (IHealthProvider 사용)
/// </summary>
public void UpdateHealth()
{
if (_healthProvider == null) return;
int currentHealth = _healthProvider.GetCurrentHealth();
int maxHealth = _healthProvider.GetMaxHealth();
float healthPercentage = _healthProvider.GetHealthPercentage();
UpdateHealthDisplay(currentHealth, maxHealth, healthPercentage);
}
/// <summary>
/// 체력 정보 업데이트 (직접 값 전달)
/// </summary>
public void UpdateHealth(int currentHealth, int maxHealth)
{
float healthPercentage = maxHealth > 0 ? (float)currentHealth / maxHealth : 0f;
UpdateHealthDisplay(currentHealth, maxHealth, healthPercentage);
}
private void UpdateHealthDisplay(int currentHealth, int maxHealth, float healthPercentage)
{
if (fillImage != null)
{
fillImage.fillAmount = healthPercentage;
fillImage.color = GetHealthColor(healthPercentage);
}
if (healthText != null)
{
healthText.text = $"{currentHealth}/{maxHealth}";
}
// 체력 변경 시 체력바 표시
ShowBar();
}
private void LateUpdate()
{
// 타겟을 따라 이동 (회전은 하지 않음)
if (_targetTransform != null)
{
transform.position = _targetTransform.position + Vector3.up * heightOffset;
}
// 체력이 가득 차면 숨김
if (hideWhenFull && barContainer != null && _healthProvider != null && _initialized)
{
float healthPercentage = _healthProvider.GetHealthPercentage();
if (healthPercentage >= 1f && Time.time - _lastShowTime > hideDelay)
{
barContainer.SetActive(false);
}
}
}
private Color GetHealthColor(float healthPercentage)
{
if (healthPercentage <= lowHealthThreshold)
return lowHealthColor;
else if (healthPercentage <= mediumHealthThreshold)
return mediumHealthColor;
else
return fullHealthColor;
}
}
}