게임을 개발할 때 UI 업데이트를 어떻게 관리할 것인가 하는 문제는 매우 중요합니다.
초기에는 UI 요소가 적기 때문에 간단한 싱글톤 UIManager로도 충분하지만, 게임이 점점 커지면서 UI 요소가 늘어나면 유지보수가 어려워질 수 있습니다.
이번 글에서는 기본적인 UIManager 방식부터 시작해, UI 요소가 많아질 때의 문제점과 이를 해결하는 이벤트 기반 UI 업데이트 방식까지 차근차근 살펴보겠습니다.
UIManager 를 활용한 기본적인 UI 관리 방식
일반적으로 UI를 업데이트할 때, FindObjectOfType<T>() 을 사용해서 UI 요소를 찾거나, 싱글톤 UIManager를 활용하는 방식이 많이 사용됩니다. 여기서는 싱글톤 UIManager를 활용하는 기본적인 예제를 먼저 살펴보겠습니다.
using UnityEngine;
using TMPro;
public class UIManager : MonoBehaviour
{
public static UIManager Instance { get; private set; }
[SerializeField] private TextMeshProUGUI scoreText;
[SerializeField] private TextMeshProUGUI healthText;
private void Awake()
{
if (Instance == null) Instance = this;
else Destroy(gameObject);
}
public void UpdateScore(int score)
{
scoreText.text = $"Score: {score}";
}
public void UpdateHealth(int health)
{
healthText.text = $"Health: {health}";
}
}
이제 게임 내에서 UIManager.Instance.UpdateScore(100);을 호출하면 UI가 업데이트됩니다.
UIManager.Instance.UpdateScore(100);
UIManager.Instance.UpdateHealth(80);
이 방식은 UI 요소가 적을 때는 깔끔하고 직관적이지만, UI 요소가 많아지면 관리가 어려워집니다.
UI 요소가 많아질 때의 문제점
UI 요소가 많아지면 모든 UI 를 한 개의 UIManager에서 관리하는 것이 부담스러워지고, 결국 개별 UIManager를 만들게 됩니다.
예를 들어, ScoreManager, HealthManager, ManaManager 같은 클래스로 나누게 됩니다.
public class ScoreManager : MonoBehaviour
{
public static ScoreManager Instance { get; private set; }
[SerializeField] private TextMeshProUGUI scoreText;
private void Awake()
{
if (Instance == null)
Instance = this;
else Destroy(gameObject);
}
public void UpdateScore(int score)
{
scoreText.text = $"Score: {score}";
}
}
public class HealthManager : MonoBehaviour
{
public static HealthManager Instance { get; private set; }
[SerializeField] private TextMeshProUGUI healthText;
private void Awake()
{
if (Instance == null)
Instance = this;
else Destroy(gameObject);
}
public void UpdateHealth(int health) { healthText.text = $"Health: {health}"; }
}
public class ManaManager : MonoBehaviour
{
public static ManaManager Instance { get; private set; }
[SerializeField] private TextMeshProUGUI manaText;
private void Awake()
{
if (Instance == null)
Instance = this;
else Destroy(gameObject);
}
public void UpdateMana(int mana)
{
manaText.text = $"Mana: {mana}";
}
}
이제 UI를 업데이트하려면 각 Manager를 개별적으로 호출해야 합니다.
ScoreManager.Instance.UpdateScore(100);
HealthManager.Instance.UpdateHealth(80);
ManaManager.Instance.UpdateMana(50);
이 방식의 문제는 UI 요소가 추가될 때마다 새로운 Manager가 필요하고, 코드 수정이 많아진다는 점입니다. 유지보수를 쉽게 하기 위해서는 UI 업데이트를 한 곳에서 관리할 필요가 있습니다.
이벤트 기반 UI 업데이트 방식으로 개선
위 문제를 해결하기 위해 이벤트 기반 UIManager 를 활용할 수 있습니다.
UIManager 는 각 UI 요소의 이벤트를 관리하고, UI 요소들은 필요할 때 이벤트를 구독하여 자동으로 UI 를 업데이트할 수 있습니다.
using UnityEngine;
using System;
public class UIManager : MonoBehaviour
{
public static UIManager Instance { get; private set; }
public static event Action<int> OnScoreChanged;
public static event Action<int> OnHealthChanged;
public static event Action<int> OnManaChanged;
private void Awake()
{
if (Instance == null) Instance = this;
else Destroy(gameObject);
}
public void UpdateScore(int score)
{
OnScoreChanged?.Invoke(score);
}
public void UpdateHealth(int health)
{
OnHealthChanged?.Invoke(health);
}
public void UpdateMana(int mana)
{
OnManaChanged?.Invoke(mana);
}
}
이제 개별 UI 클래스들은 UIManager의 이벤트를 구독하여 UI를 업데이트합니다.
public class ScoreUI : MonoBehaviour
{
[SerializeField] private TextMeshProUGUI scoreText;
private void OnEnable() { UIManager.OnScoreChanged += UpdateScore; }
private void OnDisable() { UIManager.OnScoreChanged -= UpdateScore; }
private void UpdateScore(int score)
{
scoreText.text = $"Score: {score}";
}
}
public class HealthUI : MonoBehaviour
{
[SerializeField] private TextMeshProUGUI healthText;
private void OnEnable() { UIManager.OnHealthChanged += UpdateHealth; }
private void OnDisable() { UIManager.OnHealthChanged -= UpdateHealth; }
private void UpdateHealth(int health)
{
healthText.text = $"Health: {health}";
}
}
public class ManaUI : MonoBehaviour
{
[SerializeField] private TextMeshProUGUI manaText;
private void OnEnable() { UIManager.OnManaChanged += UpdateMana; }
private void OnDisable() { UIManager.OnManaChanged -= UpdateMana; }
private void UpdateMana(int mana)
{
manaText.text = $"Mana: {mana}";
}
}
이제 게임에서 UI 업데이트를 호출하면 UIManager 만 호출하면 되고, 개별 UI 요소들은 자동으로 업데이트됩니다.
UIManager.Instance.UpdateScore(100);
UIManager.Instance.UpdateHealth(80);
UIManager.Instance.UpdateMana(50);
정리
초기에는 UIManager 를 개별적으로 나누어 관리하는 것이 좋아 보일 수 있지만, UI 요소가 많아질수록 유지보수가 어려워집니다.
이벤트 기반 UI 업데이트 방식을 사용하면 UIManager가 UI 요소를 직접 참조하지 않아도 되고, UI 변경이 있을 때마다 개별 클래스를 수정할 필요 없이 이벤트만 구독하면 됩니다.
따라서 프로젝트 규모가 커질수록 이벤트 기반 방식이 더 유리합니다.
'Unity' 카테고리의 다른 글
Destroy()의 동작 방식 (0) | 2025.03.16 |
---|---|
유니티 Coroutin 에서 continue 를 쓸 때 주의할 점 (0) | 2025.03.12 |
FindObjectOfType<T>() 함수 설명 (0) | 2025.03.10 |
Unity 에서 Tag 사용시 알아야 할 내용 (1) | 2025.03.10 |
유니티 Inspector 설정 값을 전역으로 사용하기 (0) | 2025.03.10 |