주요 구현 내용 요약
- StatusData에 OnValidate 구현
- 인스펙터에서 curValue/maxValue 변경 시 즉시 onUIChanged 호출
- PlayerStatus → BattleUIController → TopUI 이벤트 바인딩 정리
- StatusData.onUIChanged 직접 등록 vs BattleUIController를 통한 간접 등록
- UnderUI와 TopUI의 자원∙체력∙EXP 바 실시간 업데이트
- UnderUI: health.onUIChanged += UpdateHealthBar
- TopUI: Update()에서 매 프레임 playerStatus 참조로 동기화
- TopUI에 PlayerStatus 참조 추가
[SerializeField] private PlayerStatus playerStatus;
private void Update() {
if (playerStatus == null) return;
UpdateLevelDisplay(playerStatus.Level);
UpdateHpBar(playerStatus.Health.Percentage);
UpdateExpBar((float)playerStatus.CurrentExp / playerStatus.ExpToNextLevel);
}
구현 코드 구조 요약
PlayerStatus.cs
private void Update() {
// 자동 회복
food.Add(food.PassiveValue * Time.deltaTime);
mana.Add(mana.PassiveValue * Time.deltaTime);
// HP/EXP 실시간 알림
OnHPChanged?.Invoke(health.Percentage);
if (expToNextLevel > 0) {
float expPct = Mathf.Clamp01((float)currentExp/expToNextLevel);
OnExpChanged?.Invoke(expPct);
}
}
TopUI.cs
[SerializeField] private PlayerStatus playerStatus;
private void Update() {
if (playerStatus == null) return;
UpdateLevelDisplay(playerStatus.Level);
UpdateHpBar(playerStatus.Health.Percentage);
UpdateExpBar((float)playerStatus.CurrentExp / playerStatus.ExpToNextLevel);
}
고민했던 부분 & 해결법
- 에디터 값 변경이 UI에 바로 반영되지 않음
→ OnValidate로 에디터 변경 시점에 onUIChanged 트리거 - UnderUI(직접 등록) vs TopUI(간접 등록) 이벤트 흐름
→ SubscribeToPlayerStatus()가 Start 시 한 번만 호출되는지, 구독이 빠짐없이 됐는지 로그로 확인 - MonoBehaviour 라이프사이클과 Update 중복 호출
→ Awake/Start/Update 순서와 OnEnable 타이밍을 재점검해 초기화 타이밍 오류 방지
배운 점 & 느낀점
- 이벤트 바인딩 방식을 명확히 분리(직접 vs 간접)하니 디버깅이 훨씬 수월하다.
- Unity 라이프사이클( Awake → OnEnable → Start → Update )을 정확히 이해해야 예상치 못한 초기화 타이밍 이슈를 막을 수 있다.
- UI 업데이트 로직을 “어디서, 어떻게” 흐름을 정리해두면 유지보수와 협업이 한결 편해진다!