[공부] 261103 - UI2

강현구
∙∙조회 7

UI 2일차

  •  

 


Unity UI Layout 기능 정리

1. Layout Element

개별 UI의 레이아웃 설정

  • 개별 UI 오브젝트의 레이아웃 크기와 배치 조건을 지정하는 컴포넌트.
  • Layout Group이 자식 UI를 배치할 때 참고하는 정보를 제공한다.

주요 속성

속성설명
Ignore Layout자동 레이아웃 배치에서 제외
Min Width최소 너비
Min Height최소 높이
Preferred Width선호 너비
Preferred Height선호 높이
Flexible Width남는 가로 공간을 차지할 비율
Flexible Height남는 세로 공간을 차지할 비율

사용 예시

  • 특정 버튼만 크게 만들기
  • 특정 UI의 최소 크기 지정
  • 여러 UI 중 특정 UI가 남는 공간을 더 많이 사용하도록 설정

 


2. Vertical Layout Group

 

세로 자동 정렬

  • 자식 UI들을 위에서 아래 방향으로 자동 정렬하는 컴포넌트.
  • 자식 UI가 추가되거나 제거되면 레이아웃을 갱신한다.

주요 속성

속성설명
Padding부모와 자식 사이의 여백
Spacing자식 UI 사이의 간격
Child Alignment자식 UI의 정렬 위치
Control Child Size자식 UI의 크기를 제어
Child Force Expand남는 공간을 자식 UI가 확장해서 사용하도록 설정

사용 예시

  • 설정 메뉴 버튼 정렬
  • 퀘스트 목록
  • 채팅 메시지 목록
  • 세로형 아이템 목록

 


Grid Layout Group

격자형 자동 정

  • 자식 UI들을 격자 형태로 자동 배치하는 컴포넌트.
  • 행과 열을 기준으로 UI를 정렬한다.
  • 기본적으로 자식 UI에 동일한 셀 크기를 적용한다.

주요 속성

속성설명
Padding부모와 자식 사이의 여백
Cell Size각 셀의 크기
Spacing셀 사이의 간격
Start Corner배치 시작 모서리
Start Axis행 또는 열 기준 배치
Child Alignment자식 UI 정렬
Constraint행 또는 열 개수 제한

 

사용 예시

  • 인벤토리 슬롯
  • 캐릭터 선택 화면
  • 스킬 목록
  • 아이템 도감

4. Content Size Fitter

 

콘텐츠에 맞춰 크기 조절

  • 자신의 RectTransform 크기를 콘텐츠의 크기에 맞춰 자동으로 조절하는 컴포넌트.
  • 자식 UI의 크기 정보를 기반으로 부모 UI의 크기를 조절할 때 사용한다.

주요 속성

속성설명
Horizontal Fit가로 크기 조절 방식
Vertical Fit세로 크기 조절 방식

설정 종류

설정설명
Unconstrained자동 크기 조절하지 않음
Min Size최소 크기에 맞춤
Preferred Size선호 크기에 맞춤

사용 예시

  • 채팅 메시지가 늘어날 때 Content 높이 확장
  • 아이템 목록이 늘어날 때 부모 영역 크기 조절
  • 텍스트 길이에 맞춰 UI 크기 변경

5. 기능 비교 및 관계

 

기능핵심 역할
Layout Element개별 UI의 레이아웃 조건 지정
Vertical Layout Group자식 UI 세로 정렬
Grid Layout Group자식 UI 격자 정렬
Content Size Fitter자신의 크기를 콘텐츠에 맞게 조절

 

채팅창을 만든다고 가정하면

  1. Vertical Layout Group으로 채팅 메시지를 세로 정렬.
  2. Content Size Fitter로 메시지가 늘어날 때 Content 높이 조절.
  3. Layout Element로 특정 메시지 UI의 크기 조건 지정.

핵심 개념

  • Layout Group = 자식 UI의 배치 관리
  • Layout Element = 개별 UI의 레이아웃 조건 제공
  • Content Size Fitter = 자신의 크기 자동 조절 주의: Layout Group과 Content Size Fitter를 함께 사용할 때는 부모와 자식의 크기 제어가 서로 충돌하지 않도록 구성해야 한다. Grid Layout Group은 기본적으로 동일한 셀 크기를 사용하므로 개별 Layout Element의 크기 설정이 그대로 적용되지 않을 수 있다.

이 네 가지는 전부 외울 필요는 없어. 우선 배치(Layout Group) / 크기 조건(Layout Element) / 크기 조절(Content Size Fitter) 이 세 가지 역할을 구분


6. 보간

중요 개념

Mathf.Lerp

  • 보간이란 시작 값과 목표 값 사이의 중간 값을 계산하는 방법

 

예를 들어 점수가 0점에서 1000점으로 증가한다고 가정해 보자.

단순히 값을 변경하면:

ini
score = 1000;

즉시 1000점으로 변경돼.

하지만 보간을 사용하면:

yaml
0 → 200 → 400 → 600 → 800 → 1000

이런 식으로 시작 값에서 목표 값까지 점진적으로 변화하는 것처럼 표현할 수 있음

 

  1. 기본 문법

Mathf.Lerp(startValue, endValue, t);

매개변수의미
startValue시작 값
endValue목표 값
t진행률 (0~1)

여기서 가장 중요한 건 t야.

t는 시작 값과 목표 값 사이에서 얼마나 진행했는지를 나타내.

 

 

 

 

csharp
    private IEnumerator AnimateTotalScore()
    {
        float duration = 2f;
        float elapsedTime = 0f;

        while (elapsedTime < duration)
        {
            elapsedTime += Time.deltaTime;

            // 진행률 0~1
            float t = Mathf.Clamp01(elapsedTime / duration);

            // 시작 점수부터 최종 점수까지 보간
            float currentValue = Mathf.Lerp(0f, totalScore, t);

            int displayScore = Mathf.RoundToInt(currentValue);

            textTotalScore.text = $"{displayScore:D9}";

            yield return null;
        }

        // 마지막에는 정확히 최종 점수 표시
        textTotalScore.text = $"{totalScore:D9}";
    }

 


토글그룹

 

 

 

 

 


실습1. 스탯 띄우기

스탯 띄우기 딜레이와

총 점수 띄우기

  • 왼쪽 라벨 + 스탯 띄우기
  • 오른쪽 라벨 + 스탯 띄우기
  • 그다음 아래총점수 뚜루루 올라가는 연출

설계

왼쪽 라벨 코루틴으로 반복해서 띄움 -> 오른쪽도 라벨 코루틴으로 반복해서 띄움 -> 총점수 올리기

csharp
using System.Collections;
using TMPro;
using Unity.VisualScripting;
using UnityEngine;

public class GameOverWindow : GenericWindow
{
    public TMP_Text leftStatsLabel;
    public TMP_Text rightStatsLabel;
    public TMP_Text leftStatsValues;
    public TMP_Text rightStatsValues;


    public TMP_Text textTotalScore;



    //리셋될떄 초기화되는스탯들
    private int[] scores = new int[6]; //초기화해서 쓰기

    private int totalScore;
    //
    public float startValue = 0f;
    float currentTime = 0;

    public override void Open()
    {
        base.Open();
        ResetScores();

    }

    private void ResetScores()
    {
        currentTime = 0f;
        startValue = 0f;
        StartCoroutine(ResetLeft());
    }

    private IEnumerator ResetLeft()
    {
        
        for (int i = 0; i < scores.Length; i++)
        {
            scores[i] = Random.Range(0, 1000);
        }
        

        leftStatsLabel.text = string.Empty;
        rightStatsLabel.text = string.Empty;
        leftStatsValues.text = string.Empty;
        rightStatsValues.text = string.Empty;
        textTotalScore.text = string.Empty;

        for (int i = 0; i < scores.Length; i++)
        {
            yield return new WaitForSeconds(1f);

            TMP_Text label = i < 3 ? leftStatsLabel : rightStatsLabel;
            TMP_Text value = i < 3 ? leftStatsValues : rightStatsValues;

            label.text += $"STAT {i + 1}\n"; //:D 쓰면 숫자만큼 앞에 0으로 채움
            value.text += $"{scores[i % 3]:D4}\n";
        }
        yield return new WaitForSeconds(1f);


        totalScore = Random.Range(0, 100000000);

        yield return StartCoroutine(AnimateTotalScore());


    }

    private IEnumerator AnimateTotalScore()
    {
        float duration = 2f;
        float elapsedTime = 0f;

        while (elapsedTime < duration)
        {
            elapsedTime += Time.deltaTime;

            // 진행률 0~1
            float t = Mathf.Clamp01(elapsedTime / duration);

            // 시작 점수부터 최종 점수까지 보간
            float currentValue = Mathf.Lerp(0f, totalScore, t);

            int displayScore = Mathf.RoundToInt(currentValue);

            textTotalScore.text = $"{displayScore:D9}";

            yield return null;
        }

        // 마지막에는 정확히 최종 점수 표시
        textTotalScore.text = $"{totalScore:D9}";
    }

    public void OnNext()
    {
        manager.Open(Windows.Start);
    }

    public void OnClickContinue()
    {
        manager.Open(Windows.GameOver);
    }

    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {


        
    }
}

 


실습2. 키보드 시스템 만들기

 

  • 커서 깜빡이는 기능
  • 키보드 눌러 글자입력
  • Cancel 누를시 전체 제거
  • Delete 누를씨 지금 글자 제거
  • Accept 누르면
  • 7글자 이상일경우 입력 불가

 

 

 


실습3. 토글 저장 만들기

 

  • 옵션누르면 창열리기
  • Esc 누르면 원래 창으로
  • 스페이스바 누르면 현재 토글 저장
ini
using UnityEngine;
using UnityEngine.UI;

public class DifficultyWindow : GenericWindow
{
    public enum Difficulty
    {
        None = -1,
        Easy,
        Normal,
        Hard,
    }

    int intDifficulty = 1;

    public Toggle[] toggles;

    public Difficulty CurrentDifficulty
    {
        get
        {
            for (int i = 0; i < toggles.Length; i++)
            {
                if(toggles[i].isOn)
                {
                    return (Difficulty)i;
                }
            }
            return Difficulty.None;
        }

        set
        {
            toggles[(int)value].isOn = true;

        }
    }



    private void OnEnable()
    {
        intDifficulty = PlayerPrefs.GetInt("CurrentDifficulty", intDifficulty);

    }
    //private void Start()
    //{
    //    intDifficulty = 1;
    //}


    private void Update()
    {


        if(Input.GetKeyDown(KeyCode.LeftArrow))
        {
            if(intDifficulty > 0)
            {
                intDifficulty--;
            }
        }
        else if(Input.GetKeyDown(KeyCode.RightArrow))
        {
            if (intDifficulty < 2)
            {
                intDifficulty++;
            }
        }

        if(intDifficulty == 0)
        {
            CurrentDifficulty = Difficulty.Easy;
        }
        else if(intDifficulty == 1)
        {
            CurrentDifficulty = Difficulty.Normal;
        }
        else if (intDifficulty == 2)
        {
            CurrentDifficulty = Difficulty.Hard;
        }


        if (Input.GetKeyDown(KeyCode.Alpha1))
        {
            CurrentDifficulty = Difficulty.Easy;
            intDifficulty = 0;

        }
        if(Input.GetKeyDown(KeyCode.Alpha2))
        {
            CurrentDifficulty = Difficulty.Normal;
            intDifficulty = 1;
        }
        if(Input.GetKeyDown(KeyCode.Alpha3))
        {
            CurrentDifficulty = Difficulty.Hard;
            intDifficulty = 2;
        }

        if(Input.GetKeyDown(KeyCode.Escape))
        {
            manager.Open(Windows.Start);
        }


        if (Input.GetKeyDown(KeyCode.Space))
        {
            Debug.Log(CurrentDifficulty);
            Debug.Log(intDifficulty);
            //저장
            PlayerPrefs.SetInt("CurrentDifficulty", intDifficulty);

        }
    }

    public void OnClickEasy(bool value)
    {
        intDifficulty = 0;
        Debug.Log($"OnClickEasy: {value}");
    }

    public void OnClickNormal(bool value)
    {
        intDifficulty = 1;
        Debug.Log($"OnClickNormal: {value}");
    }

    public void OnClickHard(bool value)
    {
        intDifficulty = 2;
        Debug.Log($"OnClickHard: {value}");
    }





}

 

 


 

내용

csharp