게임 중간에 로딩 화면 없이 연속적으로 씬을 불러오는 스트리밍 레벨 기능을 구현하는 방법을 알아봅시다!

 

캐릭터 조작 기능이 들어있는 패키지 : https://github.com/wwer20001/wer-prog...

씬 모양을 미리 만들어둔 패키지 : https://github.com/wwer20001/wer-prog...

 

타임라인

0:03 스트리밍 레벨 기능 개요

0:40 리소스 임포트와 필요한 오브젝트 가져오기

1:28 씬 구조 살펴보기

2:32 스트리밍 씬 로드 트리거 만들기

3:02 스트리밍 씬 로드 기능 구현하기

4:30 트리거 세팅과 테스트

4:55 지나간 씬 언로드 기능 구현하기

6:09 지나간 씬이 언로드되고 캐릭터가 사라지는 문제 해결하기

7:05 다음 씬으로 완전히 넘어갔을 때 화면이 어두워지는 문제 해결하기

7:25 지나온 씬으로 다시 돌아가기 테스트

7:28 지나온 씬으로 돌아갈 때 캐릭터가 2개가 되는 문제 해결하기

8:01 스트리밍 레벨 기능 사용시 고려 사항1

8:38 스트리밍 레벨 기능 사용시 고려 사항2

 

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

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

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 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 

씬 불러오기

 

작성 기준 버전 :: 2019.2

 

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

 

유니티 엔진의 씬은 게임의 맵이나 레벨 개념에 해당한다. 그리고 대부분의 게임은 하나보다 많은 맵이나 레벨, 즉 씬으로 구성된다. 그래서 게임에서 다른 씬으로 넘어가기 위해서는 씬과 씬 사이를 이동하기 위해서 다른 씬을 불러오는 방법을 알아야 한다.

 

씬 세팅하기

 

 

먼저 프로젝트를 제일 처음 생성했을 때 있는 씬에 큐브 오브젝트를 생성하고 카메라 앞 왼쪽 화면에 보이게 적당히 배치한 다음 씬을 저장한다. [Ctrl + S] 단축키를 누르면 간단하게 씬의 변경 내용을 저장할 수 있다.

 

그리고 프로젝트 뷰의 Scenes 폴더 아래에 지금 로드되어 있는 씬인 Sample Scene의 이름을 Scene1로 변경한다.

 

그 다음에는 이 Scene1에서 이동하게 될 새로운 씬을 생성하자.

 

 

프로젝트 뷰에 우클릭하고 [Create > Scene] 항목을 선택하면 새로운 씬을 생성할 수 있다. 새로 생성한 씬의 이름은 Scene2로 한다.

 

 

이렇게 생성한 씬 애셋을 더블 클릭하면 하이어라키에 열려있던 씬이 Scene1에서 Scene2로 바뀐다.

 

 

그리고 Scene2에는 Shpere 게임 오브젝트를 생성해서 카메라 앞 오른쪽 화면에 보이게 배치한다. 이렇게 하면 Scene1에 있다가 Scene2를 불러오면 화면 앞에 있는 게임 오브젝트의 모양이 바뀌면서 씬이 바뀌었다는 것을 명확하게 알 수 있을 것이다.

 

Scene2를 저장하고 다시 Scene1로 돌아간다.

 

SceneMover 스크립트 작성하기

 

그 다음에는 SceneMover라는 이름으로 C# 스크립트를 생성하자.

 

using UnityEngine.SceneManagement;

 

제일 먼저 스크립트의 상단에 using UnityEngine.SceneManagement; 라는 코드를 작성한다. 이 코드는 개발자가 스크립트 에디터에게 "SceneMover.cs 파일에서 UnityEngine.SceneManagement 네임스페이스에 들어있는 씬과 관련된 기능을 사용하겠다"라고 알려주는 역할을 한다.

 

참고로 네임스페이스라는 것은 C#에서 특정한 기능을 묶어두는 데 주로 사용된다.

 

여기 UnityEngine.SceneManagement 네임스페이스에서는 유니티 엔진의 씬과 관련된 기능들이 담겨있다.

 

public class SceneMover : MonoBehaviour

{

    void Update()

    {

        if(Input.GetKeyDown(KeyCode.Space))

        {

            SceneManager.LoadScene("Scene2");

        }

    }

}

 

네임스페이스 선언이 끝나면 Update 함수에서 스페이스 키를 입력을 받았을 때, SceneManager.LoadScene 함수를 호출하게 만든다. 이 함수로 다른 씬을 불러올 수 있다. 

 

매개변수에 불러오고자하는 씬의 이름이나 번호를 넣으면 된다. 우리는 Scene1에서 Scene2로 이동할 생각이기 때문에 Scene2라는 이름을 넣어주면 된다.

 

코드를 저장하고 에디터로 돌아가서 게임 오브젝트를 하나 만들고 거기에 SceneMover 컴포넌트를 추가한다.

 

 

그리고 플레이 버튼을 누르고 스페이스 키를 눌러보면 Scene2라는 씬이 빌드 세팅에 추가되지 않았거나 애셋 번들로부터 불러올 수 없어서 로드할 수 없다는 로그가 나온다.

 

유니티에서 씬을 불러오기 위해서는 씬이 저장된 애셋 번들이 있든지 아니면 씬이 빌드 세팅에 추가되어 있어야 한다.

 

빌드 세팅에 씬 추가하기

 

씬을 빌드 세팅에 추가 시켜보자.

 

 

우선 상단 메뉴바에서 [File > Build Settings...] 항목을 선택하면 빌드 세팅 창이 열린다.

 

 

창 위쪽에 비어있는 Scenes In Build 칸을 볼 수 있는데 여기에 추가된 씬들은 게임에 포함되어 빌드된다.

 

만들어둔 Scene1과 Scene2를 Scenes In Build 칸에 끌어다 놓으면 Scenes In Build 칸에 추가한 씬이 표시될 것이다.

 

빌드 세팅 창을 끄고 다시 플레이를 시킨 뒤, 스페이스 버튼을 눌러보면 Scene1에서 Scene2로 이동하면서 Cube 오브젝트가 사라지고 Sphere 오브젝트가 나타난다.

 

이게 바로 제일 기본적인 씬 이동 방법이다.

 

Additive로 씬 불러오기

 

첫 번째 방법은 씬을 완전히 이동하는 방법이었다면 이번에는 기존의 씬을 남겨둔 상태로 새로운 씬을 겹쳐서 불러오는 방법을 알아보자.

 

public class SceneMover : MonoBehaviour

{

    void Update()

    {

        if(Input.GetKeyDown(KeyCode.Space))

        {

            SceneManager.LoadScene("Scene2", LoadSceneMode.Additive);

        }

    }

}

 

매개변수 "Scene2"뒤에 콤마(,)를 찍으면 두 번째 매개변수로 넣을 수 있는 옵션이 나타나는데, 형식이 LoadSceneMode 열거형인 것을 알 수 있다.

 

LoadSceneMode 모드에는 Single과 Addtive, 두 가지가 있는데 우선 Single은 기본 옵션으로 앞에서 구현한 것처럼 씬을 불러오면 이전 씬은 완전히 사라지고 새로운 씬으로 바뀌는 옵션이다. 그리고 Additive는 앞의 씬을 남겨두고 거기에 얹어서 새로운 씬을 불러오는 것이다.

 

두 번째 매개변수에 LoadSceneMode.Additive를 넣어준 뒤 코드를 저장하고 에디터로 돌아가서 게임을 플레이 시킨 다음에 스페이스 키를 누르면 Scene1이 남아있는 상태로 Scene2가 불러와지는 것을 볼 수 있다.

 

다만 콘솔 창을 보면 로그가 굉장히 많이 발생하고 씬을 불러오기 전보다 매우 밝은 것을 알 수 있는데, 이것은 씬 안에 소리를 감지하는 Audio Listener라는 컴포넌트와 Directional Light가 두 개가 있어서 발생하는 문제이다. 그리고 메인 카메라도 중복으로 두 개가 있는 상태이다.

 

이런 문제들 때문에 Addtive로 불러올 씬에는 불필요한 카메라나 Directional Light를 만들지 않는 것이 좋다.

 

비동기 방식 씬 불러오기

 

지금까지 사용한 LoadScene 함수는 동기 방식 함수이다. 이게 무슨 의미인가하면 LoadScene 함수로 씬을 호출하면 씬을 불러오는 과정이 끝날 때까지 다른 일을 아무 것도 하지 못한다는 뜻이다.

 

이 예시처럼 씬에 고작 오브젝트가 몇 개만 있는 게임이라면 LoadScene 함수만으로 씬을 불러와도 되겠지만 최신 게임들처럼 씬 하나에 엄청나게 많은 오브젝트들이 들어있는 무거운 게임이라면 씬을 불러오는 긴 시간동안 아무 것도 하지 못하게 될 것이다.

 

다른 씬을 불러오는 도중에 팁을 보여준다든지 플레이어에게 미니 게임을 할 수 있게 한다든지 해서 씬이 불러와지는 시간에 플레이어가 지루함을 느끼지 않도록 하려면 다른 작업을 처리할 수 있어야 한다. 거기에 필요한게 비동기 방식 씬 불러오기이다.

 

public class SceneMover : MonoBehaviour

{

    void Update()

    {

        if(Input.GetKeyDown(KeyCode.Space))

        {

            StartCoroutine(LoadSceneCoroutine());

        }

    }

 

    IEnumerator LoadSceneCoroutine()

    {

        yield return SceneManager.LoadSceneAsync("Scene2");

    }

}

 

LoadSceneAsync 함수를 사용하면 함수가 씬을 불러오는 도중에 다른 작업을 처리할 수 있게 된다.

 

단, 이 함수를 제대로 사용하려면 코루틴과 함께 사용해야 한다.

 

코드를 저장하고 에디터로 돌아가서 게임을 플레이 시킨 다음 스페이스 키를 누르면 사실 눈에 띄는 변화는 없다.

 

비동기 씬 로딩이 효과가 있으려면 그만큼 불러올 씬이 무겁고 불러올게 많아야 의미가 있다.

 

비동기 씬 불러오기 방식을 응용하는 방법은 아래의 포스트에서 확인할 수 있다.

 

[로딩 화면 구현하기(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 

로딩 씬(Loading Scene) 구현하기(커튼 방식)

 

작성 기준 버전 :: 2019.1.4f1

 

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

 

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

 

 

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

 

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

 

 

개념

 

 

 

이전에는 로딩하는 씬을 전용으로 만들어서 로딩하는 방법을 소개했었다. 이번에는 그 방법과는 다르게 마치 무대에서 잠시 커튼을 내리고 무대를 교체하는 것과 비슷하게 로딩 바를 보여주는 UI를 전면에 씌운 뒤 로딩하는 방법을 구현해본다.

 

 

구현하기

 

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

 

 

코드 작성하기

 

씬 화면을 가리고 로딩작업을 진행하는 SceneLoader 클래스를 생성하고 다음의 순서로 코드를 작성한다.

 

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

 

SceneLoader는 기존 씬을 UI로 가리고 다른 씬을 불러오는 등의 작업을 해야하기 때문에 UnityEngine.SceneManagement와 UnityEngine.UI 네임스페이스를 using 선언해준다.

 

public class SceneLoader : MonoBehaviour
{
    protected static SceneLoader instance;
    public static SceneLoader Instance
    {
        get
        {
            if(instance == null)
            {
                var obj = FindObjectOfType<SceneLoader>();
                if(obj != null)
                {
                    instance = obj;
                }
                else
                {
                    instance = Create();
                }
            }
            return instance;
        }

        private set
        {
            instance = value;
        }
    }

    [SerializeField]
    private CanvasGroup sceneLoaderCanvasGroup;
    [SerializeField]
    private Image progressBar;

    private string loadSceneName;

    public static SceneLoader Create()
    {
        var SceneLoaderPrefab = Resources.Load<SceneLoader>("SceneLoader");
        return Instantiate(SceneLoaderPrefab);
    }

    private void Awake()
    {
        if (Instance != this)
        {
            Destroy(gameObject);
            return;
        }

        DontDestroyOnLoad(gameObject);
    }
}

 

SceneLoader의 기본적인 구성은 위와 같다. 이 클래스는 싱글톤 패턴으로 작성되어서 어디서든지 호출할 수 있도록 만들어졌으며, 전체 패널의 투명도를 조절하는 방식으로 씬 로딩 UI를 페이드 인 아웃을 시켜서 자연스럽게 등장시키기 위해서 캔버스 그룹을 사용한다. 그리고 진행도를 표현하기 위해서 이미지를 멤버 변수로 가진다. 

 

public void LoadScene(string sceneName)
{
    gameObject.SetActive(true);
    SceneManager.sceneLoaded += LoadSceneEnd;
    loadSceneName = sceneName;
    StartCoroutine(Load(sceneName));
}

private IEnumerator Load(string sceneName)
{
    progressBar.fillAmount = 0f;
    yield return StartCoroutine(Fade(true));

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

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

        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;
            }
        }
    }
}

private void LoadSceneEnd(Scene scene, LoadSceneMode loadSceneMode)
{
    if(scene.name == loadSceneName)
    {
        StartCoroutine(Fade(false));
        SceneManager.sceneLoaded -= LoadSceneEnd;
    }
}

private IEnumerator Fade(bool isFadeIn)
{
    float timer = 0f;

    while(timer <= 1f)
    {
        yield return null;
        timer += Time.unscaledDeltaTime * 2f;
        sceneLoaderCanvasGroup.alpha = Mathf.Lerp(isFadeIn ? 0 : 1, isFadeIn ? 1 : 0, timer);
    }

    if(!isFadeIn)
    {
        gameObject.SetActive(false);
    }
}

 

커튼 방식으로 로딩 UI를 구현하는 핵심 코드는 위와 같다. 씬을 로딩하는 로직 자체는 로딩 씬 교체 방식과 같으며 여기에 추가적으로 씬을 로딩하기 직전에 로딩 UI를 페이드 인하는 부분과 로딩이 완전히 끝난 지점에서 페이드 아웃되는 코드가 추가되었다. 그리고 씬 로딩 완료가 완전히 완료된 시점을 확인하기 위해서 SceneManager의 SceneLoaded 콜백에 함수를 추가해주는 코드 역시 추가되었다.

 

코드를 모두 작성했다면 저장하고 에디터로 돌아간다.

 

 

 

로딩 UI 구성

 

 

 

로딩 UI를 구성하기 위해서 SceneLoader라는 이름으로 캔버스(Canvas)를 하나 만들고 거기에 캔버스 그룹과 아까 만든 Scene Loader 컴포넌트를 부착한다. 그리고 Scene Loader Canvas Group 프로퍼티에 방금 추가한 캔버스 그룹 컴포넌트를 할당해준다.

 

 

그 다음엔 화면 전체를 덮는 이미지 하나를 Background라는 이름으로 추가한다. 이 이미지는 화면 전체를 가림으로써 씬이 교체되는 것을 플레이어의 시선에서 가려주고 마우스 입력을 방지하는 용도로 사용된다. 여기에는 검은 이미지 이외에도 플레이어의 시선을 끌만한 컨셉아트나 배경 이미지를 넣어줄 수 있다.

 

 

 

그리고 씬 로딩 진행도를 보여줄 이미지를 Progress Bar라는 이름으로 추가하고 위의 이미지와 같이 설정한다.

 

 

Progress Bar 이미지 설정이 끝났다면 SceneLoader 게임 오브젝트를 선택한 뒤, 위의 이미지와 같이 SceneLoader의 액티브를 끄고, Canvas Group의 Alpha 값을 0으로 설정한다. 그리고 Progress Bar 이미지를 Scene Loader 컴포넌트의 Progress Bar 프로퍼티에 할당해준다.

 

 

마지막으로 프로젝트 뷰에 Resources 폴더를 만들고 방금 만든 SceneLoader 게임 오브젝트를 드래그해서 프리팹으로 만들어주고 게임 오브젝트를 씬에서 삭제한다.

 

 

테스트 세팅하기

 

 

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

 

using UnityEngine;

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

 

 

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

 

 

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

 

 

 

테스트

 

 

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

 

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

 

 

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

 


 

이렇게 로딩 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

-

로딩 씬(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

 

반응형

다른 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

 

반응형

로딩 씬(Loading Scene) 구현하기

 

 

 


이 글은 과거 버전의 글로 최신 버전의 글로 다시 작성되었다. 최신 버전의 글은 씬 교체 방식커튼 방식, 두 가지로 구현되었다.

본문

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