체력바 추가

플레이어는 상시 표시, 나머지는 체력 변경 시 표시
This commit is contained in:
2026-02-25 15:15:29 +09:00
parent f3923079a4
commit 17b3cf6746
22 changed files with 369 additions and 147 deletions

View File

@@ -1370,14 +1370,14 @@
<HintPath>Library\ScriptAssemblies\Unity.2D.Tilemap.Editor.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Unity.InputSystem.ForUI">
<HintPath>Library\ScriptAssemblies\Unity.InputSystem.ForUI.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Unity.Profiling.Core">
<HintPath>Library\ScriptAssemblies\Unity.Profiling.Core.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Unity.InputSystem.ForUI">
<HintPath>Library\ScriptAssemblies\Unity.InputSystem.ForUI.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Unity.RenderPipelines.GPUDriven.Runtime">
<HintPath>Library\ScriptAssemblies\Unity.RenderPipelines.GPUDriven.Runtime.dll</HintPath>
<Private>False</Private>

View File

@@ -78,6 +78,7 @@
<Compile Include="Assets\Scripts\TeamManager.cs" />
<Compile Include="Assets\Scripts\TeamGate.cs" />
<Compile Include="Assets\Scripts\Worker.cs" />
<Compile Include="Assets\Scripts\IHealthProvider.cs" />
<Compile Include="Assets\Scripts\WorkerSpawner.cs" />
<Compile Include="Assets\Scripts\Resource.cs" />
<Compile Include="Assets\Data\Scripts\DataClasses\UpgradeData.cs" />
@@ -88,6 +89,7 @@
<Compile Include="Assets\Scripts\PlayerStats.cs" />
<Compile Include="Assets\FlatKit\[Render Pipeline] URP\Water\Scripts\Buoyancy.cs" />
<Compile Include="Assets\Scripts\ObstacleSpawner.cs" />
<Compile Include="Assets\Scripts\UnitHealthBar.cs" />
<Compile Include="Assets\Scripts\BuildingFoundation.cs" />
<Compile Include="Assets\Scripts\AttackAction.cs" />
<Compile Include="Assets\Scripts\IInteractable.cs" />
@@ -1416,14 +1418,14 @@
<HintPath>Library\ScriptAssemblies\Unity.2D.Tilemap.Editor.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Unity.InputSystem.ForUI">
<HintPath>Library\ScriptAssemblies\Unity.InputSystem.ForUI.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Unity.Profiling.Core">
<HintPath>Library\ScriptAssemblies\Unity.Profiling.Core.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Unity.InputSystem.ForUI">
<HintPath>Library\ScriptAssemblies\Unity.InputSystem.ForUI.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Unity.RenderPipelines.GPUDriven.Runtime">
<HintPath>Library\ScriptAssemblies\Unity.RenderPipelines.GPUDriven.Runtime.dll</HintPath>
<Private>False</Private>

View File

@@ -106,6 +106,8 @@ MonoBehaviour:
maxHealth: 100
damageEffectPrefab: {fileID: 4021103657954561961, guid: 5c755f9bc5253ea418e919994537dcc7, type: 3}
destroyEffectPrefab: {fileID: 141433446842962269, guid: 9fe8f8b3288e45a44af36ff8aa04486e, type: 3}
showHealthBar: 1
healthBarPrefab: {fileID: 100000, guid: 8e7a5b12c9f8a4a5ba3c8d1f2e5a7b9c, type: 3}
--- !u!195 &4485945348237935463
NavMeshAgent:
m_ObjectHideFlags: 0

View File

@@ -106,6 +106,8 @@ MonoBehaviour:
maxHealth: 100
damageEffectPrefab: {fileID: 4021103657954561961, guid: 5c755f9bc5253ea418e919994537dcc7, type: 3}
destroyEffectPrefab: {fileID: 141433446842962269, guid: 9fe8f8b3288e45a44af36ff8aa04486e, type: 3}
showHealthBar: 1
healthBarPrefab: {fileID: 100000, guid: 8e7a5b12c9f8a4a5ba3c8d1f2e5a7b9c, type: 3}
--- !u!195 &4485945348237935463
NavMeshAgent:
m_ObjectHideFlags: 0

View File

@@ -983,6 +983,8 @@ MonoBehaviour:
maxHealth: 20
damageEffectPrefab: {fileID: 4021103657954561961, guid: 5c755f9bc5253ea418e919994537dcc7, type: 3}
destroyEffectPrefab: {fileID: 141433446842962269, guid: 9fe8f8b3288e45a44af36ff8aa04486e, type: 3}
showHealthBar: 1
healthBarPrefab: {fileID: 100000, guid: 8e7a5b12c9f8a4a5ba3c8d1f2e5a7b9c, type: 3}
--- !u!195 &2894690479083926678
NavMeshAgent:
m_ObjectHideFlags: 0

View File

@@ -1317,6 +1317,8 @@ MonoBehaviour:
maxHealth: 75
damageEffectPrefab: {fileID: 4021103657954561961, guid: 5c755f9bc5253ea418e919994537dcc7, type: 3}
destroyEffectPrefab: {fileID: 141433446842962269, guid: 9fe8f8b3288e45a44af36ff8aa04486e, type: 3}
showHealthBar: 1
healthBarPrefab: {fileID: 100000, guid: 8e7a5b12c9f8a4a5ba3c8d1f2e5a7b9c, type: 3}
--- !u!195 &3634736894319727576
NavMeshAgent:
m_ObjectHideFlags: 0

View File

@@ -1741,6 +1741,8 @@ MonoBehaviour:
maxHealth: 125
damageEffectPrefab: {fileID: 4021103657954561961, guid: 5c755f9bc5253ea418e919994537dcc7, type: 3}
destroyEffectPrefab: {fileID: 141433446842962269, guid: 9fe8f8b3288e45a44af36ff8aa04486e, type: 3}
showHealthBar: 1
healthBarPrefab: {fileID: 100000, guid: 8e7a5b12c9f8a4a5ba3c8d1f2e5a7b9c, type: 3}
--- !u!195 &2580823509700602203
NavMeshAgent:
m_ObjectHideFlags: 0

View File

@@ -663,6 +663,8 @@ MonoBehaviour:
maxHealth: 65
damageEffectPrefab: {fileID: 4021103657954561961, guid: 5c755f9bc5253ea418e919994537dcc7, type: 3}
destroyEffectPrefab: {fileID: 141433446842962269, guid: 9fe8f8b3288e45a44af36ff8aa04486e, type: 3}
showHealthBar: 1
healthBarPrefab: {fileID: 100000, guid: 8e7a5b12c9f8a4a5ba3c8d1f2e5a7b9c, type: 3}
--- !u!195 &7475699343328683952
NavMeshAgent:
m_ObjectHideFlags: 0

View File

@@ -1096,6 +1096,8 @@ MonoBehaviour:
maxHealth: 45
damageEffectPrefab: {fileID: 4021103657954561961, guid: 5c755f9bc5253ea418e919994537dcc7, type: 3}
destroyEffectPrefab: {fileID: 141433446842962269, guid: 9fe8f8b3288e45a44af36ff8aa04486e, type: 3}
showHealthBar: 1
healthBarPrefab: {fileID: 100000, guid: 8e7a5b12c9f8a4a5ba3c8d1f2e5a7b9c, type: 3}
--- !u!195 &5366269420367463183
NavMeshAgent:
m_ObjectHideFlags: 0

View File

@@ -1341,6 +1341,8 @@ MonoBehaviour:
maxHealth: 100
damageEffectPrefab: {fileID: 4021103657954561961, guid: 5c755f9bc5253ea418e919994537dcc7, type: 3}
destroyEffectPrefab: {fileID: 141433446842962269, guid: 9fe8f8b3288e45a44af36ff8aa04486e, type: 3}
showHealthBar: 1
healthBarPrefab: {fileID: 100000, guid: 8e7a5b12c9f8a4a5ba3c8d1f2e5a7b9c, type: 3}
--- !u!195 &1255224548206942124
NavMeshAgent:
m_ObjectHideFlags: 0

View File

@@ -93,6 +93,8 @@ MonoBehaviour:
deathEffectPrefab: {fileID: 5642766282230003982, guid: b5c8ca7ed10b61e499cce8ec3b6e2e4c, type: 3}
resourcePickupPrefab: {fileID: 1627676033990080135, guid: 8c45964a69bf8fa4ba461ed217bc052f, type: 3}
respawnDelay: 10
showHealthBar: 1
healthBarPrefab: {fileID: 100000, guid: 8e7a5b12c9f8a4a5ba3c8d1f2e5a7b9c, type: 3}
--- !u!143 &3007098678582223509
CharacterController:
m_ObjectHideFlags: 0

View File

@@ -727,8 +727,8 @@ Transform:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 519420028}
serializedVersion: 2
m_LocalRotation: {x: 0.38268346, y: -0.00000022436977, z: 0.000000092937015, w: 0.92387956}
m_LocalPosition: {x: -16.14601, y: 18.99998, z: 514.7231}
m_LocalRotation: {x: 0.60876137, y: 0, z: 0, w: 0.7933534}
m_LocalPosition: {x: -16.14601, y: 18.99998, z: 529.7231}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
@@ -2100,13 +2100,13 @@ Transform:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1290143989}
serializedVersion: 2
m_LocalRotation: {x: 0.38268343, y: -0.00000022436976, z: 0.00000009293699, w: 0.92387956}
m_LocalPosition: {x: -16.14601, y: 18.99998, z: 514.7231}
m_LocalRotation: {x: 0.60876137, y: 0, z: 0, w: 0.7933534}
m_LocalPosition: {x: -16.14601, y: 18.99998, z: 529.7231}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 61373299}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_LocalEulerAnglesHint: {x: 75, y: 0, z: 0}
--- !u!114 &1290143992
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -2125,7 +2125,7 @@ MonoBehaviour:
AngularDampingMode: 0
RotationDamping: {x: 1, y: 1, z: 1}
QuaternionDamping: 1
FollowOffset: {x: 0, y: 18, z: -18}
FollowOffset: {x: 0, y: 18, z: -3}
--- !u!114 &1290143993
MonoBehaviour:
m_ObjectHideFlags: 0

View File

@@ -5,7 +5,7 @@ using Northbound.Data;
namespace Northbound
{
public class Building : NetworkBehaviour, IDamageable, IVisionProvider, ITeamMember
public class Building : NetworkBehaviour, IDamageable, IVisionProvider, ITeamMember, IHealthProvider
{
[Header("References")]
public TowerData buildingData;
@@ -63,7 +63,7 @@ namespace Northbound
public event Action OnDestroyed;
public event Action<TeamType> OnTeamChanged;
private BuildingHealthBar _healthBar;
private UnitHealthBar _healthBar;
private float _lastRegenTime;
public override void OnNetworkSpawn()
@@ -390,7 +390,7 @@ namespace Northbound
}
GameObject healthBarObj = Instantiate(healthBarPrefab, transform);
_healthBar = healthBarObj.GetComponent<BuildingHealthBar>();
_healthBar = healthBarObj.GetComponent<UnitHealthBar>();
if (_healthBar != null)
{

View File

@@ -6,133 +6,11 @@ namespace Northbound
{
/// <summary>
/// 건물 위에 표시되는 체력바
/// UnitHealthBar의 별칭 (하위 호환성 유지)
/// </summary>
public class BuildingHealthBar : MonoBehaviour
[System.Obsolete("Use UnitHealthBar instead. This class is kept for backward compatibility with existing prefabs.")]
public class BuildingHealthBar : UnitHealthBar
{
[Header("UI References")]
public Image fillImage;
public TextMeshProUGUI healthText;
public GameObject barContainer;
[Header("Settings")]
public float heightOffset = 3f;
public bool hideWhenFull = true;
public float hideDelay = 3f;
public float initialShowDuration = 2f; // 건설 완료 시 체력바 표시 시간
[Header("Colors")]
public Color fullHealthColor = Color.green;
public Color mediumHealthColor = Color.yellow;
public Color lowHealthColor = Color.red;
public float mediumHealthThreshold = 0.6f;
public float lowHealthThreshold = 0.3f;
private Building _building;
private Camera _mainCamera;
private float _lastShowTime;
private Canvas _canvas;
private bool _initialized = false;
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;
}
public void Initialize(Building building)
{
_building = building;
transform.localPosition = Vector3.up * heightOffset;
// 건설 완료 시 체력바 표시
ShowBar(initialShowDuration);
_initialized = true;
}
/// <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;
}
}
}
public void UpdateHealth(int currentHealth, int maxHealth)
{
if (fillImage != null)
{
float healthPercentage = maxHealth > 0 ? (float)currentHealth / maxHealth : 0f;
fillImage.fillAmount = healthPercentage;
// 체력에 따른 색상 변경
fillImage.color = GetHealthColor(healthPercentage);
}
if (healthText != null)
{
healthText.text = $"{currentHealth}/{maxHealth}";
}
// 체력 변경 시 체력바 표시
ShowBar();
}
private void Update()
{
// 카메라를 향하도록 회전
if (_mainCamera != null)
{
transform.rotation = Quaternion.LookRotation(transform.position - _mainCamera.transform.position);
}
// 체력이 가득 차면 숨김
if (hideWhenFull && barContainer != null && _building != null && _initialized)
{
float healthPercentage = _building.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;
}
// UnitHealthBar를 상속받아 기존 프리팹과의 호환성 유지
}
}

View File

@@ -7,7 +7,7 @@ namespace Northbound
/// 적대 유닛 (적대세력 또는 몬스터)
/// </summary>
[RequireComponent(typeof(Collider))]
public class EnemyUnit : NetworkBehaviour, IDamageable, ITeamMember
public class EnemyUnit : NetworkBehaviour, IDamageable, ITeamMember, IHealthProvider
{
[Header("Team Settings")]
[Tooltip("이 유닛의 팀 (Hostile = 적대세력, Monster = 몬스터)")]
@@ -20,6 +20,10 @@ namespace Northbound
public GameObject damageEffectPrefab;
public GameObject destroyEffectPrefab;
[Header("Health Bar")]
public bool showHealthBar = true;
public GameObject healthBarPrefab;
private NetworkVariable<int> _currentHealth = new NetworkVariable<int>(
0,
NetworkVariableReadPermission.Everyone,
@@ -37,6 +41,8 @@ namespace Northbound
/// </summary>
public event System.Action<ulong> OnDeath;
private UnitHealthBar _healthBar;
public override void OnNetworkSpawn()
{
base.OnNetworkSpawn();
@@ -46,13 +52,48 @@ namespace Northbound
_currentHealth.Value = maxHealth;
_team.Value = enemyTeam;
}
// 체력 변경 이벤트 구독
_currentHealth.OnValueChanged += OnHealthChanged;
// 체력바 생성
if (showHealthBar && healthBarPrefab != null)
{
CreateHealthBar();
}
}
public override void OnNetworkDespawn()
{
_currentHealth.OnValueChanged -= OnHealthChanged;
base.OnNetworkDespawn();
}
private void OnHealthChanged(int previousValue, int newValue)
{
if (_healthBar != null)
{
_healthBar.UpdateHealth();
}
}
private void CreateHealthBar()
{
if (_healthBar != null)
return;
if (healthBarPrefab == null)
return;
GameObject healthBarObj = Instantiate(healthBarPrefab, transform);
_healthBar = healthBarObj.GetComponent<UnitHealthBar>();
if (_healthBar != null)
{
_healthBar.Initialize(this);
}
}
#region IDamageable Implementation
public void TakeDamage(int damage, ulong attackerId)
@@ -144,6 +185,20 @@ namespace Northbound
#endregion
#region IHealthProvider Implementation
public int GetCurrentHealth() => _currentHealth.Value;
public int GetMaxHealth() => maxHealth;
public float GetHealthPercentage()
{
int max = GetMaxHealth();
return max > 0 ? (float)_currentHealth.Value / max : 0f;
}
#endregion
private void OnDrawGizmosSelected()
{
#if UNITY_EDITOR

View File

@@ -0,0 +1,24 @@
namespace Northbound
{
/// <summary>
/// 체력 정보를 제공하는 인터페이스
/// Building, Player, Enemy 등 체력바가 필요한 모든 유닛이 구현
/// </summary>
public interface IHealthProvider
{
/// <summary>
/// 현재 체력
/// </summary>
int GetCurrentHealth();
/// <summary>
/// 최대 체력
/// </summary>
int GetMaxHealth();
/// <summary>
/// 체력 비율 (0.0 ~ 1.0)
/// </summary>
float GetHealthPercentage();
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 30fb4b69605e00a4eb6cac716420c414

View File

@@ -6,7 +6,7 @@ using UnityEngine.InputSystem;
using Unity.Cinemachine;
using Northbound;
public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageable
public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageable, IHealthProvider
{
[Header("Movement Settings")]
public float rotationSpeed = 10f;
@@ -22,6 +22,10 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl
[SerializeField] private GameObject resourcePickupPrefab; // 자원 드랍 프리팹
[SerializeField] private float respawnDelay = 10f; // 부활 대기 시간 (초)
[Header("Health Bar")]
[SerializeField] private bool showHealthBar = true;
[SerializeField] private GameObject healthBarPrefab;
// 이 플레이어를 제어하는 클라이언트 ID (서버 소유권이지만 논리적 소유자)
private NetworkVariable<ulong> _ownerPlayerId = new NetworkVariable<ulong>(
ulong.MaxValue,
@@ -47,6 +51,7 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl
private Animator _animator;
private NetworkAnimator _networkAnimator;
private PlayerStats _playerStats;
private UnitHealthBar _healthBar;
// 이 플레이어가 로컬 플레이어인지 확인
@@ -88,6 +93,12 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl
_currentHealth.OnValueChanged += OnHealthChanged;
_ownerPlayerId.OnValueChanged += OnOwnerPlayerIdChanged;
// 체력바 생성
if (showHealthBar && healthBarPrefab != null)
{
CreateHealthBar();
}
// 이미 로컬 플레이어로 설정되어 있으면 입력 초기화
TryInitializeLocalPlayer();
}
@@ -443,6 +454,12 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl
private void OnHealthChanged(int previousValue, int newValue)
{
// 체력바 업데이트
if (_healthBar != null)
{
_healthBar.UpdateHealth();
}
if (IsLocalPlayer)
{
// UI 업데이트 등
@@ -451,6 +468,29 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl
#endregion
#region Health Bar
private void CreateHealthBar()
{
if (_healthBar != null)
return;
if (healthBarPrefab == null)
return;
GameObject healthBarObj = Instantiate(healthBarPrefab, transform);
_healthBar = healthBarObj.GetComponent<UnitHealthBar>();
if (_healthBar != null)
{
// 플레이어 체력바는 상시 표시
_healthBar.hideWhenFull = false;
_healthBar.Initialize(this);
}
}
#endregion
#region Gizmos
private void OnDrawGizmosSelected()

View 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;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 860b045e364e57547811662937cb3ac6

View File

@@ -14,7 +14,7 @@ GameObject:
- component: {fileID: 100004}
- component: {fileID: 100005}
m_Layer: 5
m_Name: BuildingHealthBar
m_Name: HealthBar
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
@@ -85,7 +85,7 @@ MonoBehaviour:
m_FallbackScreenDPI: 96
m_DefaultSpriteDPI: 96
m_DynamicPixelsPerUnit: 10
m_PresetInfoIsWorld: 0
m_PresetInfoIsWorld: 1
--- !u!114 &100004
MonoBehaviour:
m_ObjectHideFlags: 0