본문 바로가기

Unity3D/C#

유니티 Canvas Scaler 해상도 대응 · Match 값 고르기 (Scale With Screen Size)

반응형

CANVAS SCALER ScreenSize

 

유니티 UI 를 여러 해상도에 맞추려면 Canvas 에 붙은 Canvas Scaler 의 UI Scale Mode 를 Scale With Screen Size 로 바꾸고, UI 를 그린 기준 해상도(Reference Resolution)를 넣습니다. 비율이 다른 화면에서 너비와 높이 중 어디에 맞출지는 Screen Match Mode 와 Match 값으로 정합니다.

 

canvas_scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
canvas_scaler.referenceResolution = new Vector2(1080f, 1920f);   // UI 를 그린 기준 화면
canvas_scaler.matchWidthOrHeight = 0.5f;                         // 0 너비 · 1 높이

 

에디터의 Game 창에서는 버튼이 딱 맞았는데 폰에서 켜 보면 글자가 깨알처럼 작거나, 화면 양옆이 잘리거나, 태블릿에서 위아래 버튼이 화면 밖으로 나가는 일이 있습니다. Canvas Scaler 를 기본값(Constant Pixel Size)으로 두었거나, 맞추는 기준이 화면 비율과 맞지 않을 때 생기는 일입니다. 이번 글에서는 Canvas Scaler 를 코드로 맞추고 지금 화면의 배율과 UI 크기를 글자로 띄워 보는 CanvasScaleInfo 를 만들고, 폰 · 태블릿 해상도마다 Match 값에 따라 UI 가 어떤 크기가 되는지 계산해 비교했습니다.

 

C# (UNITY 3D)

 

 

CanvasScaleInfo 를 Canvas 에 붙이면 시작할 때 Canvas Scaler 가 Scale With Screen Size · 기준 1080 x 1920 · Match 0.5 로 맞춰집니다. text_info 를 넣어 두면 화면 크기가 바뀔 때마다 「화면 1080 x 2400 · 배율 · UI 크기」 가 글자로 바뀌어, 기기마다 UI 가 몇 단위 크기로 잡히는지 바로 볼 수 있습니다.

 

1. 준비 — Canvas Scaler 가 하는 일

 

using TMPro;
using UnityEngine.UI;

 

CanvasScaler 는 UnityEngine.UI, 정보 글자 TMP_Text 는 TMPro 에 있습니다.

Hierarchy 에서 UI 를 하나 만들면 Canvas 에 Canvas Scaler 가 함께 붙습니다. 기본인 Constant Pixel Size 는 UI 를 화면 픽셀 그대로 그려서, 해상도가 높은 폰일수록 UI 가 작아 보입니다. Scale With Screen Size 로 바꾸면 UI 는 늘 기준 해상도 단위로 놓이고, 실제 화면에 맞게 통째로 키우거나 줄입니다. 세로 게임이면 기준을 1080 x 1920 처럼 세로로 넣고, Game 창도 같은 해상도로 두고 배치하면 편합니다.

 

2. Canvas Scaler 를 맞추고 값을 띄우는 코드

 

using TMPro;
using UnityEngine;
using UnityEngine.UI;

//CodeFinder 코드파인더
//From https://codefinder.janndk.com/
[RequireComponent(typeof(CanvasScaler))]
public class CanvasScaleInfo : MonoBehaviour
{
    public Vector2 reference_resolution = new Vector2(1080f, 1920f);  // UI 를 그린 기준 화면(세로 게임)
    [Range(0f, 1f)] public float match = 0.5f;                       // 0 이면 너비, 1 이면 높이에 맞춤
    public TMP_Text text_info;                                        // 지금 값을 띄울 글자(없어도 됨)

    CanvasScaler canvas_scaler;
    Canvas canvas;
    int last_width;
    int last_height;

    void Awake()
    {
        canvas_scaler = GetComponent<CanvasScaler>();
        canvas = GetComponent<Canvas>();
        canvas_scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
        canvas_scaler.referenceResolution = reference_resolution;
        canvas_scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
        canvas_scaler.matchWidthOrHeight = match;
    }

    void LateUpdate()
    {
        if (Screen.width == last_width && Screen.height == last_height)
            return;
        last_width = Screen.width;                    // 회전하거나 창 크기가 바뀌었을 때만
        last_height = Screen.height;
        Show_Info();
    }

    public void Show_Info()
    {
        if (text_info == null || canvas == null)
            return;
        Vector2 size = ((RectTransform)canvas.transform).rect.size;      // UI 단위로 본 화면 크기
        text_info.text = "화면 " + Screen.width + " x " + Screen.height
            + "\n배율 " + canvas.scaleFactor.ToString("F3")
            + "\nUI 크기 " + size.x.ToString("F0") + " x " + size.y.ToString("F0");
    }

    public static float Calc_Scale(Vector2 screen, Vector2 reference, float match)
    {
        float log_width = Mathf.Log(screen.x / reference.x, 2f);         // 너비 비율(로그)
        float log_height = Mathf.Log(screen.y / reference.y, 2f);        // 높이 비율(로그)
        return Mathf.Pow(2f, Mathf.Lerp(log_width, log_height, match));
    }
}

 

Awake 에서 Canvas Scaler 의 네 값을 코드로 넣습니다. 인스펙터에서 같은 값을 골라도 되지만, 씬이 여러 개일 때 Canvas 마다 손으로 맞추다 하나를 빠뜨리는 일을 줄여 줍니다. LateUpdate 는 화면 크기가 바뀐 프레임에만 Show_Info 를 불러, Canvas Scaler 가 그 프레임의 배율을 넣은 뒤의 scaleFactor(실제 픽셀 ÷ UI 단위)와 Canvas 자신의 크기(UI 단위로 본 화면 크기)를 보여 줍니다. Update 에 두면 Canvas Scaler 보다 먼저 돌 수 있어 바뀌기 전 값이 남습니다.

Calc_Scale 은 Match Width or Height 모드에서 배율이 정해지는 식입니다. 너비 비율과 높이 비율을 로그로 바꿔 Match 값만큼 섞은 뒤 되돌립니다. Match 0 이면 「화면 너비 ÷ 기준 너비」, 1 이면 「화면 높이 ÷ 기준 높이」 가 그대로 배율이 됩니다. 이 식으로 낸 값이 Unity 가 실제로 넣은 scaleFactor 와 같은지 확인했습니다(3절).

이 스크립트는 Unity 6000.6.3f1 에서 컴파일 오류 없이 들어갔고, 배치 모드에서 Awake · LateUpdate 를 불러도 예외가 없었습니다. 배치 모드는 화면이 640 x 480 하나뿐이라 실제 폰에서 띄워 보지는 못했고, 다른 기기 해상도는 Calc_Scale 로 계산했습니다.

 

변수(variable)
reference_resolution(Vector2) : UI 를 그린 기준 해상도
match(float) : 너비(0)와 높이(1) 중 어디에 맞출지
text_info(TMP_Text) : 지금 값을 띄울 글자
canvas_scaler(CanvasScaler) : 같은 오브젝트의 Canvas Scaler
canvas(Canvas) : 같은 오브젝트의 Canvas
last_width · last_height(int) : 지난번에 본 화면 크기

 

함수(function)
Awake : Canvas Scaler 를 Scale With Screen Size · 기준 해상도 · Match 로 맞춥니다.
LateUpdate : 화면 크기가 바뀐 때만 Show_Info 를 부릅니다.
Show_Info : 화면 크기 · 배율 · UI 크기를 글자로 띄웁니다.
Calc_Scale : 화면 · 기준 해상도 · Match 로 배율을 계산합니다.

 

3. 기기마다 UI 가 잡히는 크기

 

기준 1080 x 1920 을 폰 · 태블릿에 넣었을 때 Match 0 · 0.5 · 1 마다 UI 크기 — 길쭉한 폰은 Match 1 에서 옆이, 태블릿은 Match 0 에서 위아래가 잘림

 

먼저 배치 모드 화면(640 x 480)에서 Unity 가 넣은 scaleFactor 를 읽어 보니 Match 0 은 0.5926, 0.5 는 0.3849, 1 은 0.2500 이었고, Calc_Scale 로 낸 값과 소수 넷째 자리까지 같았습니다. Expand 는 너비 · 높이 비율 중 작은 쪽(0.25), Shrink 는 큰 쪽(0.5926)과 같았습니다.

그 식으로 기준 1080 x 1920 을 여러 기기에 넣어 보면, 1080 x 1920 · 720 x 1280 처럼 비율이 같은 화면은 Match 와 상관없이 UI 크기가 1080 x 1920 그대로입니다. 차이는 비율이 다를 때 납니다. 요즘 폰처럼 더 길쭉한 1080 x 2400 에서 Match 1(높이)이면 UI 너비가 864 로 줄어, 1080 폭으로 그린 UI 의 양옆이 잘립니다. Match 0(너비)이면 1080 x 2400 이 되어 너비는 그대로이고 위아래로 공간이 늘어납니다. 반대로 더 넓적한 태블릿 2048 x 2732 에서 Match 0 이면 UI 높이가 1441 로 줄어 1920 높이로 그린 UI 의 위아래가 잘리고, Match 1 이면 1439 x 1920 으로 옆이 남습니다.

 

4. 어느 화면이든 기준이 다 들어가게 — Expand

 

canvas_scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.Expand;   // 기준보다 작아지지 않음
canvas_scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.Shrink;   // 기준보다 커지지 않음

 

Match 값 하나로는 길쭉한 폰과 넓적한 태블릿을 둘 다 잘리지 않게 맞출 수 없습니다. Screen Match Mode 를 Expand 로 두면 Unity 문서대로 Canvas 크기가 기준보다 작아지지 않게 너비나 높이 중 한쪽을 늘립니다. 계산해 보면 1080 x 2400 폰은 1080 x 2400(너비에 맞춤), 2048 x 2732 태블릿은 1439 x 1920(높이에 맞춤)이 되어, 1080 x 1920 으로 그린 UI 가 어느 쪽에서도 잘리지 않습니다. 늘어난 공간은 버튼들을 화면 가장자리 앵커에 붙여 두면 자연스럽게 벌어집니다. Shrink 는 반대로 기준보다 커지지 않게 한쪽을 잘라 냅니다.

 

5. 안 될 때 — 자주 하는 실수

 

폰에서 UI 가 깨알처럼 작다
UI Scale Mode 가 기본값 Constant Pixel Size 로 남아 있으면 UI 를 화면 픽셀 그대로 그립니다. 에디터 Game 창이 작은 해상도였다면 해상도가 높은 폰에서는 같은 UI 가 훨씬 작게 보입니다. Scale With Screen Size 로 바꿉니다.

 

폰에서 양옆이 잘린다
세로 게임인데 Match 를 1(높이)로 두면 기준보다 길쭉한 폰에서 UI 너비가 줄어듭니다(1080 x 2400 에서 864). 세로 게임에서 길쭉한 폰만 본다면 Match 0, 태블릿까지 함께 맞추려면 Expand 를 씁니다.

 

태블릿에서 위아래가 잘린다
Match 0 으로 맞춘 세로 게임을 태블릿에서 켜면 UI 높이가 기준보다 짧아집니다(2048 x 2732 에서 1441). 화면 위 · 아래 끝에 붙은 버튼이 사라지면 Expand 로 바꾸거나 태블릿 화면도 Game 창에 추가해 확인합니다.

 

배율은 맞는데 노치에 버튼이 가린다
Canvas Scaler 는 크기만 맞출 뿐, 노치나 둥근 모서리는 피하지 않습니다. 그 부분은 Screen.safeArea 로 따로 줄여야 합니다.

 

함께 보기: 노치 · 둥근 모서리를 피하는 법은 유니티 노치 화면 대응 · 안전 영역 (Screen.safeArea) 에 있습니다.

 

Canvas Scaler 동작은 Unity 공식 문서 Canvas Scaler 와 Unity 6000.6.3f1 에디터(Windows)에서 값을 넣어 본 결과를 기준으로 적었습니다.

 

 

반응형

Google Play 및 Google Play 로고는 Google LLC의 상표입니다.