티스토리 뷰

소스코드 출처

https://github.com/gportelli/UnityScreenResolutionManager/blob/master/Assets/ScreenResolutionManager/AspectUtility.cs


다양한 화면 비율의 문제와 래터박스


다양한 화면 비율에 맞춰 게임을 각각 개발하는 것은 매우 어렵고도 힘든 일입니다.

하지만 화면 비율을 무시하고 게임을 개발하게 된다면 게임 플레이어들은 개발자가 의도하지 않은 상황에 처할 수 있으며 극단적으로는 게임을 플레이할 수 없게 되기도 합니다. 

이러한 문제를 해결하기 위한 방법중 간단한 방법으로 래터박스가 있습니다. 대표적인 몇 가지 화면 비율로 개발한 뒤 그 외의 화면 비율에는 래터박스를 채워 화면 비율을 맞추는 방법입니다. 


유니티에서 래터박스를 만드는 방법


유니티의 카메라 컴포넌트를 살펴보면 Viewport Rect라는 속성이 있습니다. Viewport Rect는 유니티의 가상 카메라가 비추는 영역을 실제 화면의 어느 위치에 보여줄지 정할 수 있는데 이를 이용하여 래터박스를 생성합니다. 예를 들면 게임의 가로 화면 비율보다 화면의 가로 비율이 높다면 화면의 가로 일정 영역을 피해 게임 화면을 보여줄 수 있습니다.



화면 비율 계산


가로의 경우 

실제 화면과 원하는 화면의 세로 비율을 1로 놓았을 때 가로의 비를 구합니다. 

 (원하는 화면의 가로 비 나누기 실제 화면 가로 비) 를 통해 실제 화면에서 원하는 화면의 비율을 구합니다.

이 비율은 Viewport Rect 의 W에 들어갑니다. 

이제 원하는 화면만큼만 사용할 수 있게 되었지만 화면의 위치가 중앙이 아니라는 문제가 남았습니다. 이 문제는 Viewport Rect의 X 값을 조절하여 해결할 수 있습니다. 

X 값은 원하는 화면의 비율에서 1을 뺀 값 나누기 2 를 한 값 입니다.

원하는 화면의 비율에서 1을 빼게 되면 원하지 않는 화면의 비율입니다. 이 비율의 반 값 만큼 X 축으로 이동하면 정확히 중앙에 위차하게 됩니다. 


예) 실제 화면 비율 3:2 -> 1.5:1 ,원하는 비율 9:16 -> 0.5625:1 ,

     0.5625/1.5 = 0.375 실제 화면에서 원하는 화면이 차지하는 비율   

      

세로의 경우도 위와 약간의 순서만 다르고 비슷합니다.


소스코드 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
using UnityEngine;
 
public class AspectUtility : MonoBehaviour
{
 
    public int x;
    public int y;
  
    static float wantedAspectRatio;
    static Camera cam;
    static Camera backgroundCam;
 
    void Awake()
    {
 
        cam = GetComponent<Camera>();
        if (!cam)
        {
            cam = Camera.main;
        }
        if (!cam)
        {
            Debug.LogError("No camera available");
            return;
        }
        wantedAspectRatio = (float)x / y;
        SetCamera();
    }
 
    public static void SetCamera()
    {
        float currentAspectRatio = (float)Screen.width / Screen.height;
        // If the current aspect ratio is already approximately equal to the desired aspect ratio,
        // use a full-screen Rect (in case it was set to something else previously)
 
      
 
        if ((int)(currentAspectRatio * 100/ 100.0f == (int)(wantedAspectRatio * 100/ 100.0f)
        {
            cam.rect = new Rect(0.0f, 0.0f, 1.0f, 1.0f);
            if (backgroundCam)
            {
                Destroy(backgroundCam.gameObject);
            }
 
            return;
        }
        // Pillarbox
        if (currentAspectRatio > wantedAspectRatio)
        {
            float inset = 1.0f - wantedAspectRatio / currentAspectRatio;
            //Debug.Log(new Rect(inset / 2, 0.0f, 1.0f - inset, 1.0f));
            cam.rect = new Rect(inset / 20.0f, 1.0f - inset, 1.0f);
        }
        // Letterbox
        else
        {
            float inset = 1.0f - currentAspectRatio / wantedAspectRatio;
            cam.rect = new Rect(0.0f, inset / 21.0f, 1.0f - inset);
        }
 
        if (!backgroundCam)
        {
            // Make a new camera behind the normal camera which displays black; otherwise the unused space is undefined
            backgroundCam = new GameObject("BackgroundCam"typeof(Camera)).GetComponent<Camera>();
            backgroundCam.depth = int.MinValue;
            backgroundCam.clearFlags = CameraClearFlags.SolidColor;
            backgroundCam.backgroundColor = Color.black;
            backgroundCam.cullingMask = 0;
        }
    }
 
    public static int screenHeight
    {
        get
        {
            return (int)(Screen.height * cam.rect.height);
        }
    }
 
    public static int screenWidth
    {
        get
        {
            return (int)(Screen.width * cam.rect.width);
        }
    }
 
    public static int xOffset
    {
        get
        {
            return (int)(Screen.width * cam.rect.x);
        }
    }
 
    public static int yOffset
    {
        get
        {
            return (int)(Screen.height * cam.rect.y);
        }
    }
 
    public static Rect screenRect
    {
        get
        {
            return new Rect(cam.rect.x * Screen.width, cam.rect.y * Screen.height, cam.rect.width * Screen.width, cam.rect.height * Screen.height);
        }
    }
 
    public static Vector3 mousePosition
    {
        get
        {
            Vector3 mousePos = Input.mousePosition;
            mousePos.y -= (int)(cam.rect.y * Screen.height);
            mousePos.x -= (int)(cam.rect.x * Screen.width);
            return mousePos;
        }
    }
 
    public static Vector2 guiMousePosition
    {
        get
        {
            Vector2 mousePos = Event.current.mousePosition;
            mousePos.y = Mathf.Clamp(mousePos.y, cam.rect.y * Screen.height, cam.rect.y * Screen.height + cam.rect.height * Screen.height);
            mousePos.x = Mathf.Clamp(mousePos.x, cam.rect.x * Screen.width, cam.rect.x * Screen.width + cam.rect.width * Screen.width);
            return mousePos;
        }
    }
}
 
cs



댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함