From 2cc24188b381a978c276cf2dd9bbf9f844d9af30 Mon Sep 17 00:00:00 2001 From: dal4segno Date: Tue, 10 Mar 2026 15:55:56 +0900 Subject: [PATCH] =?UTF-8?q?[UI]=20=EC=B2=B4=EB=A0=A5/=EB=A7=88=EB=82=98=20?= =?UTF-8?q?=EB=B0=94=20HUD=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - StatBar: Slider/Image.Fill 방식 지원 - PlayerHUD: PlayerNetworkController 연동 - TextMeshPro 어셈블리 참조 추가 - Fantasy Warrior HUD 애셋 임포트 --- Assets/Scripts/Colosseum.Game.asmdef | 3 +- Assets/Scripts/UI/PlayerHUD.cs | 76 ++++++++++++++++++ Assets/Scripts/UI/PlayerHUD.cs.meta | 2 + Assets/Scripts/UI/StatBar.cs | 111 +++++++++++++++++++++++++++ Assets/Scripts/UI/StatBar.cs.meta | 2 + 5 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 Assets/Scripts/UI/PlayerHUD.cs create mode 100644 Assets/Scripts/UI/PlayerHUD.cs.meta create mode 100644 Assets/Scripts/UI/StatBar.cs create mode 100644 Assets/Scripts/UI/StatBar.cs.meta diff --git a/Assets/Scripts/Colosseum.Game.asmdef b/Assets/Scripts/Colosseum.Game.asmdef index 320bcfdb..9f849cf1 100644 --- a/Assets/Scripts/Colosseum.Game.asmdef +++ b/Assets/Scripts/Colosseum.Game.asmdef @@ -5,7 +5,8 @@ "Unity.Netcode.Runtime", "Unity.Networking.Transport", "Unity.Transport", - "Unity.InputSystem" + "Unity.InputSystem", + "Unity.TextMeshPro" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/Assets/Scripts/UI/PlayerHUD.cs b/Assets/Scripts/UI/PlayerHUD.cs new file mode 100644 index 00000000..522c1a1a --- /dev/null +++ b/Assets/Scripts/UI/PlayerHUD.cs @@ -0,0 +1,76 @@ +using UnityEngine; +using Colosseum.Player; + +namespace Colosseum.UI +{ + /// + /// 플레이어 HUD - 체력/마나 바 관리 + /// + public class PlayerHUD : MonoBehaviour + { + [Header("Stat Bars")] + [SerializeField] private StatBar healthBar; + [SerializeField] private StatBar manaBar; + + [Header("Target")] + [Tooltip("자동으로 로컬 플레이어 찾기")] + [SerializeField] private bool autoFindPlayer = true; + + private PlayerNetworkController targetPlayer; + + private void Start() + { + if (autoFindPlayer) + { + FindLocalPlayer(); + } + } + + private void Update() + { + if (targetPlayer == null && autoFindPlayer) + { + FindLocalPlayer(); + } + + if (targetPlayer != null) + { + UpdateStatBars(); + } + } + + private void FindLocalPlayer() + { + var players = FindObjectsByType(FindObjectsSortMode.None); + foreach (var player in players) + { + if (player.IsOwner) + { + SetTarget(player); + break; + } + } + } + + /// + /// 추적할 플레이어 설정 + /// + public void SetTarget(PlayerNetworkController player) + { + targetPlayer = player; + } + + private void UpdateStatBars() + { + if (healthBar != null) + { + healthBar.SetValue(targetPlayer.Health, targetPlayer.MaxHealth); + } + + if (manaBar != null) + { + manaBar.SetValue(targetPlayer.Mana, targetPlayer.MaxMana); + } + } + } +} diff --git a/Assets/Scripts/UI/PlayerHUD.cs.meta b/Assets/Scripts/UI/PlayerHUD.cs.meta new file mode 100644 index 00000000..1308d51b --- /dev/null +++ b/Assets/Scripts/UI/PlayerHUD.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e956d7232e6a45246ac4d8079f12f9c3 \ No newline at end of file diff --git a/Assets/Scripts/UI/StatBar.cs b/Assets/Scripts/UI/StatBar.cs new file mode 100644 index 00000000..f1fca4e7 --- /dev/null +++ b/Assets/Scripts/UI/StatBar.cs @@ -0,0 +1,111 @@ +using UnityEngine; +using UnityEngine.UI; +using TMPro; + +namespace Colosseum.UI +{ + /// + /// 체력/마나 바 UI 컴포넌트 + /// Slider 또는 Image.Fill 방식 지원 + /// + public class StatBar : MonoBehaviour + { + [Header("References - Slider 방식")] + [SerializeField] private Slider slider; + + [Header("References - Fill Image 방식")] + [SerializeField] private Image fillImage; + + [Header("References - 텍스트")] + [SerializeField] private TMP_Text valueText; + + [Header("Colors")] + [SerializeField] private Color fullColor = Color.green; + [SerializeField] private Color lowColor = Color.red; + [SerializeField] private float lowThreshold = 0.3f; + [SerializeField] private bool useColorTransition = true; + + [Header("Animation")] + [SerializeField] private bool smoothTransition = true; + [SerializeField] private float lerpSpeed = 5f; + + private float currentValue; + private float maxValue; + private float displayValue; + + /// + /// 바 값 설정 + /// + public void SetValue(float current, float max) + { + currentValue = current; + maxValue = max; + + if (!smoothTransition) + { + displayValue = currentValue; + UpdateVisuals(); + } + } + + private void Update() + { + if (smoothTransition && !Mathf.Approximately(displayValue, currentValue)) + { + displayValue = Mathf.Lerp(displayValue, currentValue, lerpSpeed * Time.deltaTime); + + if (Mathf.Abs(displayValue - currentValue) < 0.01f) + displayValue = currentValue; + + UpdateVisuals(); + } + } + + private void UpdateVisuals() + { + if (maxValue <= 0f) return; + + float ratio = Mathf.Clamp01(displayValue / maxValue); + + // Slider 방식 + if (slider != null) + { + slider.value = ratio; + + // Slider 내부 Fill 이미지 색상 변경 + if (useColorTransition && slider.fillRect != null) + { + var fillImg = slider.fillRect.GetComponent(); + if (fillImg != null) + { + fillImg.color = ratio <= lowThreshold ? lowColor : fullColor; + } + } + } + // Image.Fill 방식 + else if (fillImage != null) + { + fillImage.fillAmount = ratio; + + if (useColorTransition) + { + fillImage.color = ratio <= lowThreshold ? lowColor : fullColor; + } + } + + // 텍스트 + if (valueText != null) + { + valueText.text = $"{Mathf.CeilToInt(displayValue)} / {Mathf.CeilToInt(maxValue)}"; + } + } + + private void OnValidate() + { + if (slider == null) + slider = GetComponentInChildren(); + if (fillImage == null && slider == null) + fillImage = GetComponentInChildren(); + } + } +} diff --git a/Assets/Scripts/UI/StatBar.cs.meta b/Assets/Scripts/UI/StatBar.cs.meta new file mode 100644 index 00000000..a1edc161 --- /dev/null +++ b/Assets/Scripts/UI/StatBar.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b5c5d0fa667f83d4399abb45ffcaea31 \ No newline at end of file