202 lines
6.7 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|