[20260210] UI 기본 및 연동

SmartBear·2026년 2월 10일

UI

Static / Dynamic / Popup

Image & Raw Image

Image

Image Type

  • Simple: 이미지를 있는 그대로 출력. (배경, 아이콘)
  • Sliced: 테두리는 유지하고 중심만 늘리는 방식 (버튼)
  • Tiled: 이미지(중심)을 타일 형태로 반복.
  • Filled: 이미지 일부를 노출 (체력바, 스킬 쿨타임)
    • Fill Amount (0 - 1)
    • Fill Method

Image(Sprite) vs Raw Image(Texture)

  • Image; 버튼, 배경, 체력바, 아이템, 아이콘
    • 단점: Sprite 변환 과정 필요 (자원 소모가 상대적으로 있음)
  • Raw Image; CCTV, Scope, Minimap, Potal, 영상
    • 단점: Texture 당 Draw Call 발생

버튼

  • On Click 함수에 등록 (Event 기반)

Slider

  • 환경 설정
  • 맵 확대 축소
  • 체력바

Scroll View

  • 예제: Inventory
  • Content: 실제 보여야 할 내용(컨텐츠)
  • Movement Type
    • Elastic
    • Clamped

실습

  • Scroll Rect 컴포넌트
  • Content 파트
    • Layout 컴포넌트 (Horizontal, Vertical, Grid)
    • Content 자식 UI 컴포넌트 추가로 동작 여부 확인

Text Mesh Pro

Q. Why TMP?

A. Render 방식 변화에 따른 최적화가 이루어짐

Unity6 에서는 기본으로 사용

폰트

  • 다운 받고, 받은 폰트에 우클릭->Create->TMP->SDF 로 변환 후 사용

UI 연동 With MVC (Model View Controller)

게임 Logic 은 건들지 않고 UI 만 갈아 끼우는 설계
참고: MVP(Model-View-Presenter) 패턴도 있음.

Q. Why UI 와 로직을 분리 해야하나?

using TMPro;
using UnityEngine;

public class Enemy : MonoBehaviour
{
    public int hp = 100;
    public TextMeshProUGUI hpText;

    public void OnDamage(int damage) 
    {
        hp -= damage;
        hpText.text = "HP: " + hp.ToString();        
    }
}

👉A. 유지보수의 지옥

MVC vs MVP vs MVVM

  • 공통
    • Model: 데이터, MonoBehaviour 상속 X
    • View: UI, MonoBehaviour 상송 O

MVC

  1. 사용자 입력이 Controller 로 전달.
  2. 의존성: View 가 Model 을 직접 참조
    1. 프로젝트가 커질수록 Model -> View 의 의존성이 많아짐.
    2. 유지보수가 어려워짐.

예시

// PlayerModel.cs
public class PlayerModel
{
    public int Hp = 100;    
}

// PlayerView.cs
using UnityEngine;
using UnityEngine.UI;

public class PlayerView : MonoBehaviour
{
    public PlayerModel playerModel;
    public Slider hpSlider;

    public void UpdateView() 
    {
        hpSlider.value = playerModel.Hp / 100f;
    }
}

// PlayerController.cs
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public PlayerModel playerModel = new();
    public PlayerView playerView;

    public void OnDamage(int damage) 
    {
        playerModel.Hp -= damage;
        playerView.UpdateView();
    }
}

MVP

  1. 사용자 입력이 View 를 통해 들어와 Presenter로 전달.
  2. 의존성: View 와 Model 사이에 참조가 전혀 없음.

예시

// PlayerModel.cs
public class PlayerModel
{
    public int Hp = 100;    
}

// PlayerView.cs
using UnityEngine;
using UnityEngine.UI;

public class PlayerView : MonoBehaviour
{
    public Slider hpSlider;

    public void SetFillAmount(float ratio) 
    {
        hpSlider.value = ratio;
    }
}

// PlayerPresenter.cs
using UnityEngine;

public class PlayerPresenter : MonoBehaviour
{
    public PlayerModel playerModel = new();
    public PlayerView playerView;

    public void TakeDamage(int damage) 
    {
        playerModel.Hp -= damage;
        float ratio = playerModel.hp / 100f;
        playerView.SetFillAmount(ratio);
    }
}

MVP 실전 예제

1. Basic = 단일 연동

  • StatusModel.cs
public class StatusModel
{
    public float Hp;
    public float MaxHp;
    public float GetRatio() => Hp / MaxHp;
}
  • StatusView.cs
using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class StatusView : MonoBehaviour
{
    [SerializeField] private Slider _slider;

    public void SetValue(float ratio) => _slider.value = ratio;
}

public class TextPercentView : MonoBehaviour
{
    public TMP_Text _text;

    public void SetProgress(float ratio) => _text.text = $"{ratio}%";
}
  • StatusPresenter.cs
using UnityEngine;

public class StatusPresenter : MonoBehaviour
{
    private StatusModel _statusModel = new ();
    [SerializeField] private StatusView _statusView;
    [SerializeField] private TextPercentView _textView;

    public void TakeDamage(float damage)
    {
        _statusModel.Hp -= damage;
        _statusView.SetValue(_statusModel.GetRatio());
        _textView.SetProgress(_statusModel.GetRatio());
    }
    
}

2. Advanced = 다중 연동

  • ScoreModel.cs
public class ScoreModel
{
    private int score;
    public event System.Action<int> OnScoreChange;

    public void AddScore(int amount)
    {
        score += amount;
        OnScoreChange?.Invoke(amount);
    }
}
  • ScoreViews.cs
using UnityEngine;

public class ScoreView : MonoBehaviour
{
    public void UpdateScore(int score)
    {
        // ToDo: Do something
    }
}

public class TargetView : MonoBehaviour
{
    public void UpdateScore(int score)
    {
        // ToDo: Do something
    }
}

public class RankingView : MonoBehaviour
{
    public void UpdateScore(int score)
    {
        // ToDo: Do something
    }
}
  • ScorePresenter.cs
using UnityEngine;

public class ScorePresenter : MonoBehaviour
{
    [SerializeField] private ScoreView _scoreView;
    [SerializeField] private TargetView _targetView;
    [SerializeField] private RankingView _rankingView;
    [SerializeField] private PopUpView _popUpView;
    private ScoreModel _scoreModel = new();

    private void OnEnable()
    {
        _scoreModel.OnScoreChange += _scoreView.UpdateScore;
        _scoreModel.OnScoreChange += _targetView.UpdateScore;
        _scoreModel.OnScoreChange += _rankingView.UpdateScore;
        _scoreModel.OnScoreChange += CheckAchivement;
    }

    private void OnDisable()
    {
        _scoreModel.OnScoreChange -= _scoreView.UpdateScore;
        _scoreModel.OnScoreChange -= _targetView.UpdateScore;
        _scoreModel.OnScoreChange -= _rankingView.UpdateScore;
        _scoreModel.OnScoreChange -= CheckAchivement;
    }

    private void CheckAchivement(int score)
    {
        if (score > 100)
        {
            _popUpView.Show(score.ToString());
        }
    }
}

QnA 내용

❓: UI 뿐만 아니라 다른 기능들도 MVP 로 연결하는 것이 보통인가? 혹은 외적으로 보이는 기능 외 게임 내 기능끼리와의 연결도 MVP 로 하는 것이 보통인가?
(게임 규모에 따라 다르겠지만)

👉: 클라이언트 게임 개발에서는 View 는 대부분 UI 에 해당하기 때문에 다른 기능이나 클래스등과의 연결을 MVP 로 하는 경우는 흔치 않다. 다만, 응용하여 적용하거나 하는 것은 개발자 개인의 역량에 따를 것 같다.

❓: 보통 게임 개발에서 데이터 모델링의 주체는 "개발자"일 것 같은데, 모델링을 위해서는 데이터 지표가 제대로 나와야 한다. 그것은 기획의 영역에 가까운가?
👉: 개발 전 데이터에 대한 개념이나 정의 등은 기획에서 넘어오는 경우가 많지만, 상세 부분은 개발자의 역량이 적용되는 부분이 있다. 그리고 이 또한 규모에 따라도 다를 것 같다.

profile
Python Dev with Infra -> Game Programmer

0개의 댓글