[TIL-260210] UGUI, MVC / MVP

데비·2026년 2월 10일

본과정

목록 보기
53/64

오늘 배운 내용

- UGUI

- MVC / MVP


Image & RawImage

Image

Image Type (4가지)

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

Image(Sprite) VS Raw Image(Texture)

Image

  • 버튼
  • 배경
  • 체력바
  • 아이템
  • 아이콘

단점: Sprite 변환 과정이 필요(자원 소모가 상대적으로 있음)

Raw Image

  • CCTV 화면
  • 저격총 Scope
  • 영상
  • 미니맵

단점: 각 텍스처당 Draw Call 발생


Slider

  • Value 값 (0~1)

사용 사례

[환경 설정]

[체력바 - 플레이어, 적, 보스 등등]

[과열기능 - 게임적 허용]


Scroll View

  • Content: 실제 아이템들이 담기는 도화지
  • Movement Type
    - Elastic
    - Clamped

TextMeshPro (TMP)

Q . 왜 Text 대신 TMP를 사용하는가?

  • "랜더링 방식에서 최적화가 일어났기 때문에"
    (Unity6에서 기본적으로 사용중)

MVC / MVP

  • 게임 로직은 건들지 않고 UI만 갈아 끼우는 설계

1. 왜 UI와 로직을 분리해야 하나?

using TMPro;
using UnityEngine;

public class Enemy : MonoBehaviour
{
	public int hp = 100;
    public TextMeshProUGUI hp Text; // UI와 강한 결합
    
    public void OnDamage(int damage)
    {
    	hp -= damage;
        
        // 로직 스크립트 안에서 UI를 건든다 -> UI와 강한 결합
        hpText.text = "HP:" + hp.ToString();
        
        if(hp <= 0)
        {
        	Debug.Log("Enemy 죽음...");
        }
    }
}
  • 유지보수 지옥
    - 프로젝트 커질 시 추적 불가 (어딨는지 못 찾음)

2. MVC vs MVP

  1. MVC
    • 사용자 입력이 Controller로 전달.
    • 의존성: View가 Model을 직접 참조.
  2. MVP
    • 사용자의 입력이 View를 통해 들어와서 Presenter로 전달.
    • 의존성: View와 Model 사이에 참조가 전혀 없음(의존 안함)

3-1. MVC (Model - view - Controller) 예시

[Model - 데이터 관리]

public class PlayerModel_MVC
{
	public int hp = 100;
}

[View - 화면에 출력 - Model 직접 참조중]

using UnityEngine;
using UnityEngine.UI;

public class PlayerView_MVC : MonoBehaviour
{
	public PlayerModel_MVC playerModel;
    public Slider hpSlider;
    
    public void UpdateView()
    {
    	hpSlider.value = playerModel.hp / 100f;
    }
}

[Controller - 입력 처리 및 Model(데이터) 수정]

using UnityEngine;

public class PlayerController_MVC : MonoBehaviour
{
	PlayerModel_MVC playerModel = new PlayerModel_MVC();
    public PlayerView_MVC playerView;
    
    public void OnDamage(int damage)
    {
    	playerModel.hp -= damage; // Model(데이터) 수정
        playerView.UpdateView(); // View(UI - Slider) 갱신
    }
}

3-2. 같은 코드로 MVP 패턴 구현

[Model 파트 MVC와 동일]

[View - 오로지 그려주기만 한다]

using UnityEngine;
using UnityEngine.UI;

public class PlayerView_MVP : MonoBehaviour
{
	[serializeField] Slider slider;
    
    public void SetFillAmount(float ratio)
    {
    	slider.value = ratio;
    }
}

// Model을 직접 참조하고 있지 않다.
// 데이터 가공도 하지 않는다.
// 오로지 그려주기만 한다.

[Presenter - Model(데이터)와 View(화면) 중재]

using UnityEngine;

public class PlayerPresenter_MVP : MonoBehaviour
{
	PlayerModel_MVC model = new PlayerModel_MVC();
    [SerializeField] PlayerView_MVP playerview; // 조립할 대상
    
    public void TakeDamage(int damage)
    {
    	model.hp -= damage;
        
        float ratio = model.hp / 100f;
        playerView.SetFillAmount(ratio)
    }
}

4. MVP 패턴

역할

  • Model
    • 데이터 관리(값만 가짐, 순수 C#)
    • MonoBehaviour 상속받지 않음
  • View
    • 그리기만 하는 녀석 -> UI 컴포넌트 수정/갱신
    • MonoBehaviour 상속받음
  • Presenter
    • Model(데이터)의 변화를 감지해서 View(화면)에 전달/명령내리는 대상

사고방식

  • Model: 어떤 데이터를 관리하지? (선언된 데이터)
  • View: 화면에 뭘 그리지?
  • Presenter: Model(데이터) 업데이트와 그려줄 View(화면)를 누가 중재/관리하나?

5. 실전 예제

1) Basic - 단일 연동 (1:1)
[model]

public class StatModel
{
	public float hp = 100f;
    public float maxHP = 100f;
    public float GetHpRatio() => hp / maxHP;
}

[View]

using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class SliderBarView : MonoBehaviour
{
    public Slider slider;
    public void SetValue(float ratio) => slider.value = ratio;
}

public class TextPercentView : MonoBehaviour
{
    public TMP_Text hpText;
    public void SetProgress(float ratio)
    {
        hpText.text = $"HP; {ratio * 100}%";
    }
}

[presenter]

using UnityEngine;

public class StastPresenter : MonoBehaviour
{
    StatModel model = new StatModel();
    [SerializeField] SliderBarView viewA;
    [SerializeField] TextPercentView viewB;

    public void OnDamage(float damage)
    {
        model.hp -= damage;
        viewA.SetValue(model.GetHpRatio());
        viewB.SetProgress(model.GetHpRatio());
    }
}

2) Advanced - 다중 연동 (1:N)
[Model]

public class ScoreModel
{
	int score;
    
    public event System.Action<int> OnScoreChanged; // 전광판
    
    public void AddScore(int amount)
    {
    	score += amount;
        OnScoreChanged?.Invoke(score);
    }
}

[Presenter]

using UnityEngine;

public class ScorePresenter : MonoBehaviour
{
    [SerializeField] ScoreView scoreView;
    [SerializeField] TargetView targetView;
    [SerializeField] RankingView rankingView;
    [SerializeField] AchievementPopupView popupView;

    ScoreModel model = new ScoreModel();

    void OnEnable()
    {
        model.OnScoreChanged += scoreView.UpdateScoreText;
        model.OnScoreChanged += targetView.UpdateTargetText;
        model.OnScoreChanged += CheckAchievement;  // Q. 왜 View쪽이 아닌 여기에 구현할까?  
    }

    void OnDisable()
    {
        // 구독 해지 안해준다면??  
    }

    void CheckAchievement(int score)
    {
        if(score >= 100) popupView.Show("팝업 떳습니다");
    }
}

6. 안전한 구독: OnEnable / OnDisable

  • OnEnable (구독 시작)
  • OnDisable (구독 해제) -> 메모리 누수/죽은 참조 호출 방지가 핵심

📢중요 ) 밥먹듯이 습관적으로 해주기


7. 정리

  • MVC: 프로젝트가 커지면, View와 Model이 꼬이는(참조가 많이 일어나는)스파게티의 위험이 있다.
  • MVP: Presenter가 Model과 View를 중재해서 깔끔하게 관리 -> 대규모 프로젝트로 가도 문제가 발생할 확률이 적다.

0개의 댓글