Unity - UI

땡구의 개발일지·2025년 4월 22일

Unity마스터

목록 보기
14/78

게임의 시각적인 정보를 사용자에게 제공하는 인터페이스다

    // <Event System>
    // 키보드, 마우스, 터치, 등을 게임오브젝트에 이벤트를 전송하는 방법
    // Event System이 씬에 없는 경우 UI가 반응하지 않으니 주의

UI

  • User Interface : 사용자 인터페이스
  • 사용자와 컴퓨터 시스템 사이의 의사소통 매개를 의미함
    • 넓게는 마우스, 키보드와 같은 입력매체도 UI다

유니티에서의 UI

  • 유니티에서는 버튼, 이미지, 텍스트 등과 같은 시각적 매체들이 UI에 해당한다
  • 우리는 uGUI를 배운다. 회사에 따라서 nGUI(구버전)을 쓰는 곳들도 있다

임포트

  • UI 모듈에 따라 임포트 방법이 달라진다

버튼만들기

  • 하이라키 창에서 다음과 같이 선택

캔버스

  • UI 요소들을 가지고 있는 UI 구성틀

  • 모든 UI는 캔버스 위에서 표현된다

  • 위치가 겹칠 경우 나중에 그려지는 UI 요소가 이전 UI 요소를 덮어씀 (해당 문제는 SortGroup을 통해 해결한다)

  • UI위치를 수정하려면 canvase를 더블 클릭하면 된다

  • 그리고 씬 창에서 2D 모드로 바꿔준다

  • UI 편집은 게임의 상황과 무관하다 버튼이 게임 화면을 가린것처럼 씬 창에서 보여도, 실제로 게임에서는 영향이 없다

UI 비활성화

  • 씬 창에서 걸리적 거린다면, Layers 에서 UI에서 눈 모양을 끄면 된다

모양 편집

  • 2D니까 Rect툴을 써서 편집한다
  • 편집 할 때는 게임 창에서 해상도를 고정시키는게 좋다

렌더 모드

  • Screen Space - Overlay

    • 오버레이는 게임 화면과 UI화면을 별도로 렌더하고 합성하는 방식이다
    • 3D에서는 오버레이가 많이 쓰인다
    • 오늘은 오버레이만 배운다
  • Screen Space - Camera

    • 카메라는 메인카메라 앞에 캔버스를 두는 방식이다
    • 2D에서 많이 쓰이는 방식
  • World Space

    • 실제 3D 오브젝트처럼 다루는 방식이다. 다른 게임 오브젝트에 상속시킬 수 있다
    • 정보 표시용으로 많이 쓴다. 이름 태그, Hp바 등

캔버스 사이즈 조절

  • 캔버스 내 UI 요소의 전체적인 크기와 비율을 제어한다

  • Constant Pixel Size

    • 고정된 스크린 해상도 크기 : 모니터가 커지면, 버튼도 작아진다
  • Scale with Screen Size

    • 비율로 조정하기 : 모니터가 커져도 비율은 그대로 유지된다
    • 크게 신경 쓰지 않아도 되는 방법이라 추천된다. 다만, 여러 플랫폼에서 퀄리티를 높이려면 결국 하나하나 조정하는 방식으로 가야된다
  • Constant Physical Size

    • 플랫폼, 모니터에 상관없이 물리적으로 동일한 크기를 갖는다
    • 예를들어 손바닥을 표현할 때 표현 매체에 상관없이 동일한 크기를 출력할 수 있다

버튼의 클릭

  • Graphic Raycaster
  • 레이캐스트 기능을 사용해서 마우스 클릭을 인식한다.

이벤트 시스템

  • UI는 캔버스, 이벤트 시스템 게임 오브젝트가 필수로 포함된다

비주얼라이즈

  • 버튼에 화살표가 생기는 것을 볼 수있는데

  • 여기서 비주얼라이즈 버튼이 활성화 되어 있으면 보이는거다

인풋 모듈

  • 인풋 매니저에서 편집 가능하다

UI Transform

  • 버튼이나 캔버스 같은것들은 보면 transform이 Rect transform이 컴포넌트로 들어가 있는 것을 볼 수 있다
  • 3D 씬의 게임오브젝트들과 다르게 Pos X, Pos Y의 값들은 픽셀이 단위다

Anchors

  • 기준 점이 앵커다. 캔버스 정중앙이 기본값이다. 드래그하면 바꿀 수 있다
  • 각 버튼마다 앵커를 조정해서 배치하면 된다
  • UI의 좌표들은 앵커를 기준으로 위치한다
  • 화면해상도 크기가 늘거나 줄어도 기준에 따라 UI 위치를 달리 하기 때문에 필요하다

앵커 프리셋

  • 프리셋을 이용해서 배치하는것도 방법이다
  • Alt를 누르면서 하면 UI 위치도 같이 변한다
  • 스트레치도 있다. 해상도에 따라 버튼의 크기가 달라진다

분할 앵커

  • 스트레치를 하기 위한 방법.앵커를 반, 또는 쿼터로 쪼개서 배치하면 화면 해상도의 변화가 있어도 알아서 스트레치 된다

피벗

  • UI의 기준점. 앵커는 캔버스가 기준이라면 피벗은 자기자신의 기준이다
  • 피벗을 왼쪽으로 두고, 크기를 변화시키면 오른쪽으로 늘어나는 것을 볼 수 있다

이미지

  • 소스 이미지 : 원본 이미지. 스프라이트
  • 컬러 : 스프라이트 위에 색깔을 덧대어서 칠한다
  • 마테리얼 : 이미지의 속성을 변경한다
  • 레이캐스트 타겟 : 다른 UI위에 칠해진다. 그래서 버튼 위에 두면 버튼이 안눌린다

Raw이미지

  • 쌩 이미지를 넣을때 쓴다

Text


  • 말 그대로 문구를 UI로 화면에 띄우는 거다. 대화창이 해당된다
  • 폰트를 변경하려면 Font Asset 에서 직접 폰트를 넣어줘야 한다
  • Wrapping : 줄이 넘어갈 정도로 긴 글은 자동으로 줄을 바꿔서 출력
  • Overflow : 대화창을 넘어가는 글들을 어떻게 처리할지 선택 가능

Text 컬러 중간만 바꾸기


UI 이벤트

  • 모든 UI에는 이벤트가 달려있다. 클릭 시 포탄 발사, 체력 30이하면 경고음 재생 등 많은 기능을 구현할 수 있다

캔버스와 이벤트 시스템

  • 캔버스는 여러개 만들 수 있다. Sort Order의 숫자가 클 수록 앞에 온다
  • 이벤트 시스템은 단 하나만 존재할 수 있다. 싱글톤 처럼 생각할 수 있는데, 씬 당 하나가 존재하는 애다. Lazy Initialization 처럼 UI를 처음 만들 때 생성 되는 것도 비슷하다
  • 싱글톤으로 써야 되는 UI는 잘 생각해야된다. 예시 : 로딩 UI, 메뉴 UI
  • 이벤트 시스템은 무조건 UI만 다루는 애는 아니다

실습에서의 스코어 UI에 연동

  • 이전 실습에서 점수를 올리는 기능을 구현했다. UI로 스코어를 표현해보자
// 텍스트매니저 프로를 사용한다
using TMPro;
using UnityEngine;
// UI 를 추가
using UnityEngine.UI;

public class ScoreUI : MonoBehaviour
{
    // 버튼 컴포넌트 
    //[SerializeField] Button button;
    // 스크롤바 컴포넌트 
    //[SerializeField] Scrollbar hpbar;
    // 토글 컴포넌트
    //[SerializeField] Toggle toggle;
    // 텍스트 컴포넌트
    // [SerializeField] Text text;
    // 텍스트는 레거시라서 쓰지 않는다

    // TMP_Text 클래스를 쓴다
    [SerializeField] TMP_Text textUI;

    //private void Update()
    //{
    //    // 스코어를 스트링으로 바꾼다
    //    // 업데이트마다 스트링을 불러와 확인하는 것은 효율적이지 않다
    //    // 이벤트를 사용해야 한다
    //    textUI.text = PracticeSingleton.Instance.score.ToString();
    //}
    private void Update()
    {
        // 싱글톤에 저장되는 스코어를 스트링으로 바꿔서 저장한다
        textUI.text = $"Score : {PracticeSingleton.Instance.score.ToString()}";
    }
}
  • 업데이트마다 스트링을 불러와 확인하는 것은 효율적이지 않다
  • UI는 체력 20인데 실제 체력은 10인 경우가 생길 수 있다. 데이터와 UI가 똑같을 필요가 있다. 이럴 때 옵저버 패턴(이벤트)을 이용한다

이벤트 사용하기

  • 스코어에 변동이 있을 때만 스코어를 갱신하도록 해보자. MVC 패턴으로 구현한다

  • 게임매니저 싱글톤 스크립트 수정

    • MVC 패턴에서 M에 해당한다
    public event Action<int> OnScoreChanged;
    private int score;
    
    public int Score { get { return score; } set { score = value; OnScoreChanged?.Invoke(score); } }
    public int maxScore;
    public void GetScore(int score)
    {
        Score += score;
        if(Score> maxScore)
        {
            maxScore = this.score;
        }
    }
    • 점수가 바뀔 때 마다 이벤트로 등록된 함수가 실행된다
  • 스코어UI 스크립트

    • MVC 패턴에서 C에 해당한다
    private void OnEnable()
    {
        SetScore(PracticeSingleton.Instance.Score);
        PracticeSingleton.Instance.OnScoreChanged += SetScore;
    }
    private void OnDisable()
    {
        PracticeSingleton.Instance.OnScoreChanged -= SetScore;
    }
    private void SetScore(int score)
    {
        textUI.text = score.ToString();
    }
    • 맨 처음 UI가 활성화 될 때, 점수를 표기(0점)하고 이벤트에 점수 표기 함수(SetScore)를 추가한다. 이렇게 하면 업데이트마다 최신화 하는게 아니라, 점수가 바뀔 때마다 최신화 된다
    • 스코어 UI가 비활성화 되면 함수를 이벤트에서 뺀다

    • 이제 점수가 제대로 나온다. 화면상의 UI가 MVC 패턴에서 V에 해당한다

MVC 패턴

  • Model - View - Controller 패턴
  • Model : 데이터(변수 함수 등), View : 사용자 인터페이스(UI,View), Controller : 로직(분리된 View와 Model을 관리하기 위한 알고리즘)을 분리하여 프로그램을 관리하는 패턴

구현

  • Model

  • 프로그램의 데이터
  • Controller에 의해 갱신됨(Manipulate)
  • 데이터의 변경 사항을 View에게 Update
  • View

  • 사용자 인터페이스
  • Model의 데이터를 기반으로 사용자에게 내용 표현
  • 사용자에게 입력을 받아 Controller에게 알림
  • 때로는 컨트롤러가 직접 갱신함
  • Controller

  • 로직
  • View의 입력을 받아 논리 로직을 처리
  • 논리 로직의 결과를 Model에 갱신

장점

  1. 설계패턴 중에서 구현이 간단함
  2. 프로그램의 구성 요소를 영향없이 쉽게 고칠 수 있도록 해줌
  3. 유지보수성, 어플리케이션의 확장성, 유연성코드재사용성이 좋아짐

주의점

  • ModelView 둘이 의존성을 갖지 않도록 주의한다

파생 패턴

  • MVP(Model-View-Presenter)
  • MVVM(Model-View-ViewModel)
  • 구분하기에는 애매하다(초보자 기준)
using UnityEngine;
using UnityEngine.Events;

namespace DesignPattern
{
    public class Model
    {
        private int data;
        public int Data
        {
            set
            {
                Debug.Log("[Model] Change data");
                data = value;
                OnDataChanged?.Invoke(value);
            }
            get
            {
                return data;
            }
        }

        public UnityAction<int> OnDataChanged;
    }

    public class View
    {
        public Controller controller;

        public void UpdateData(int data)
        {
            Debug.Log("[View] Update data");
        }

        public void Input()
        {
            Debug.Log("[View] Input");
            controller.Logic();
        }
    }

    public class Controller
    {
        public Model model;

        public void Logic()
        {
            Debug.Log("[Controller] Logic");
            model.Data += 1;
        }
    }

    public class MVCTester
    {
        public void Test()
        {
            Model model = new Model();
            View view = new View();
            Controller controller = new Controller();

            view.controller = controller;
            controller.model = model;
            model.OnDataChanged += view.UpdateData;

            view.Input();
            // 1. [View] Input
            // 2. [Controller] Logic
            // 3. [Model] Change data
            // 4. [View] Update data
        }
    }
}
profile
개발 박살내자

0개의 댓글