[UI] 플레이어 HUD 및 체력바 개선

- PlayerHUD: HP/MP 바 동기화 개선
- StatBar: 체력바 표시 로직 개선
- Player 프리팹 UI 컴포넌트 설정

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-03-12 01:30:28 +09:00
parent baf4da0f77
commit aa00ee520e
3 changed files with 123 additions and 12 deletions

View File

@@ -21,6 +21,8 @@ GameObject:
- component: {fileID: 6912018896034183004} - component: {fileID: 6912018896034183004}
- component: {fileID: 6585367215453362640} - component: {fileID: 6585367215453362640}
- component: {fileID: 1242716222252539497} - component: {fileID: 1242716222252539497}
- component: {fileID: 3552488436187204500}
- component: {fileID: -5132198055668300151}
m_Layer: 0 m_Layer: 0
m_Name: Player m_Name: Player
m_TagString: Player m_TagString: Player
@@ -104,7 +106,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
GlobalObjectIdHash: 652883547 GlobalObjectIdHash: 291279334
InScenePlacedSourceGlobalObjectIdHash: 0 InScenePlacedSourceGlobalObjectIdHash: 0
DeferredDespawnTick: 0 DeferredDespawnTick: 0
Ownership: 1 Ownership: 1
@@ -134,8 +136,8 @@ MonoBehaviour:
rotationSpeed: 10 rotationSpeed: 10
gravity: -9.81 gravity: -9.81
jumpForce: 5 jumpForce: 5
skillController: {fileID: 0} skillController: {fileID: 6912018896034183004}
animator: {fileID: 0} animator: {fileID: 3426985706796420257}
--- !u!114 &194806265065691022 --- !u!114 &194806265065691022
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -195,7 +197,7 @@ MonoBehaviour:
m_Name: m_Name:
m_EditorClassIdentifier: Colosseum.Game::Colosseum.Player.PlayerNetworkController m_EditorClassIdentifier: Colosseum.Game::Colosseum.Player.PlayerNetworkController
ShowTopMostFoldoutHeaderGroup: 1 ShowTopMostFoldoutHeaderGroup: 1
characterStats: {fileID: 0} characterStats: {fileID: -5132198055668300151}
--- !u!114 &8606252901290138286 --- !u!114 &8606252901290138286
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -328,8 +330,45 @@ MonoBehaviour:
m_Name: m_Name:
m_EditorClassIdentifier: Colosseum.Game::Colosseum.Abnormalities.AbnormalityManager m_EditorClassIdentifier: Colosseum.Game::Colosseum.Abnormalities.AbnormalityManager
ShowTopMostFoldoutHeaderGroup: 1 ShowTopMostFoldoutHeaderGroup: 1
characterStats: {fileID: 0} characterStats: {fileID: -5132198055668300151}
networkController: {fileID: 0} networkController: {fileID: 1664515335065415329}
--- !u!114 &3552488436187204500
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6473031571298860035}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d1f7d13276f272b428bddd4d9aa5b3d8, type: 3}
m_Name:
m_EditorClassIdentifier: Colosseum.Game::Colosseum.Team
teamType: 1
--- !u!114 &-5132198055668300151
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6473031571298860035}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fae0149926eea244dad932b67ee76f7b, type: 3}
m_Name:
m_EditorClassIdentifier: Colosseum.Game::Colosseum.Stats.CharacterStats
strength:
baseValue: 10
dexterity:
baseValue: 10
intelligence:
baseValue: 10
vitality:
baseValue: 5
wisdom:
baseValue: 100
spirit:
baseValue: 100
--- !u!1001 &7705728874586931617 --- !u!1001 &7705728874586931617
PrefabInstance: PrefabInstance:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0

View File

@@ -1,3 +1,4 @@
using System;
using UnityEngine; using UnityEngine;
using Colosseum.Player; using Colosseum.Player;
@@ -28,22 +29,26 @@ namespace Colosseum.UI
private void Update() private void Update()
{ {
// 플레이어가 아직 없으면 계속 찾기
if (targetPlayer == null && autoFindPlayer) if (targetPlayer == null && autoFindPlayer)
{ {
FindLocalPlayer(); FindLocalPlayer();
} }
}
if (targetPlayer != null) private void OnDestroy()
{ {
UpdateStatBars(); // 이벤트 구독 해제
} UnsubscribeFromEvents();
} }
private void FindLocalPlayer() private void FindLocalPlayer()
{ {
var players = FindObjectsByType<PlayerNetworkController>(FindObjectsSortMode.None); var players = FindObjectsByType<PlayerNetworkController>(FindObjectsSortMode.None);
Debug.Log($"[PlayerHUD] Finding player... found {players.Length} players");
foreach (var player in players) foreach (var player in players)
{ {
Debug.Log($"[PlayerHUD] Player {player.OwnerClientId}: IsOwner={player.IsOwner}, IsSpawned={player.IsSpawned}");
if (player.IsOwner) if (player.IsOwner)
{ {
SetTarget(player); SetTarget(player);
@@ -57,20 +62,81 @@ namespace Colosseum.UI
/// </summary> /// </summary>
public void SetTarget(PlayerNetworkController player) public void SetTarget(PlayerNetworkController player)
{ {
Debug.Log($"[PlayerHUD] SetTarget called: {(player != null ? $"Player {player.OwnerClientId}" : "null")}");
// 이전 타겟 구독 해제
UnsubscribeFromEvents();
targetPlayer = player; targetPlayer = player;
// 새 타겟 구독
SubscribeToEvents();
// 초기 값 설정
UpdateStatBars();
Debug.Log($"[PlayerHUD] Initial HP: {targetPlayer?.Health}/{targetPlayer?.MaxHealth}, MP: {targetPlayer?.Mana}/{targetPlayer?.MaxMana}");
}
private void SubscribeToEvents()
{
if (targetPlayer == null) return;
targetPlayer.OnHealthChanged += HandleHealthChanged;
targetPlayer.OnManaChanged += HandleManaChanged;
}
private void UnsubscribeFromEvents()
{
if (targetPlayer == null) return;
targetPlayer.OnHealthChanged -= HandleHealthChanged;
targetPlayer.OnManaChanged -= HandleManaChanged;
}
private void HandleHealthChanged(float oldValue, float newValue)
{
if (healthBar != null && targetPlayer != null)
{
healthBar.SetValue(newValue, targetPlayer.MaxHealth);
}
}
private void HandleManaChanged(float oldValue, float newValue)
{
if (manaBar != null && targetPlayer != null)
{
manaBar.SetValue(newValue, targetPlayer.MaxMana);
}
} }
private void UpdateStatBars() private void UpdateStatBars()
{ {
if (targetPlayer == null)
{
Debug.Log("[PlayerHUD] UpdateStatBars: targetPlayer is null");
return;
}
Debug.Log($"[PlayerHUD] UpdateStatBars: HP={targetPlayer.Health}/{targetPlayer.MaxHealth}, MP={targetPlayer.Mana}/{targetPlayer.MaxMana}");
if (healthBar != null) if (healthBar != null)
{ {
healthBar.SetValue(targetPlayer.Health, targetPlayer.MaxHealth); healthBar.SetValue(targetPlayer.Health, targetPlayer.MaxHealth);
} }
else
{
Debug.LogWarning("[PlayerHUD] healthBar is null!");
}
if (manaBar != null) if (manaBar != null)
{ {
manaBar.SetValue(targetPlayer.Mana, targetPlayer.MaxMana); manaBar.SetValue(targetPlayer.Mana, targetPlayer.MaxMana);
} }
else
{
Debug.LogWarning("[PlayerHUD] manaBar is null!");
}
} }
} }
} }

View File

@@ -44,8 +44,10 @@ namespace Colosseum.UI
if (!smoothTransition) if (!smoothTransition)
{ {
displayValue = currentValue; displayValue = currentValue;
UpdateVisuals();
} }
// 항상 즉시 시각적 업데이트 수행
UpdateVisuals();
} }
private void Update() private void Update()
@@ -63,7 +65,11 @@ namespace Colosseum.UI
private void UpdateVisuals() private void UpdateVisuals()
{ {
if (maxValue <= 0f) return; if (maxValue <= 0f)
{
Debug.LogWarning($"[StatBar:{gameObject.name}] UpdateVisuals: maxValue is {maxValue}, skipping");
return;
}
float ratio = Mathf.Clamp01(displayValue / maxValue); float ratio = Mathf.Clamp01(displayValue / maxValue);