ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [게임엔진활용] 2강 게임 개발 해상도(더블버퍼링, 뷰포트)
    [DSU] 게임엔진활용 2026. 3. 25. 22:30
    728x90

     

    본격적으로 게임을  개발을 시작하기 전, 게임 장르, 주 사용자층 등을 고려해서 가장 먼저 결정해야 하는 것 중 하나가 개발 해상도입니다.

     게임 제작진 입장에서는 개발 기간에 영향을 주고, 게임 플레이어에게는 시스템 요구 사양에 영향을 주기 때문에 아주 중요한 결정입니다. 개발 중간에 바꾸면 큰 손실이 발생할 수 있기 때문에 신중히 선택해야 합니다.

     

    전체영역에서 게임에서 사용되는 영역을 Viewport라고 합니다.

    개발 해상도가 (devWidth, devHeight)이고, 디스플레이영역이 (Screen.width, Screen.height)라고 가정했을때,

    Rect viewport를 설정하는 함수 void setViewport(devWidth, devHeight)를 살펴보시기 바랍니다.

     

     

    해상도가 변경될때(게임 창모드를 지원하고 윈도우 창의 크기를 플레이가 변경할때), void Update() 함수에서 디스플레이 해상도가 변경되었는지 체크하고, 변경되었다면 3가지 상황에 따라 백버퍼로 사용하는 RenderTexture의 크기를 변경합니다.

    1. 백버퍼 크기 고정 (MainCamera 42라인)

     개발이 편합니다. 가로 넓은 화면(울트라 와이드 모니터 32:9)이나 세로가 긴 화면(아이패드 4:3)에서 많은 여백이 발생하기 때문에 사용자 입장에서 불만족스럽습니다.

    2. 모든 사이즈 대응(MainCamera 46라인)

     가로나 세로 사이즈를 고정하고, 다른 사이즈는 유동적으로 변경하되 디스플레이 화면과 동일한 비율을 가집니다. 

     가로 비율이 큰 32:9, 세로 비율이 큰 4:3 모두를 대응하고 괜찮은 UI/UX를 표현하기에는 현실적으로 힘듭니다.

    3. 최대, 최소 비율을 제한 대응(MainCamera 51라인)

     가장 현실적이고 가장 많이 사용되는 방법입니다.

     

    더블버퍼링을 사용하는 이유는 고전적인 이유(깜빡임 방지, 속도향상 등)도 있지만,

    개발자가 백버퍼의 크기를 변경함에 따라 마우스(스마트폰 터치)의 좌표를 구하기 쉬워집니다.(MainCamera 82라인)

     

    MainCamera.cs 파일입니다.

    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
    using UnityEngine;
    using UnityEngine.InputSystem;
     
    public class MainCamera : MonoBehaviour
    {
        int devWidth = 1280, devHeight = 720, depth = 24;
        int _width = 1280, _height = 720;
        public Rect viewport;
        public RenderTexture rt;
     
        void Start()
        {
            rt = new RenderTexture(devWidth, devHeight, depth);
     
            Camera c = GetComponent<Camera>();
            //c.targetTexture = null;// 실제 화면 디스플레이
            c.targetTexture = rt;
        }
        
        void setViewport(int devWidth, int devHeight)
        {
            float r0 = (float)Screen.width / devWidth;
            float r1 = (float)Screen.height / devHeight;
            float r = (r0 < r1 ? r0 : r1);
            int w = (int)(devWidth * r);
            int h = (int)(devHeight * r);
            int x = (Screen.width - w) / 2;
            int y = (Screen.height - h) / 2;
            viewport = new Rect(x, y, w, h);
        }
     
        void Update()
        {
            if (_width != Screen.width || _height != Screen.height)
            {
                _width = Screen.width;
                _height = Screen.height;
     
                bool changeRt = false;
    #if true
                // devWidth, devHeight 종횡비 고정 : 가로/세로 여백 발생
                setViewport(devWidth, devHeight);
    #elif false
                // 모든 사이즈 대응 : (devWidth고정) 비율 유지                                  
                // devWidth : devHeight = Screen.width : Screen.height
                devHeight = (int)((float)devWidth * Screen.height / Screen.width);
                viewport = new Rect(00, Screen.width, Screen.height);
                changeRt = true;
    #else
                // (devWidth고정) 높이 최소, 최대 제한                                                                                                            
                float r = (float)Screen.height / Screen.width;
                float min = 9f / 16, max = 3f / 4;
                if (r < min) r = min;
                else if (r > max) r = max;
                devHeight = (int)(devWidth * r);
                setViewport(devWidth, devHeight);
                changeRt = true;
    #endif
     
                if (changeRt)
                {
                    rt.Release();
                    Destroy(rt);
                    rt = new RenderTexture(devWidth, devHeight, depth);
     
                    Camera c = GetComponent<Camera>();
                    c.targetTexture = rt;
                }
            }
     
            float x = 0, y = 0, z = 0;
            if (Keyboard.current.aKey.isPressed)        x = -1;
            else if (Keyboard.current.dKey.isPressed)    x = 1;
            if (Keyboard.current.wKey.isPressed)        z = 1;
            else if (Keyboard.current.sKey.isPressed)    z = -1;
            if (Keyboard.current.eKey.isPressed)        y = 1;
            else if (Keyboard.current.qKey.isPressed)    y = -1;
            Vector3 v = new Vector3(x, y, z);
            v.Normalize();
            transform.Translate(v * 5f * Time.deltaTime);
     
            if (Mouse.current.leftButton.isPressed)
            {
                Vector2 p = Mouse.current.position.value;
                p.x = devWidth * (p.x - viewport.x) / viewport.width;
                p.y = devHeight * (1f - (p.y - viewport.y) / viewport.height);
                Debug.LogFormat($"({p.x}, {p.y})");
            }
        }
    }
     
    cs

     

    RenderView.cs 파일입니다.

    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
    using UnityEngine;
     
    public class RenderView : MonoBehaviour
    {
        MainCamera mc;
        Material[] material;
        public int materialIndex = 0;
     
        public float mozicSize = 10;
        float playbackTime = 0f;
     
        void Start()
        {
            mc = FindFirstObjectByType<MainCamera>();
            //material = new Material(Shader.Find("Unlit/Texture"));
     
            string[] str = { "std""grey""mozic""snow""rain" };
            material = new Material[str.Length];
            for (int i = 0; i < str.Length; i++)
                material[i] = new Material(Shader.Find("Custom/" + str[i]));
        }
     
        void Update()
        {
            
        }
     
        void OnGUI()
        {
            GL.Clear(truetrue, Color.black);
     
    #if false
            GUI.color = Color.white;
            GUI.DrawTexture(new Rect(00, Screen.width, Screen.height),
                mc.rt, ScaleMode.ScaleToFit);//StretchToFill
    #else
            Material m = material[materialIndex];
            m.SetColor("_BaseColor", Color.white);
            m.SetTexture("_BaseMap", mc.rt);
            m.SetFloat("size", mozicSize);
            m.SetFloat("iTime", playbackTime);
            playbackTime += Time.deltaTime;
            m.SetVector("iMouse"new Vector4(0000));
     
            Graphics.DrawTexture(mc.viewport, mc.rt, m);
    #endif
        }
    }
     
     
     
    cs

     

    수업시간에 적용했던 비내리는 코드가 안되었던 이유는, 매크로 설정이 잘 못 되었습니다.

    a가 수식일 수 있기 때문에, (1 - a)에서 (1 - (a))로 수정하면 됩니다.

    #define mix(x, y, a) ((x)*(1-a) + (y)*(a))

    => #define mix(x, y, a) ((x)*(1-(a)) + (y)*(a))

    https://www.shadertoy.com/view/ltffzl

     

    수업시간에 사용했던 shader파일 첨부합니다.

    scripts.zip
    0.01MB

Designed by Tistory.