UNet 

UNet 지원 중단과 새로운 네트워크 지원 예정

 

이전 포스트에서 유니티에서 지원하는 네트워크 게임 구현 API인 UNet에 관한 내용을 다룬 적이 있으며, 추후에 추가적인 내용을 다룰 예정이었지만 지난 2018년 8월 2일에 유니티 공식 블로그에 게시된 글에 의하면 UNet은 deprecated 되었으며 새로운 네트워크 API를 지원하기 위한 준비를 하고 있다고 이야기하고 있다.

 

 

UNet 튜토리얼 (1) - 개요 글에서 이야기하였듯이 UNet의 구조는 위의 이미지처럼 전송 계층에 가까운 LLAPI(Low Level API)와 게임에 필요한 기능을 제공하는 HLAPI(High Level API)로 나누어져 있다. 이 LLAPI와 HLAPI는 유니티의 계획에 의하면 :

 

1. HLAPI는 2018.4(LTS) 이후 더 이상 유니티와 함께 제공되지 않음. 2018.4(LTS) 출시일 이후 2년 동안 유니티의 장기 지원 정책에 따라 중요한 수정 사항 제공.

2. LLAPI는 2019.4(LTS) 이후 더 이상 유니티와 함께 제공되지 않음. 2019.4(LTS) 출시일  이후 2년 동안 중요한 수정 사항 제공.

3. 유니티 릴레이 서버 및 레거시 매치 메이커 서비스는 2018.4(LTS) 제공 이후 3년 이상 계속 운영하며 다음 서비스로 전환 계획.

 

UNet은 더 이상 사용되지 않지만 ECS(Entity Component System)와 대응이 되는 차세대 네트워크 기능을 곧 제공할 예정이다. 유니티가 제공할 예정인 기능은 다음과 같다.

 

1. 기존 UNet의 HLAPI 및 LLAPI를 대체하며 DOTS와 호환되는 새로운 네트워킹 계층.

2. P2P 지원 릴레이 서버를 대체하는 멀티 플레이 게임 서버 호스팅 서비스.

3. 레거시 매치 메이커 서비스를 대체하는 새로운 매치 메이킹 서비스.

 

유니티의 네트워크 API 제공 계획은 다음과 같습니다.

 

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

Programming

-

로딩 씬(Loading Scene) 구현하기

(씬 교체 방식)

작성 기준 버전 :: 2019.2.9f1

 

게임의 장르와 배경들의 종류는 많고도 많지만 그 어떤 종류의 게임이던간에 아주 가벼운 게임이 아닌 이상 반드시 등장하는 장면이 있다. 그 장면은 바로 로딩 씬이다. 다들 로딩 씬이 등장하면 언제쯤 지나가려나 하며 로딩 바에 마우스를 올리고 정말로 바가 채워지고 있는지 확인해본 경험이 있을 것이다. 게임을 처음으로 접했던 어린 시절에는 이런 로딩 씬이 왜 필요한지도 몰랐고 그냥 재미있는 게임을 할 시간을 잡아먹는 나쁜 녀석이라는 생각만 가득했다.

 

 

하지만 게임 개발을 시작한 이후로 이 로딩 씬만큼 중요한 씬이 또 없다는 것을 깨달을 수 있었다. 로딩 씬의 역할은 단지 시간만 잡아먹는 것이 아니라 게임의 씬이 전환될 때 다음 씬에서 사용될 리소스들을 물리적인 저장소에서 읽어와서 메모리에 올리는 등의 게임을 하기 위한 준비를 하는 작업이다.

 

만약에 게임에 로딩 장면이 존재하지 않는다면 어떻게 될까? 아마 플레이어는 다음 씬으로 넘어가는 동안 가만히 게임이 멈춘 화면을 보고 있거나 까만 화면을 보고 있어야 한다. 그런 일이 발생한다면 로딩이 얼마나 진행되었는지 알 수 없고 이 게임이 로딩 중인지 정지한 것인지 구분할 수도 없어서 너무 답답할 것이다. 그렇기 때문에 씬이 전환될 때에는 로딩 씬을 만들어서 플레이어에게 로딩이 얼마나 진행되었는지 알려주면서 플레이어가 지루하지 않게 게임 팁이나 게임 스토리등을 보여주는 것이다.

 

 

개념

 

 

로딩 씬을 구현하는 방법에는 여러가지 방법이 있을 수 있지만 이번 섹션에서는 로딩씬을 불러들인 다음에 호출하는 씬을 비동기로 호출하는 방법을 사용한다.

 

 

구현하기

 

앞에서는 로딩 씬의 필요성에 대해서 이야기했다면 이제는 실제로 로딩씬을 유니티에서 구현하는 방법을 알아보자. 

 

 

코드 작성하기

 

로딩 씬을 불러오고 로딩작업을 진행하는 LoadingSceneManager 클래스를 생성하고 다음의 코드를 작성한다.

 

using System.Collections;

using UnityEngine;

using UnityEngine.UI;

using UnityEngine.SceneManagement;

 

public class LoadingSceneManager : MonoBehaviour

{

    public static string nextScene;

 

    [SerializeField]

    Image progressBar;

 

    private void Start()

    {

        StartCoroutine(LoadScene());

    }

 

    public static void LoadScene(string sceneName)

    {

        nextScene = sceneName;

        SceneManager.LoadScene("LoadingScene");

    }

 

    IEnumerator LoadScene()

    {

        yield return null;

 

        AsyncOperation op = SceneManager.LoadSceneAsync(nextScene);

        op.allowSceneActivation = false;

 

        float timer = 0.0f;

        while (!op.isDone)

        {

            yield return null;

 

            timer += Time.deltaTime;
            if (op.progress < 0.9f)            {                progressBar.fillAmount = Mathf.Lerp(progressBar.fillAmount, op.progress, timer);                if (progressBar.fillAmount >= op.progress)                {                    timer = 0f;                }            }            else            {                progressBar.fillAmount = Mathf.Lerp(progressBar.fillAmount, 1f, timer);
                if (progressBar.fillAmount == 1.0f)                {                    op.allowSceneActivation = true;                    yield break;                }            }

        }

    }

}

 

처음 씬을 불러오는 방법을 배우는 유니티 게임 제작자의 경우에는 대부분 SceneManager.LoadScene()을 사용하지만, 로딩 씬을 만들기 위해서는 SceneManager.LoadSceneAsync()를 사용해야 한다. LoadScene()의 경우에는 동기 방식으로 불러올 씬을 한꺼번에 불러오고 다른 모든 것이 불러오는 동안 기다리는 방식으로 불러오는 도중에는 다른 작업을 할 수 없게 되지만, LoadSceneAsync()의 방식은 비동기 방식으로 씬을 불러오는 도중에도 다른 작업이 가능한 방식이다. 로딩의 진행 정도는 LoadSceneAsync() 함수가 AsyncOperation 클래스 형식으로 반환한다.

 

위의 코드에서는 AsyncOperation 클래스의 객체인 op를 통해서 allowSceneActivation을 false로 설정하는데, 이 옵션은 씬을 비동기로 불러들일 때, 씬의 로딩이 끝나면 자동으로 불러온 씬으로 이동할 것인가를 의미한다. 로딩씬에서 이 값을 false로 설정하는 것은 로딩이 완료되었을 때, 자동으로 다음 씬으로 넘어가지 않고 기다린다는 의미이다. allowSceneActivation이 false일 때는 씬이 로드될 때, 95%까지만 로딩되고 더 이상 불러들이지 않는다. 다시 allowSceneActivation을 true로 변경하면 그 때 마무리 로딩을 완료하고 씬을 불러오게 된다.

 

코드 작성을 완료했다면 코드를 저장하고 에디터로 넘어간다.

 

 

씬 구성

 

그 다음에는 로딩 씬을 구성한다.

 

 

로딩 씬을 위의 이미지와 같이 구성하자. 초록색 막대는 다음 씬이 얼마나 로딩되었는지 알려주는 진행막대(Progress Bar)이다. 배경은 아무런 이미지 없이 카메라가 찍고 있는 텅 빈 씬을 보여주고 있지만, 실제의 게임에서는 그 게임의 일러스트나 게임 장면 등을 넣을 수 있고, 덤으로 게임 팁(Tip)이나 게임의 배경이 되는 스토리를 보여줄 수도 있다. 그렇게 하면 엘리베이터에 거울을 달아두면 엘리베이터 탑승자들이 거울을 보느라 엘리베이터가 조금 느려도 속도에 신경을 덜 쓰게 되는 것처럼 로딩을 기다리는 유저들 또한 배경 이미지나 팁, 배경 스토리를 읽으면서 로딩의 지루함을 덜어낼 수 있게 되는 것이다.

 

 

진행막대로 사용되는 이미지의 경우에는 스케일을 조정해서 로딩의 진행도를 표시할 수도 있지만, 만약 위의 이미지처럼 단색의 이미지를 사용하는 것이 아닌 경우에는 이미지가 찌그러져 출력될 것이기 때문에 가능하다면 Image Type을 Filled를 사용할 것을 권장한다. Fill Method를 Horizontal로 설정하면 수평으로 채워지고 Fill Origin를 Left로 하면 이미지가 왼쪽부터 채워진다. 그리고 Fill Amount를 이용해서 로딩이 얼마나 진행되었는지 표시할 수 있다.

 

 

그 다음에는 로딩 씬에 게임 오브젝트를 하나 생성하고 Loading Scene Manager로 이름을 지은다음에 LoadingSceneManager 스크립트를 추가한다. 그리고 하이어라키 뷰에서 Progress Bar 이미지를 Progress Bar 프로퍼티에 끌어다 넣는다.

 

 

 

 

 

테스트 세팅하기

 

 

  

위의 과정을 모두 마쳤다면 두 개의 씬을 새로 만들고 씬이 전환되었음을 확인하기 위해서 각 씬에 다른 모양의 게임 오브젝트를 추가해준다.

 

using UnityEngine;
public class SceneLoadTester : MonoBehaviour
{
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            LoadingSceneManager.LoadScene("Scene2");
        }
    }
}

 

 

그리고 LoadingSceneManager의 기능을 테스트하기 위해 스페이스바를 누르면 LoadingSceneManager를 호출하는 코드를 작성하고 Scene1에 게임 오브젝트를 생성해서 스크립트를 넣어준다.

 

 

상단의 [File > Build Settings] 메뉴를 선택하거나 [Ctrl + Shift + B]를 눌러서 빌드 세팅 창을 연 다음 만든 씬들을 빌드될 씬 목록에 넣어준다.

 

 

테스트

 

 

그런 후에 첫 번째 씬에서 플레이 버튼을 눌러 게임을 실행하고 스페이스바를 누르면 첫 번째 씬에서 로딩 씬으로 넘어간 후에 아래쪽 로딩 바가 자연스럽게 차오른뒤에 두 번째 씬으로 넘어가는 것을 확인할 수 있다.

 

위의 예시에서는 씬의 로딩 진행도 만을 이용해서 진행 정도를 체크했지만, 유니티에서는 다음 씬에서 사용될 애셋 번들을 불러오는 것 또한 로딩에 포함될 수 있고, 만약 네트워크 게임을 제작한다면 네트워크 동기화 정도도 포함될 수 있다.

 

 

여담으로 일부 게임 제작자의 경우에는 로딩 시간이 너무 짧아서 로딩 시간동안 보여주고자 하는 팁이나 스토리 등이 너무 빠르게 스쳐지나간다고 생각하는 경우에는 일부러 로딩 속도를 늦추거나 페이크 로딩 시간을 넣어서 로딩 시간을 일부러 길게 만드는 경우도 있다.

 


 

이 글은 이전에 작성된 로딩 씬 구현하기 글의 새로운 버전으로 좀 더 따라하기 쉽고 이전에는 생략되었던 중간과정이 추가되었으며 코드가 약간 수정되었다.

 

로딩 씬을 불러온 뒤에 다음 씬을 로딩하는 이 방법 이외에도 로딩 UI 만을 무대 커튼처럼 전면에 깔아두고 다음 씬을 로딩하는 방법도 있다.

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

Programming 

static 키워드를 파일 경로와 URL 표현에 사용하기

 

작성 기준 버전 :: 2019.1.4f1

 

유니티 엔진으로 게임을 만들 때, 스크립트 작업은 대부분 C# 스크립트로 이루어진다. 한 때 유니티 초기에는 자바 스크립트(Java Script)나 부(Boo) 같은 언어도 지원을 했었지만, 최신 버전의 유니티 엔진은 C#만을 지원한다. 그렇기 때문에 C#에서 지원하는 기본적인 문법을 충분히 배우고 활용하는 법을 공부해야한다.

 

이번에는 유니티에서 static 키워드를 활용하여 파일 경로와 URL 표현에 사용하는 방법에 대해서 알아볼 것이다. C#의 static 키워드에 대한 기본적인 내용은 링크를 통해서 확인할 수 있다.

 

 

파일 경로와 URL 표현

 

public class FileLoader : MonoBehaviour

{

    void Start()

    {

        LoadSomeFile(Application.dataPath + "/Save/" + "fileName.txt");

    }

 

    public void LoadSomeFile(string filePath)

    {

        // 파일을 로드하는 작업...

        Debug.Log(filePath);

    }

}

 

public class UrlDownloader : MonoBehaviour
{
    void Start()
    {
        StartCoroutine(DownloadSomeFile("https://SomeUrl/GameData/" + "fileName.png"));
    }

    public IEnumerator DownloadSomeFile(string filePath)
    {
        UnityWebRequest request = new UnityWebRequest(filePath);
        yield return request.SendWebRequest();
        var data = request.downloadHandler.data;
        // URL에서 받아온 데이터로 작업...
    }
}

 

모든 프로그래밍도 마찬가지겠지만 게임 프로그래밍 역시 게임 저장/불러오기나 네트워크 게임이라면 게임 데이터 받아오기 등의 파일 경로와 URL을 다루어야 할 일이 발생한다. 하지만 위의 코드처럼 경로와 URL을 코드에 하드코딩을 해버리면 나중에 파일 경로나 URL이 바뀌는 경우가 발생했을 때, 변경된 경로를 모두 찾아서 바꾸어야 하는 번거로움이 발생한다. 그리고 그 중에 하나라도 놓치는 경우가 발생한다면, 그것은 곧바로 게임이 제대로 동작하지 않은 버그로 직행한다.

 

이러한 문제를 막기 위해서 게임에서 사용되는 모든 경로는 하나의 클래스로 묶어두고 그 클래스에서 경로를 가져오도록 만드는게 좋다. 다만 클래스에서 경로를 불러올 때는 객체를 생성하지 않고 곧바로 불러올 수 있게 하는 것이 좋다. 바로 그런 점에서 static 키워드를 적용하면 매우 좋다.

 

public static class GamePath

{

    public static string savePath = Application.dataPath + "/Save/";

}

 

public static class GameURL

{

    public static string GameDataURL = "https://SomeUrl/GameData/";
}

}

 

위 예시 코드처럼 정적 클래스와 정적 변수를 만들어서 경로를 표현한다.

 

public class FileLoader : MonoBehaviour

{

    void Start()

    {

        LoadSomeFile(GamePath.savePath + "fileName.txt");

    }

 

    public void LoadSomeFile(string filePath)

    {

        // 파일을 로드하는 작업...

        Debug.Log(filePath);

    }

}

 

public class UrlDownloader : MonoBehaviour
{
    void Start()
    {
        StartCoroutine(DownloadSomeFile(GameURL.GameDataURL + "fileName.png"));
    }

    public IEnumerator DownloadSomeFile(string filePath)
    {
        UnityWebRequest request = new UnityWebRequest(filePath);
        yield return request.SendWebRequest();
        var data = request.downloadHandler.data;
        // URL에서 받아온 데이터로 작업...
    }
}

 

경로를 사용할 때는 바로 위 예시 코드처럼 사용하면 된다. 그러면 만약에 경로가 변경되었을 때, 모든 코드에서 수정된 경로를 일일이 찾아서 바꿀 필요없이 GamePath 클래스와 GameURL 클래스의 경로만 수정하면 모든 코드에 적용이 된다.

 

이런 식으로 코드 내에 상수로 들어가지만, 추후에 변경이 발생할 수 있는 부분을 정적 클래스로 묶어서 관리하면 좋다.

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

UI 비법서 (3) 

군무는 칼같은 각이 생명! Layout Group

 

작성 기준 버전 :: 2019.1 - 2019.4

 

[이 포스트의 내용을 유튜브 영상으로도 시청하실 수 있습니다]

 

UI를 구성할 때, 아무 위치에나 배치되는 경우는 거의 없다. 대부분의 UI는 미리 설계된 레이아웃에 따라서 배치된다. 대부분의 UI 혹은 메인 메뉴 같은 UI는 고정된 위치에 배치되고 그것이 추가/삭제되는 등의 변동이 실시간으로 일어나지는 않는다.

 

하지만 카드 수집형 게임(TCG) 같은 게임에서 자신이 보유한 카드를 보여주는 형식의 카드 인벤토리 UI를 예시로 들자면, 보유하고 있는 카드의 숫자에 따라서 보여주는 카드 UI의 숫자가 달라질 것이다. 카드를 새로 얻으면 가지고 있는 카드 UI들 끝에 새로 얻은 카드 UI가 바로 앞 카드 UI와 일정한 간격을 두고 생성되어야 한다. 그리고 카드를 이름이나 카드의 성능 등 사용자가 원하는 기준에 따라서 정렬하고자 하면 현재 보여주고 있는 카드 UI들을 기준에 맞춰 재정렬한 뒤에 보여주는 줘야한다. 이렇듯이 고정된 UI와 달리 실시간으로 UI를 추가/삭제하고 순서를 변동하는 작업을 필요로 한다.

 

실시간 카드 추가/제거 및 정렬을 위한 기능을 수동으로 직접 구현 한다면 상당히 많은 작업을 해야하고 시간 역시 많이 소모될 것이다. 이런 UI의 위치를 자동으로 맞춰주는 기능을 구현하는데 소모되는 시간을 줄여주기 위해서 유니티 엔진에서 제공하는 기능이 바로 레이아웃 그룹(Layout Group)이다.

 

『성능 최적화를 위해서는 사실 해당 기능을 직접 구현하는 것이 좋다. 레이아웃 그룹의 성능 문제는 본 포스트의 제일 하단에 언급되어 있다.』

 

 

 

 

 

레이아웃 그룹(Layout Group)

 

레이아웃 그룹은 UI를 자동으로 정렬하는 컴포넌트이다. 이 컴포넌트의 종류로는 수직 레이아웃 그룹(Vertical Layout Group), 수평 레이아웃 그룹(Horizontal Layout Group), 그리드 레이아웃 그룹(Grid Layout Group)이 있다.

 

  

 

수직 레이아웃 그룹(Vertical Layout Group)

 

수직 레이아웃 그룹은 주로 리스트 형식으로 UI들을 보여줄 때 사용된다. 

 

  

수직 레이아웃 그룹 컴포넌트를 사용하면 위의 이미지와 같이 하이어라키에서 자식 게임오브젝트로 들어온 UI들이 수직으로 자동 정렬된다. 이 때 캔버스에 표시되는 순서는 하이어라키에 표시된 순서와 같다.

 

 

하이어라키 창에서 Vertical Layout Group 게임오브젝트 아래에 있는 Image 게임오브젝트를 복사하면 복사/생성된 이미지 UI 게임오브젝트들이 자동으로 정렬되는 것을 확인할 수 있다.

 

 

수직 레이아웃 그룹의 프로퍼티는 위의 이미지에서 볼 수 있듯이 패딩(Padding), 스페이싱(Spacing, 간격), 차일드 얼라인먼트(Child Alignment, 자식 정렬), 컨트롤 차일드 사이즈(Control Child Size, 자식 크기 조절), 유즈 차일드 스케일(Use Child Scale, 자식 크기 사용), 차일드 포스 익스팬드(Child Force Expand, 자식 강제 확장)가 있다.

 

 

패딩(Padding)

 

 

콘텐츠 사이즈 피터를 사용하면 패딩 프로퍼티에 대해서 더 잘 이해할 수 있다. 좌 우 위 아래의 패딩 값을 조절을 하면 Vertical Layout Group 게임 오브젝트의 넓이와 높이가 늘어나며 Vertical Layout Group 게임오브젝트 아래에 있는 자식 게임오브젝트들을 감싸는 공간이 생기는 것을 확인할 수 있다.

 

 

스페이싱(Spacing)

 

 

스페이싱 프로퍼티는 자식 게임오브젝트 사이의 간격을 의미한다.

 

 

차일드 얼라인먼트(Child Alignment)

 

 

차일드 얼라인먼트는 수직 레이아웃 그룹 컴포넌트가 부착된 게임오브젝트 내부에서 어느 위치를 중심으로 콘텐츠들을 정렬할 것인가를 의미한다. 위의 이미지에서도 볼 수 있듯이 회색 사각형은 Vertical Layout Group 게임오브젝트의 영역인데, 차일드 얼라인먼트의 값에 따라서 차일드 게임 오브젝트들의 위치가 변하는 것을 확인할 수 있다.

 

 

 

 

 

컨트롤 차일드 사이즈(Control Child Size)

 

컨트롤 차일드 사이즈 프로퍼티는 단독으로는 큰 의미를 가지지 않는다.

 

 

하지만 위의 움직이는 이미지에서 볼 수 있듯이 컨트롤 차일드 사이즈 프로퍼티만 단독으로 사용할 때는 아무런 반응이 없었지만 차일드 포스 익스팬드 프로퍼티와 함께 사용하면 레이아웃 그룹 컴포넌트가 부착된 게임오브젝트의 사이즈만큼 하위 UI들의 크기가 커지는 것을 확인할 수 있다.

 

 

혹은 레이아웃 그룹의 하위에 속하는 요소들이 레이아웃 엘리먼트(Layout Element, 레이아웃 요소) 컴포넌트를 가지는 경우에도 의미를 가지고 동작한다.

 

 

위의 움직이는 이미지에서 레이아웃 엘리먼트와 레이아웃 그룹의 상호작용을 볼 수 있는데 컨트롤 차일드 사이즈를 넓이 옵션만 활성화 한 뒤, 레이아웃 엘리먼트의 Preferred Width와 Preferred Height를 둘 다 활성화하고 변경했지만, 넓이만 변경되는 것을 확인할 수 있다.

 

 

차일드 스케일 사용(Use Child Scale)

 

차일드 스케일 사용 프로퍼티는 하위의 UI들을 정렬할 때, 차일드 게임 오브젝트의 스케일 값을 사용할 것인가를 정한다.

 

 

위의 예시 이미지를 보면 차일드 스케일 사용 프로퍼티를 켜기 전에는 자식 오브젝트의 크기가 바뀌어도 정렬 위치 자체는 그대로인 반면에 차일드 스케일 사용 프로퍼티를 켜면 자식 오브젝트의 크기에 따라서 정렬 위치가 바뀌는 것을 확인할 수 있다.

 

 

차일드 강제 확장(Child Force Expand)

 

차일드 강제 확장 프로퍼티는 컨트롤 차일드 사이즈 프로퍼티 설명에서 봤듯이 자식 오브젝트의 크기를 레이아웃 그룹 컴포넌트가 부착된 게임 오브젝트의 크기에 맞춰서 강제로 늘어나게 만드는 옵션이다.

 

 

 

 

 

수평 레이아웃 그룹(Horizontal Layout Group)

 

 

수평 레이아웃 그룹은 자기 밑에 속한 자식 UI 오브젝트들을 수평으로 정렬한다. 그리고 수평 레이아웃 그룹 컴포넌트가 가진 프로퍼티는 수직 레이아웃 프로퍼티가 가진 것과 동일하며 그 기능 역시 같다.

 

 

그리드 레이아웃 그룹(Grid Layout Group)

 

 

그리드 레이아웃 그룹은 하이어라키 뷰에서 자식으로 속한 UI 게임 오브젝트들을 바둑판식으로 정렬한다. 그리드 레이아웃이 가지는 프로퍼티는 패딩(Padding), 셀 크기(Cell Size), 시작 코너(Start Corner), 시작 축(Start Axis), 차일드 얼라인먼트(Child Alignment), 제약(Constraint, 콘스트래인트)가 있다.

 

우선 패딩과 차일드 얼라인먼트 프로퍼티는 위의 수직/수평 레이아웃 그룹이 가지는 프로퍼티와 같은 기능이다.

 

 

셀 크기(Cell Size)

 

 

셀 크기 프로퍼티는 그리드 레이아웃 그룹에 속하는 UI들의 크기를 지정해주는데 사용된다.

 

 

시작 코너(Start Corner)

 

 

시작 코너 프로퍼티는 자식 UI를 정렬될 때 시작 위치를 정하는 기능이다. 왼쪽 위(Upper Left), 오른쪽 위(Upper Right), 왼쪽 아래(Lower Left), 오른쪽 아래(Lower Right)로 설정할 수 있으며 위치를 제대로 잡기 위해서는 차일드 얼라인먼트와 함께 사용하는 것이 좋다.

 

 

시작 축(Start Axis)

 

 

시작 축은 자식 UI를 정렬할 때, 수직으로 먼저 정렬할 것인지, 수평으로 먼저 정렬할 것인지를 정하는 옵션이다. 기본 설정은 수평이며, 수직으로 변경하면 새로 생겨난 UI는 아래쪽 행으로 추가 되며 레이아웃 그룹 컴포넌트가 부착된 게임 오브젝트의 가장 아래쪽 행까지 채우고 나면 그 다음 옆에 열을 추가하는 방식으로 정렬된다.

 

 

제약(Constraint)

 

 

그리드 레이아웃의 제약 프로퍼티는 기본적으로 플랙시블, 즉 유연성있는 상태로 설정되어 있는데 이것은 위의 이미지와 같이 2x2, 3x3, 4x4 형태로 정사각형의 정렬된다. 그리고 해당 NxN의 레이아웃이 수용할 수 있는 UI의 갯수를 넘어가면 N+1xN+1 행렬로 자동으로 확장된다.

 

 

이렇게 자동으로 행렬 모두가 확장되는 방식은 일반적인 UI에서는 쉽게 사용되지 않는다. 대부분의 경우에는 행이나 열 중 하나는 갯수를 고정시켜두고 추가하는 방식을 사용한다.

 

제약 프로퍼티를 Fixed Column Count로 선택하면 첫 번째 이미지처럼 열의 갯수를 고정시킨 채로 행을 추가해 나갈 수 있고, Fixed Row Count로 설정하면 두 번째 이미지처럼 행의 갯수를 고정시킨 채로 열을 추가해 나갈 수 있다.

 

 

정리

 

레이아웃 그룹은 이렇게 동적으로 추가/제거되는 UI를 정렬하기에 좋은 컴포넌트이다. 하지만 동적이지 않은 UI더라도 일정한 간격을 두고 배치되는 UI라면 번거롭게 위치를 일일이 계산해서 배치하는 것보다 빠르게 작업할 수 있게 도와준다.

 

또한, 레이아웃 그룹 컴포넌트는 콘텐츠 사이즈 피터스크롤 뷰와 함께 사용하면 좋다.

 


 

추가로...

 

조금 늦게 알게된 사실이지만 최적화를 위해서는 레이아웃 그룹의 사용을 자제해야 된다는 유니티 최적화 팁이 있다. 해당 내용은 레이아웃 그룹 내에서 하나 이상의 자식 요소가 변경되면 레이아웃 시스템이 변경된 것으로 인식해서 레이아웃 그룹 내의 각 UI 요소 1개마다 최소 1회의 GetComponents 호출을 수행한다고 한다. 단순 GetComponents 호출이 아닌 연속적인 GetComponents 호출이 성능에 얼마나 악영향을 끼치는지는 대부분의 개발자들 역시 인지하고 있을 것이다.

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

다른 cs 파일의 클래스 호출과 static 로직에 대하여

 

로딩 씬(Loading Scene) 구현하기 글에 달아주신 ㅇㅇ님의 질문 댓글에 대한 답변입니다. 질문의 내용은 아래와 같습니다.

 

 

로딩 씬 구현하기 글을 보면 LoadingSceneManager 클래스는 아래와 같이 구현되어 있습니다.

 

LoadingSceneManager.cs

using System.Collections;

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class LoadingSceneManager : MonoBehaviour
{
    public static string nextScene;

    [SerializeField]
    Image progressBar;

    private void Start()
    {
        StartCoroutine(LoadScene());
    }

    public static void LoadScene(string sceneName)
    {
        nextScene = sceneName;
        SceneManager.LoadScene("LoadingScene");
    }

    IEnumerator LoadScene()
    {
        yield return null;

        AsyncOperation op = SceneManager.LoadSceneAsync(nextScene);
        op.allowSceneActivation = false;

        float timer = 0.0f;
        while (!op.isDone)
        {
            yield return null;

            timer += Time.deltaTime;

            if (op.progress >= 0.9f)
            {
                progressBar.fillAmount = Mathf.Lerp(progressBar.fillAmount, 1f, timer);

                if (progressBar.fillAmount == 1.0f)
                    op.allowSceneActivation = true;
            }
            else
            {
                progressBar.fillAmount = Mathf.Lerp(progressBar.fillAmount, op.progress, timer);
                if (progressBar.fillAmount >= op.progress)
                {
                    timer = 0f;
                }
            }
        }
    }
}

 

첫 번째 질문에서의 LoadingSceneManager.LoadScene("Scene2");를 호출하는 클래스는 아래와 같이 구현되어 있었습니다.

 

TestCode.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestCode : MonoBehaviour
{

    // Use this for initialization
    void Start ()
    {
        LoadingSceneManager.LoadScene("Scene2");
    }
}

 

 

다른 cs 파일의 클래스 호출

 

우선 첫 번째로 LoadingSceneManager.cs 파일에 구현된 LoadingSceneManager 클래스를 어떻게 TestCode.cs 파일로 다른 선언 없이 바로 호출할 수 있었느냐라는 의도로 질문하신 것 같습니다. 어떻게 다른 cs 파일에 정의된 클래스를 바로 호출할 수 있었냐는 질문을 하신 걸로 보아, ㅇㅇ님께서는 아마 유니티를 배우시기 이전에 C/C++과 같이 다른 소스 파일의 코드를 불러오기 위해서는 별도의 include 등의 전처리가 필요한 언어를 배우신게 아닌가 싶습니다.

 

그런 C/C++에서는 다른 소스 파일의 클래스를 불러와서 사용하기 위해서는 아래의 예시와 같이 전처리기에서 사용하고자 하는 클래스가 담긴 헤더를 포함시켜주어야 합니다.

 

LoadingSceneManager.h

#include <string>

 

#pragma once

 

class LoadingSceneManager

{

public:

static void LoadingScene(std::string sceneName)

{

// 씬 로딩 처리 ...

}

};

 

TestCode.h

#include "LoadingSceneManager.h" // C/C++에서는 이렇게 호출하고자 하는 클래스가 담긴 헤더를 호출해주어야 한다.

 

#pragma once

 

class TestCode

{

public:

void Start()

{

LoadingSceneManager::LoadingScene("Scene2");

}

};

 

하지만 C#에서는 헤더 파일이 따로 존재하지 않고 cs파일만 존재하며, 같은 프로젝트 안에 있는 cs파일이라면, 다른 cs 파일 안에 정의된 클래스를 가져와서 사용할 수 있다는 뜻입니다.

 

 

정적 함수(Static Function)

 

두 번째 질문으로는 static으로 선언된 LoadScene() 함수가 LoadSceneManager 오브젝트가 없는 다른 씬에서 어떻게 호출될 수 있는가 입니다.

 

 

 

 

로딩 씬 구현하기 글에서 구현한 방식을 그림으로 표현하면 위의 이미지와 같습니다. 씬은 Scene1, LoadingScene, Scene2가 존재하고 TestCode 클래스의 인스턴스는 Scene1에서만 존재하고 LoadSceneManager 클래스의 인스턴스는 LoadingScene에서만 존재합니다. 그런데 Scene1에서 LoadScene에만 존재하는 LoadSceneManager의 함수를 호출할 수 있느냐가 메인인데, 이 부분은 정적 함수(static function)에 대한 기본적인 이해가 필요합니다.

 

    public static void LoadScene(string sceneName)
    {
        nextScene = sceneName;
        SceneManager.LoadScene("LoadingScene");

    }

 

코드를 다시 보면 아시겠지만, LoadSceneManager 클래스의 LoadScene(string) 함수는 static으로 선언되어 있습니다. 이렇게 static으로 선언된 함수를 정적 함수라고 하며, 이러한 정적 함수는 클래스의 인스턴스가 생성되지 않았더라도 컴파일 시점에 전역에 할당되어 있습니다. 그렇기 때문에 TestCode.cs에서와 같이 해당 클래스의 이름을 통해서 곧바로 접근이 가능해집니다.

 

다시 한번 풀어서 이야기하자면, TestCode.cs가 실행되는 때에는 LoadSceneManager 클래스의 인스턴스가 생성되어 있지 않지만, 전역에 할당되어 있는 LoadSceneManager 클래스의 전역 함수에 접근하여 정적 변수(이것 역시 전역에 할당되어 모든 LoadSceneManager 클래스의 인스턴스들이 공유한다.)인 nextSceneName에 다음으로 넘어갈 씬 이름을 전달하고 LoadingScene을 불러옵니다. 

 

LoadingScene이 전부 불러와지면 LoadSceneManager 클래스의 인스턴스가 생성되고, LoadSceneManager의 Start() 함수가 실행되면서 LoadScene 코루틴을 호출하는 순서로 동작합니다.

 


 

PS.

로딩 씬 구현하기 글은 2017년에 작성한 오래 전 글이라 내용이 많이 불친절한 편입니다. 기본적인 내용은 많이 바뀌지 않겠지만, 유니티 버전이 많이 바뀌면서 좀 더 알아보기 쉽게 새로 작성할 계획은 오래 전부터 세워둔 상태였는데, 이래저래 시간을 보내면서 아직까지 작성하지 못했습니다. 최대한 이른 시일 내로 새로운 버전으로 작성해보도록 하겠습니다.

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

UI 비법서 (2)

-

하나의 그룹 Canvas Group

 

작성 기준 버전 :: 2019.1.4f1

 

UI를 만들 때, 한 UI 창에 텍스트, 이미지, 버튼, 체크박스 등 수많은 구성요소들이 들어간다. 하나의 UI 창을 컨트롤 한다는 것은 이 UI 창 아래에 들어가는 구성요소들을 한꺼번에 통제해야 한다는 것을 의미한다.

 

 

위 이미지와 같은 UI 창이 있고, 이 창이 열리거나 닫힐 때, 투명도를 사용해서 투명해지는 연출을 사용한다고 가정했을 때, 일반적으로는 UI에서 이미지나 텍스트를 표현하는 모든 컴포넌트의 색상 값 중에 알파 값을 일일이 수정해주어야 한다. 이 작업은 UI 창의 구성이 복잡해질 수록 더욱 귀찮고 힘든 작업이 되며 이런 작업을 해놓은 다음에 UI의 구성이 바뀌기라도 하면 더 큰 문제가 발생한다.

 

 

이러한 문제를 해결해 주는 것이 바로 캔버스 그룹(Canvas Group) 컴포넌트다.

 

 

캔버스 그룹(Canvas Group)

 

 

캔버스 그룹 컴포넌트를 사용하기 위해서는 하나의 그룹으로 하고자하는 UI의 최상단 게임 오브젝트를 선택하고 인스펙터 창의 [Add Component] 버튼을 눌러 Canvas Group를 검색하여 추가하면 된다.

 

 

그렇게 캔버스 그룹을 추가하면 위의 이미지와 같은 캔버스 그룹 컴포넌트가 생성된다. 캔버스 그룹 컴포넌트는 많은 기능을 가지고 있지 않고 간단한 기능 몇 가지만 제공한다. 하지만 이것들만으로도 원래라면 손이 많이 가는 작업들을 크게 줄여준다.

 

 

알파 값 조정

 

캔버스 그룹의 첫 번째 주요 기능은 하위 그룹의 UI들의 알파 값을 한꺼번에 조절할 수 있게 해주는 것이다.

 

 

위 이미지를 보면 아까 전의 예시에서는 UI 구성요소들의 알파 값을 일일이 수정해주던 것에서 캔버스 그룹의 알파 값만 수정해주면 되는 것으로 바뀌었다.

 

 

 

캔버스 그룹으로 알파 값을 조정하는 방법의 최대의 장점은 그룹 아래의 UI 구조가 어떤 식으로 바뀌어도 그 밑에 있는 모든 UI에 자동으로 적용이 된다는 것이다.

 

 

상호작용가능함(Interactable)

 

두 번째 옵션은 상호작용에 관한 것이다. UI에는 상호작용이 가능한 종류의 컴포넌트인 버튼(Button), 토글(Toggle), 슬라이더(Slider) 등이 있는데, 이런 컴포넌트들은 기본적으로 상호작용에 대한 옵션을 가진다. 그 옵션이 바로 상호작용가능함(Interactable)이다.

 

 

이것들 역시 캔버스 그룹 없이 한꺼번에 제어하고자 한다면, 각각의 컴포넌트에 일일이 접근하여서 값을 수정해주어야 한다.

 

 

하지만 캔버스 그룹의 상호작용가능함 옵션을 사용하면 하위에 있는 모든 상호작용가능한 컴포넌트들이 제어되는 것을 확인할 수 있다.

 

 

 

 

레이캐스트 블록(Blocks Raycasts)

 

레이캐스트는 일종의 광선을 의미하는데, 일반적으로 마우스를 클릭한 화면 위치에서 게임 속 공간에 레이저를 쏴서 클릭한 위치를 찾아내는 역할을 하는 데에 주로 사용되며, 이를 응용해서 주로 RPG 게임에서 클릭한 위치로 캐릭터를 이동시키는데 사용하게 된다.

 

UI에서 레이캐스트를 블록, 즉 레이캐스트를 막는다는 것의 의미는 첫 번째로, UI가 입력을 받아들인다는 뜻이다.

 

 

위의 gif 이미지를 보면, 입력을 받아들인다는 뜻을 이해할 수 있다. Blocks Raycasts를 켰을 때는 레이캐스트가 UI에 막혀서 입력을 받을 수 있게 되고, 반대로 꺼졌을 때는 입력을 받아들이지 못한다.

 

그렇다면 여기서 위의 상호작용가능함 옵션과 관련해서 어떤 차이가 있느냐는 질문을 할 수가 있다. 둘 다 똑같이 입력을 못받게 되는데 다른게 없지 않은가? 물론 이 두 개의 옵션은 완전히 다른 옵션이다.

 

상호작용가능함 옵션의 예시 gif에서는 상호작용가능함 옵션이 꺼지면서 버튼, 토글, 슬라이드가 회색으로 변하고 버튼, 토글, 슬라이드와의 상호작용이 불가능하게 바뀌었지만 레이캐스트, 즉 마우스가 클릭될 때 발사되는 눈에 보이지 않는 광선은 그대로 UI에 충돌하는 상태였다.

 

하지만 레이캐스트 블록 옵션의 예시 gif에서는 마우스가 클릭될 때 발사되는 광선이 UI를 무시하고 지나간 것이다.

 

 

레이캐스트 블록 옵션의 꺼진 상태와 켜진 상태의 차이점과 활용에 대한 예시는 디아블로 3로 들 수 있다. 디아블로 3에서는 탭(tab) 키를 누르면 미니맵이 열리는데 미니맵 UI가 화면 전체에 가득 차서 흐릿하게 보일 것이다. 하지만 이 미니맵은 화면 전체를 덮는 UI임에도 불구하고 클릭한 위치로 캐릭터를 이동할 수 있게 마우스의 클릭에서 발사되는 레이캐스트를 막지 않는다.

 

 

 

하지만 인벤토리 창의 경우에는 인벤토리 찾 어느 위치를 클릭해도 캐릭터가 인벤토리 창 너머로 클릭된 지형의 위치로 이동하지 않는다. 즉, 마우스 클릭에서 발사된 레이캐스트가 인벤토리라는 UI에 막혀서 이동할 위치를 찾지 못한 것이다.

 

 

마지막 옵션인 부모 그룹 무시(Ignore Parent Group)은 잘 사용되지 않는 옵션이다.

 

이렇듯이 캔버스 그룹을 사용하면 원래는 간단하지만 계층이 복잡해지면 어려워지는 작업을 간단하게 처리할 수 있다.

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

UI 비법서 (1)

 

당신의 레이아웃에 딱맞는 Content Size Fitter

 

 

작성 기준 버전 :: 2018.3 -2019.4

 

[이 포스트의 내용은 유튜브 영상으로도 시청하실 수 있습니다]

 

UI를 제작할 때, 보기 좋은 레이아웃을 만들어 내는 일은 매우 어렵다. 어렵사리 보기 좋은 레이아웃을 만들어 냈다고 해도 UI에 들어가는 내용의 크기가 바뀔 수 있는 상황이라면 정해진 텍스트나 이미지 등의 콘텐츠가 정해진 레이아웃을 벗어나는 경우가 쉽게 발생한다.

이러한 유동적인 상황에 대처하는 UI를 만드는 것은 이전까지는 스크립트에서 일일이 처리해야하는 번거로움이 있었다. 그런 번거로움을 해소하기 위한 기능으로 유니티는 콘텐츠 사이즈 피터(Content Size Fitter)라는 컴포넌트를 지원한다. 이 컴포넌트를 이용해서 콘텐츠의 크기에 따라 자유롭게 변화하는 UI를 제작하는 방법에 대해서 알아보도록 하자.

 

 

콘텐츠 사이즈 피터(Content Size Fitter)

 

콘텐츠 사이즈 피터는 단어 그대로 컴포넌트가 붙어있는 오브젝트의 크기를 내용물, 콘텐츠의 크기에 맞게 만들어주는 역할을 한다.

 

 

콘텐츠 사이즈 피터 컴포넌트를 게임 오브젝트에 추가하는 방법은 위의 이미지처럼 씬에서 Add Component 버튼을 누르고 "Content"를 검색해서 나오는 Content Size Fitter 컴포넌트를 선택하는 것이다.

 

 

콘텐츠 사이즈 피터를 사용하지 않으면 위의 이미지처럼 내용이 UI의 사이즈보다 커지면 옵션에 따라 뒷 부분이 잘리거나 영역을 넘어가서 표시되는 바람에 다른 UI를 침범하는 문제가 발생한다.

 

 

콘텐츠 사이즈 피터를 사용하면 전과 다르게 UI의 사이즈가 텍스트의 내용에 따라 자동으로 변하는 것을 확인할 수 있다.

 

 

추가된 콘텐츠 사이즈 피터는 인스펙터 뷰에서 위의 이미지와 같은 모습으로 보이며, Horizental Fit과 Vertical Fit라는 두 가지 프로퍼티를 설정할 수 있다.

 

선택 가능한 옵션으로는 Unconstrained, Min Size, Preferred Size 이렇게 세 가지가 있다. Unconstrained는 UI의 크기를 콘텐츠에 맞추지 않는 옵션으로 UI의 크기를 직접 맞추거나 고정된 크기를 사용해야 하는 경우 사용하면 되는 옵션이다. Min Size는 레이아웃 요소의 최소 크기를 기준으로 UI의 크기를 맞추는 옵션인데, 이 옵션의 경우에는 단독으로 사용되는 경우는 거의 없고 Layout group 계열의 컴포넌트와 함께 주로 사용된다. 마지막으로 Preferred Size 옵션은 기본적인 콘텐츠의 크기에 따라 UI의 크기를 맞추는 옵션으로 위의 예시 이미지처럼 텍스트나 이미지에 단독으로 콘텐츠 사이즈 피터를 사용하는 경우에 주로 사용된다.

 

 

 

 

 

콘텐츠 사이즈 피터와 피봇

 

콘텐츠 사이즈 피터로 인해 Rect Transform의 크기가 변경될 때는 UI의 피벗을 중심으로 조정되기 때문에, 피벗을 사용해서 UI의 크기가 조정되는 방향을 조절할 수 있다. 위의 이미지 중에서 콘텐트 사이즈 피터를 붙인 gif를 보면 피벗이 UI의 중심에 있어서 피벗을 중심으로 양 쪽 방향을 향해서 UI가 커지는 것을 볼 수 있다.

 

 

위의 이미지를 보면 X Pivot을 0으로 변경해서 UI의 피봇을 가장 좌측으로 옮긴 결과, UI의 트랜스폼이 왼쪽에서 오른쪽으로 확장되는 것을 볼 수 있다.

 

 

콘텐츠 사이즈 피터와 스크롤 뷰(Content Size Fitter & Scroll View)

 

콘텐츠 사이즈 피터를 가장 잘 응용할 수 있는 부분은 바로 스크롤 뷰이다. 이전에 스크롤 뷰에 대해서 설명했던 글에서는 스크롤 뷰에 들어가는 내용에 따라서 Content 오브젝트의 크기를 맞춰주는 방법을 스크립트로 설명했지만 콘텐츠 사이즈 피터를 사용하면 스크립트를 작성하지 않고도 훨씬 편하게 Content 오브젝트의 크기를 맞출 수 있다.

 

이제 콘텐츠 사이즈 피터를 사용해서 스크롤 뷰의 Content의 크기를 조절하는 방법을 알아보자.

 

 

우선 (200, 200) 사이즈의 스크롤 뷰를 생성한다.

 

 

 

그리고 스크롤 뷰의 Content 오브젝트 아래에 (150, 50) 사이즈의 이미지를 하나 만들어주자.

 

 

콘텐츠 사이즈 피터가 없는 상태에서 플레이 버튼을 눌러서 실행해보면 스크롤 뷰의 크기는 이미지의 크기나 갯수에 상관없이 동작하는 것을 확인할 수 있다.

 

다음에는 이 스크롤 뷰에 콘텐츠 사이즈 피터를 적용해보자.

 

 

우선 아이템들이 들어갈 Content 오브젝트를 선택하고 여기에 오브젝트들을 자동으로 정렬해주는 Vertical Layout Group 컴포넌트를 추가한다.

 

원하는 정렬 방법에 따라 Grid Layout Group이나 Horizontal Layout Group을 사용해도 된다. 각 프로퍼티의 값은 보기 좋아보이는 값을 찾아서 입력하도록 하자. 이 예시에서는 위 아래의 패딩을 10씩 주고 각 아이템 간의 간격을 5로 설정했다. 그리고 아이템의 정렬은 상단 중앙으로 설정했다.

 

그 다음에 내용물에 따라 오브젝트의 크기를 조정해주는 콘텐츠 사이즈 피터 컴포넌트를 추가한 뒤에 사용한 Layout Group에 맞게 Fit 옵션을 Min Size로 변경한다.

 

 

스크롤 뷰에 콘텐츠 사이즈 피터와 레이아웃 그룹을 적용하고 플레이를 해보면 아이템이 추가/제거됨에 따라서 자연스럽게 내부 Content의 크기가 늘어나서 스크롤이 가능하게 되는 것을 확인할 수 있다.

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

당신을 고통에 빠뜨릴 문제 (1)

코루틴과 Instantiate 그리고 Start

 

게임을 개발하면서 생겨나는 많은 버그들은 개발자들을 고통에 빠뜨리지만, 그 중에서도 엔진의 특성과 맞물려서 발생하는 버그는 깊이가 다른 고통을 느끼게 해준다. 이런 종류의 버그는 개발자가 생각한 로직으로는 완벽하지만 엔진의 특성으로 인해서 그 로직이 완벽하게 동작하지 않는 것이기 때문에 해당 엔진의 동작과 특성을 확실하게 알고 있지 않으면 어디서 버그가 발생했는지, 원인이 무엇인지 알기 어렵다.

 

이제부터 이러한 버그들에 대해서 알아보도록 하자. 첫 번째 내용은 코루틴과 Instantiate 그리고 Start에 얽힌 문제에 관한 내용이다.

 

 

코루틴(Coroutine)의 특성

 

코루틴은 유니티로 게임을 개발할 때, 자주 사용되는 기능 중에 하나다. 주로 비동기 처리나, 프로그램을 멈추지 않고 다른 동작을 기다리거나, 메인 로직 뒤에서 돌아가는 서브 루틴을 구현할 때 사용된다.

 

using System.Collections;
using UnityEngine;

public class B : MonoBehaviour
{
    public void CallCoroutine()
    {
        StartCoroutine(SomeCoroutine());
    }

    private IEnumerator SomeCoroutine()
    {
        Debug.Log("Start Coroutine");
        yield return null;
        Debug.Log("End Coroutine");
    }
}

 

using UnityEngine;

public class A : MonoBehaviour
{
    public B b;

    void Start()
    {
        b.CallCoroutine();
    }
}

 

위의 코드를 보면 A라는 클래스가 B라는 클래스의 CallCoroutine() 함수를 호출하여 B 클래스의 SomeCoroutine()을 동작하게 만들고 있다.

 

 

이 코드는 만약 B 오브젝트가 활성화(enabled)되어 있다면 정상적으로 동작하겠지만, 비활성화(disabled)된 상태라면 위의 이미지처럼 오브젝트가 활성화되지 않은 상태에서 코루틴을 호출했다는 에러 로그가 발생하고 해당 코루틴의 코드가 하나도 실행되지 않는다.

 

이것은 코루틴을 사용할 때 기본적인 주의 사항으로 해당 오브젝트가 활성화(Enabled)된 상태에서 코루틴을 호출해야 한다는 것을 의미한다.

 

이런 문제는 에러 로그가 명확하기 때문에 코루틴을 호출하기 전에 게임 오브젝트의 액티브를 확인해주거나 게임 오브젝트가 활성화된 이후에만 코루틴을 호출하도록 수정하면 간단하게 해결된다.

 

 

 

 

진짜 문제

 

이전의 예시처럼 이미 생성되어 있는 게임 오브젝트의 코루틴을 호출하는 문제는 활성화 상태만 잘 체크하면 된다. 진짜 문제는 이 코루틴과 Start, Instantiate가 엮였을 때부터 발생한다.

 

using UnityEngine;

public class A : MonoBehaviour
{
    public GameObject bPrefab;

    void Start()
    {
        var b = Instantiate(bPrefab).GetComponent<B>();
        b.CallCoroutine();
    }
}

 

이미 생성되어 있는 B 오브젝트를 참조하는 이전의 예시와 다르게 이번에는 B 오브젝트의 프리팹을 소유하고 있다가 A 오브젝트가 Start() 되면 B 오브젝트를 새로 생성해서 B 오브젝트의 코루틴을 호출하는 상황을 가정해보자.

 

using System.Collections;
using UnityEngine;

public class B : MonoBehaviour
{
    private void Start()
    {
        Debug.Log("Start");
    }

    public void CallCoroutine()
    {
        StartCoroutine(SomeCoroutine());
    }

    private IEnumerator SomeCoroutine()
    {
        // yield return null;
        Debug.Log("Start Coroutine");
      
        Debug.Log("Ing Coroutine");
       
        Debug.Log("End Coroutine");
    }
}

 

그리고 B 클래스는 Start() 함수가 호출되면 "Start"라는 로그를 띄우고 SomeCoroutine()에서 각 과정에 맞는 로그를 띄울 것이다.

 

자, 그러면 아래와 같이 여러 상황의 코루틴들을 가정해보자.

 

private IEnumerator SomeCoroutine()
{
    yield return null;
    Debug.Log("Start Coroutine");
      
    Debug.Log("Ing Coroutine");
       
    Debug.Log("End Coroutine");


}

 


private IEnumerator SomeCoroutine()
{
   
    Debug.Log("Start Coroutine");
    yield return null;
    Debug.Log("Ing Coroutine");
       
    Debug.Log("End Coroutine");


}

 


private IEnumerator SomeCoroutine()
{

    Debug.Log("Start Coroutine");
      
    Debug.Log("Ing Coroutine");
    yield return null;
    Debug.Log("End Coroutine");


}

 


private IEnumerator SomeCoroutine()
{

    Debug.Log("Start Coroutine");
      
    Debug.Log("Ing Coroutine");
       
    Debug.Log("End Coroutine");

    yield return null;
}

 

여러 상황의 코루틴에서 과연 Start() 함수는 같은 시점에 호출될까? 아니다. 유니티에서 게임 오브젝트의 Start() 함수는 게임 오브젝트가 Instantiate()로 생성되고 한 프레임 뒤에 호출되기 때문에 코루틴이 돌다가 만난 첫 번째, yield return 시점에 Start() 함수가 호출되게 된다. 만약 Start() 함수가 별다른 역할을 하지 않는다거나, 호출순서에 상관없는 일을 처리한다면 큰 문제는 없겠지만, 만약 코루틴의 동작과 연관된 중요한 작업을 Start()가 처리하고 있다면, 이 yield return의 위치에 따라서 심각하고도 감지하기 어려운 난감한 문제를 만나게 될 확률이 높아진다.

 

그리고 이것보다 심각한 문제로, Instantiate() 직후에 코루틴을 호출한 경우, 희박한 확률로 코루틴의 앞 부분 코드는 정상 동작을 하다가 첫 번째 yield return을 만나는 순간, 그 이후의 코드를 실행하지 않는 문제도 발생한다. 게다가 이 경우에는 어떠한 에러나 경고 로그도 발생하지 않기 때문에, 이 문제의 원인이나 해결법을 찾아내기가 아주 어렵다.

 

using System.Collections;
using UnityEngine;

public class B : MonoBehaviour
{
    private void Start()
    {
        Debug.Log("Start");
        StartCoroutine(SomeCoroutine());
    }

    private IEnumerator SomeCoroutine()
    {
        Debug.Log("Start Coroutine");
        yield return null;
        Debug.Log("Ing Coroutine");
       
        Debug.Log("End Coroutine");
    }
}

 

그렇기 때문에 만약 Instantiate()와 Start() 그리고 코루틴이 긴밀하게 엮여있거나, Instantiate()로 게임 오브젝트를 생성한 직후에 코루틴을 호출해서 어떤 작업을 처리해야 하는 경우라면, 별도로 코루틴을 호출하는 것보다는, 위의 코드처럼 Start() 함수에서 코루틴을 실행하는 것을 권장한다.

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

+ Recent posts