체력바 추가
플레이어는 상시 표시, 나머지는 체력 변경 시 표시
This commit is contained in:
201
Assets/Scripts/UnitHealthBar.cs
Normal file
201
Assets/Scripts/UnitHealthBar.cs
Normal file
@@ -0,0 +1,201 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user