using UnityEngine; // 유니티 기능을 불러올거에요 -> UnityEngine을 using UnityEngine.UI; // UI 기능을 불러올거에요 -> UnityEngine.UI를 using TMPro; // TextMeshPro를 사용할거에요 -> TMPro를 /// /// 몬스터 체력바 UI — MonsterClass.OnHealthChanged 이벤트 연동 /// 몬스터 프리팹의 MonsterHP_Canvas에 부착 /// public class MonsterHPUibar : MonoBehaviour // 클래스를 선언할거에요 -> 몬스터 체력바 UI를 { [Header("--- 타겟 ---")] // 인스펙터 제목을 달거에요 -> 타겟 설정을 [Tooltip("MonsterClass가 붙어있는 몬스터 오브젝트")] [SerializeField] private GameObject targetObject; // 변수를 선언할거에요 -> 타겟 오브젝트를 (몬스터 루트) [Header("--- UI 요소 ---")] // 인스펙터 제목을 달거에요 -> UI 요소를 [Tooltip("HP_Filled (Image, Fill 방식)")] [SerializeField] private Image hpFilledImage; // 변수를 선언할거에요 -> 체력바 Fill 이미지를 [Tooltip("HP_Text (TMP) — '현재HP / 최대HP' 표시")] [SerializeField] private TextMeshProUGUI hpText; // 변수를 선언할거에요 -> 체력 텍스트를 // 캐싱된 참조 private MonsterClass _monster; // 변수를 선언할거에요 -> 몬스터 클래스 참조를 private void Awake() // 함수를 실행할거에요 -> 초기화 Awake를 { // 타겟이 비어있으면 부모에서 자동으로 찾기 if (targetObject == null) // 조건이 맞으면 실행할거에요 -> 타겟이 설정 안 됐다면 { targetObject = transform.root.gameObject; // 할당할거에요 -> 최상위 부모 오브젝트를 } if (targetObject != null) // 조건이 맞으면 실행할거에요 -> 타겟이 있다면 { _monster = targetObject.GetComponent(); // 가져올거에요 -> MonsterClass를 if (_monster == null) // 조건이 맞으면 실행할거에요 -> 직접 못 찾았다면 { _monster = targetObject.GetComponentInParent(); // 가져올거에요 -> 부모에서 MonsterClass를 } } } private void OnEnable() // 함수를 실행할거에요 -> 활성화 시 OnEnable을 { if (_monster != null) // 조건이 맞으면 실행할거에요 -> 몬스터가 있다면 { _monster.OnHealthChanged += OnMonsterHealthChanged; // 등록할거에요 -> 체력 변경 이벤트 리스너를 } // 활성화 시 즉시 UI 갱신 (풀에서 재사용될 때 대비) ResetUI(); // 실행할거에요 -> UI 초기화를 } private void OnDisable() // 함수를 실행할거에요 -> 비활성화 시 OnDisable을 { if (_monster != null) // 조건이 맞으면 실행할거에요 -> 몬스터가 있다면 { _monster.OnHealthChanged -= OnMonsterHealthChanged; // 해제할거에요 -> 체력 변경 이벤트 리스너를 } } /// /// MonsterClass.OnHealthChanged(currentHP, maxHP) 이벤트 콜백 /// private void OnMonsterHealthChanged(float currentHP, float maxHP) // 함수를 선언할거에요 -> 체력 변경 콜백을 { // Fill Amount 갱신 if (hpFilledImage != null) // 조건이 맞으면 실행할거에요 -> Fill 이미지가 있다면 { float ratio = (maxHP > 0) ? currentHP / maxHP : 0f; // 비율을 계산할거에요 -> 현재 / 최대로 hpFilledImage.fillAmount = Mathf.Clamp01(ratio); // 값을 설정할거에요 -> 0~1 사이로 Fill Amount를 } // 텍스트 갱신 if (hpText != null) // 조건이 맞으면 실행할거에요 -> 텍스트가 있다면 { hpText.text = $"{currentHP:F0} / {maxHP:F0}"; // 텍스트를 설정할거에요 -> "현재 / 최대" 형식으로 } } /// /// UI를 풀 체력 상태로 초기화 (스폰/재활성화 시) /// private void ResetUI() // 함수를 선언할거에요 -> UI 초기화를 { if (hpFilledImage != null) // 조건이 맞으면 실행할거에요 -> Fill 이미지가 있다면 { hpFilledImage.fillAmount = 1f; // 값을 설정할거에요 -> Fill을 꽉 채우기로 } // _monster가 있으면 실제 스탯으로 텍스트 갱신 if (hpText != null) // 조건이 맞으면 실행할거에요 -> 텍스트가 있다면 { hpText.text = ""; // 텍스트를 비울거에요 -> 초기화로 (첫 OnHealthChanged에서 갱신됨) } } }