반응형

 

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

 

캐릭터 조작 기능이 들어있는 패키지 : 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

반응형
반응형

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 방식)]

 

[로딩 화면 구현하기(로딩 씬 방식)]

반응형
반응형

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를 무대의 커튼처럼 전면에 깔아서 로딩하는 방법 이외에도 아예 로딩씬을 불러온 뒤에 다음 씬을 로딩하는 방법도 있으며 해당 방법은 링크에서 확인할 수 있다.

반응형
반응형

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 만을 무대 커튼처럼 전면에 깔아두고 다음 씬을 로딩하는 방법도 있다.

반응형
  1. 장도 2020.04.28 13:31

    time += Time.deltaTime
    이렇게 하고 mathf.lerf( , , time)을 해버리면
    time이 1보다 커질 수도 있지 않나요?
    그런 경우엔 어떻게 되나요?

    • wergia 2020.04.28 15:06 신고

      Mathf.Lerp(a, b, t);에서 t가 1보다 커지면 자동으로 최대값은 b로 고정됩니다.
      만약 t값이 1보다 커졌을 때, 나오는 값이 b를 넘어서 계산되기를 원하면
      Mathf.LerpUnclamped(a, b, t); 를 사용하면 됩니다.

반응형

Tutorial (3)

 

씬(Scene) 뷰 조작법

 

작성 기준 버전 :: 2018.3.1f1

 

[본 튜토리얼은 유튜브 영상으로도 시청하실 수 있습니다]

 

이번 섹션에서는 유니티 에디터의 씬 뷰에 대한 기초적인 조작법을 알아본다.

 
씬 기즈모
 

 

씬 기즈모는 씬 뷰에 우측 상단에 표시되며, 씬 뷰 카메라가 현재 바라보는 방향을 나타낸다. 각 축을 클릭해서 씬 뷰의 카메라가 해당 축을 기준으로 씬을 바라보게 할 수 있다.

 

 

그리고 씬 기즈모 아래에 Persp이라는 글자가 적혀있는데 이것은 현재 씬 뷰의 카메라가 원근 모드(Perspective Mode)라는 뜻으로 이것을 클릭하면 씬 뷰의 카메라를 직교 모드(Orthographic Mode)로 전환해서 원근감으로 왜곡되어 보이지 않게 만들 수 있다.

 

 

 
 

화살표 이동

 

씬 뷰에서는 키보드 화살표를 이용해서 카메라 정면을 기준으로 전후좌우로 움직일 수 있다.

 

 

Shift 키를 누르면 더 빠르게 움직일 수 있다.

 

 

핸드 툴

 

단축키 Q를 누르거나 핸드툴을 선택하면 아래와 같은 마우스 컨트롤을 할 수 있다.

 

Move : 좌클릭 드래그 하면 카메라를 기준으로 화면을 상하좌우로 움직일 수 있다.

 

 

 

Orbit : Alt 키를 누르고 좌클릭 드래그를 하면 카메라가 바라보는 피벗을 기준으로 궤도를 따라서 회전한다.

 

 

 

Zoom : Alt 키를 누르고 우클릭 드래그를 하면 씬 뷰를 확대/축소할 수 있다. Mac은 Control 키를 누르고 좌클릭 드래그해서 확대/축소할 수 있다. 마우스 스크롤을 회전시켜도 줌 조절이 가능하다.

 

 

 

 

Shift 키를 누르고 조작하면 이동, 회전, 줌 속도가 빨라진다.

 

 

플라이스루(Flythrough) 모드

 

플라이스루 모드를 통해서 1인칭 게임과 같은 동작으로 씬을 날아다니면서 탐색할 수 있다.

 

 

 

씬 뷰에 마우스를 우클릭한 채로 FPS 게임처럼 WASD를 통해서 전후좌우로 움직일 수 있다. QE 키로는 상하로 움직일 수 있다.

 

플라이스루 모드는 Perspective Mode에서만 동작하며, Orthgraphic Mode에서는 우클릭 드래그를 하면 피벗을 중심으로 회전하는 동작만 보인다. 더불어 2D모드에서도 플라이스루 모드가 동작하지 않고, 우클릭 드래그하면 화면을 이동시키는 동작만 보인다.

반응형
반응형

Tutorial (2)

유니티 에디터의 화면 구성

 

작성 기준 버전 :: 2018.3.1f1

 

[본 튜토리얼은 유튜브 영상으로도 시청하실 수 있습니다]

 

유니티 에디터로 프로젝트를 처음으로 열면 화면이 아래의 이미지와 같이 구성되어 있다.

 

 

 

이 화면의 구성에 대해서 하나씩 알아보도록 하자.

 

 

씬(Scene) 뷰

 

 

에디터 레이아웃 구성의 한 가운데 있는 것은 Scene 뷰다. 유니티에서 씬(Scene)이라는 개념은 일종의 맵(Map)이나 레벨(Level)에 해당한다. 씬 뷰는 이러한 씬에 배경을 꾸미기위해 소품이나 배경 건물 등을 배치하는데 사용된다.

 

 

하이어라키(Hierarchy) 창

 

 

씬 뷰의 좌측에 있는 하이어라키 창은 씬에 배치되어 있는 오브젝트들을 보여준다. 가장 상단에 SampleScene이라는 이름으로 현재 열려있는 씬의 이름이 표시되고, 그 아래에 그 씬에 포함된 오브젝트들이 나타난다. 기본적으로 배치되어 있는 오브젝트는 Main Camera라는 이름의 카메라와, Directional Light라는 이름의 조명이다.

 

 

프로젝트(Project) 창

 

 

프로젝트 창은 현재 프로젝트에 포함된 텍스처나 모델링, 스크립트, 씬 등의 애셋(Asset)을 보여주는 창이다. 개발 경험이 많지 않은 경우에는 프로젝트 창에 애셋들이 추가되는 대로 중구난방으로 쌓아두는 일이 많은데, 애셋들을 적절하게 분류해서 정리해두는 버릇을 들여두는게 나중에 필요한 애셋을 찾거나 불필요한 애셋을 정리할 때 큰 도움이 되며, 개발 속도에도 긍정적인 영향을 미칠 것이다.

 

 

인스펙터(Inspector) 창

 

 

인스펙터 창은 지금은 아무 내용이 없지만, 하이어라키 창에서 씬에 배치된 오브젝트나 프로젝트 창에서 프로젝트에 포함된 애셋을 선택하면 그것에 대한 자세한 정보를 보여주는 역할을 한다.

 

 

하이어라키 창에서 메인 카메라 오브젝트를 선택하고 인스펙터 창을 보면 메인 카메라 오브젝트의 자세한 정보를 확인하고 수정할 수 있게 된다.

 

 

게임(Game)

 

 

씬 창 뒤에 탭으로 되어 있는 게임 창 탭을 선택하면 씬 창이 뒤로 전환되고 게임 창이 앞으로 나온다.

 

게임 창은 씬 창과 같이 씬을 보여주는 역할을 하지만, 게임 창은 씬 창과는 다르게 카메라가 보여주는 것만을 볼 수 있는 창이다. 즉, 실제 게임에서 보게 될 장면을 보여주는 창이다.

 

 

 

 

 

콘솔(Console) 창

 

 

콘솔 창은 개발도중에 발생한 에러나 경고, 개발자가 기능을 테스트하거나 값을 체크하기 위해 출력시킨 로그 등이 출력되는 창이다. 출력된 로그를 더블클릭하면 비주얼 스튜디오가 열리고 해당 로그가 출력된 스크립트의 위치로 이동하게 된다.

 

로그의 종류

 

 

유니티 엔진에서 로그는 크게 일반 로그, 경고 로그, 에러 로그로 나누어지고, 일반 로그는 데이터의 값이나 진행 상황, 상태를 체크하기 위해 사용되는 로그이고, 경고 로그는 치명적이지는 않지만 수정할 것을 권장하는 로그이며, 에러 로그는 게임이 정지하거나 기능에 심각한 이상이 발생하는 상황에 대한 로그이다.

 

세부적인 버튼의 내용은 다음과 같다.

 

 

뒤의 세 개 버튼은 각각 일반 로그, 경고 로그, 에러 로그 보기 버튼이며, 해당 버튼을 눌러서 원하는 종류의 로그만 볼 수 있다.

 

 

일반 로그만 활성화한 상태이다.

 

 

경고 로그만 활성화한 상태이다.

 

 

 

에러 로그만 활성화한 상태이다.

 

 

Clear 버튼은 현재까지 출력된 로그들을 모두 지운다.

 

 

 

Collapse 버튼은 같은 내용의 로그가 여러 번 출력되면 여러 줄로 표시하지 않고 한 줄로 표시하며 같은 내용의 로그가 몇 번이나 출력되었는지를 보여주는 기능이다. 이것은 로그의 순서가 중요하지 않고, 출력되었느냐 혹은 몇 회나 출력되었는지가 중요한 경우에 사용하게 된다.

 

Clear on Play 버튼은 에디터에서 플레이 버튼을 눌렀을 때, 남아있는 로그를 모두 지우고 플레이를 시작하게 만든다. 남아있는 이전에 띄운 로그가 남아있다면 플레이 중에 뜨는 로그가 보기 힘들어지는 경우가 많기 때문에 사용한다.

 

Error Pause 버튼은 플레이 도중에 에러 로그가 발생하면 플레이를 일시정지 시키는 버튼이다. 에러가 나도 플레이가 계속되면 에러가 발생하는 순간을 놓칠 수도 있기 때문에 에러가 발생한 순간을 잡아내기 위해서 사용하는 기능이다.

 

Editor 버튼은 에디터의 로그를 출력한다는 뜻의 버튼이다. 나중에 모바일 게임을 개발하다보면 APK로 빌드한 앱을 모바일 기기에 설치해서 컴퓨터와 연결하고 실행해서 실시간으로 로그를 보기위해서 사용되는 버튼이다.

 

 

애셋 스토어(Asset Store) 창

 

 

 

 

애셋 스토어 창은 다른 개발자들이 만든 게임 개발용 애셋들을 구매할 수 있는 창이다. 대규모 개발팀이나 회사는 게임에 필요한 모든 리소스들을 개발할 여력이 있겠지만, 소규모 개발팀이나 1인 개발자는 모든 리소스를 만들어내기 매우 어렵기 때문에 다른 사람이 만든 애셋을 구매해서 사용하면 더 빠르게 게임을 개발할 수 있다는 장점이 있다.

 

 

그 외의 창들

 

유니티 엔진에는 방금 화면 구성에서 소개한 창들 외에도 많은 종류의 창들이 존재한다.

 

 

에디터 상단의 메뉴 바에서 Window 메뉴를 클릭해서 드롭다운 메뉴를 펼치면 숨겨진 창들의 목록을 보고 필요한 창을 열어서 사용할 수 있다.

 

 

그 외의 버튼들

 

 

이 버튼들은 씬에 배치된 오브젝트를 이동, 회전, 크기 조절을 하는데 사용되는 버튼들이다.

 

 

이 버튼들은 에디터에서 게임을 실행, 일시정지, 한 프레임씩 넘기기를 하는 버튼이다.

 

 

좌측 버튼부터 순서대로, 팀단위 작업을 위한 유니티 콜라보(Collaborate) 버튼, 유니티가 제공하는 서비스 버튼, 계정 관리 버튼, 씬 창에서 보이기 원하는 레이어를 선택하는 버튼, 유니티 에디터의 레이아웃 버튼이다.

 

 

유니티 에디터 레이아웃 수정하기

 

유니티 에디터의 화면 구성은 사용자가 편한 방식으로 레이아웃을 수정할 수 있다.

 

 

위치를 수정하려고하는 창의 탭을 드래그해서 에디터에서 완전히 떼어내거나 다른 곳에 배치할 수 있다.

 

 

완전히 떼어낸 하이어라키 창

 

 

다른 위치에 배치한 하이어라키 창

 

 

각자 사용하기 편한 방식으로 레이아웃을 배치해보자.

 

 

수정한 레이아웃 저장하기

 

수정한 레이아웃은 저장해두고 언제든지 다시 불러올 수 있다. 에디터 우측 상단 구석에 Default 버튼을 클릭하면 드롭다운 메뉴 중에 Save Layout... 버튼을 클릭하면 수정한 레이아웃의 이름을 지어줄 수 있는 대화상자가 뜬다.

 

 

좌측부터 게임, 씬, 콘솔이 한 묶음으로 묶여있고, 그 다음엔 하이어라키와 프로젝트, 마지막으로 인스펙터로 나열되서 3-2-1로 배치되어 있으니 이 레이아웃의 이름을 Countdown이라고 저장하겠다.

 

 

레이아웃을 저장하고 나면 Default로 되어있던 버튼이 Countdown으로 되어있으며 추가한 Countdown 레이아웃이 드롭다운 목록에 추가되어 있는 것을 확인할 수 있다. 이렇게 레이아웃을 저장해두면, 레이아웃이 바뀌어도 언제든지 손쉽게 자주 사용하는 레이아웃으로 돌아올 수 있다.

 

 

반응형
반응형

다른 레벨로 이동하기

 

작성 기준 버전 :: 4.21.1

 

대규모 오픈 월드 게임이 아닌 경우 장소를 이동할 때나 대규모 오픈 월드 게임이더라도 특정한 장소로 이동하는 경우에도 레벨 혹은 씬을 전환하는 방식을 주로 사용한다. 이번 섹션에서는 다른 레벨로 이동하는 방법에 대해서 알아본다.

 

프로젝트 생성

 

LevelOpenTest라는 이름으로 C++ 삼인칭 프로젝트 하나를 새로 생성한다.

 

 

 

레벨 구성

 

프로젝트가 생성되면 레벨은 다음과 같이 꾸며져 있을 것이다.

 

 

그 레벨을 다음과 같이 수정한다. 새로 만든 구역에 들어가면 다른 레벨로 이동하도록 만들 예정이다.

 

 

이 다음에는 이동할 레벨을 추가한다.

 

 

그리고 새로 추가한 레벨을 다음과 같이 꾸민다.

 

 

이 맵을 SecondMap이라는 이름으로 저장한다.

 

 

 

 

 

LevelTransferVolume

 

이 다음에는 콜리전에 플레이어가 닿으면 다른 레벨로 전송시키는 볼륨을 만든다.

 

Actor 클래스를 상속받아서 LevelTransferVolume 클래스를 만든다.

 

 

LevelTransferVolume.h에서 다음 함수와 멤버 변수를 선언한다.

 

protected:
    virtual void NotifyActorBeginOverlap(AActor* OtherActor) override;

private:
    UPROPERTY(EditAnywhere, meta = (AllowPrivateAccess = "true"))
    FString TransferLevelName;

    UPROPERTY()
    class UBoxComponent* TransferVolume;

 

LevelTransferVolume.cpp로 넘어가서 박스 컴포넌트의 기능과 레벨 이동 함수를 사용하기 위해서 다음 전처리기를 추가한다.

 

#include "Engine/Classes/Components/BoxComponent.h"

#include "Kismet/GameplayStatics.h"

 

생성자 함수에 TransferVolume를 초기화 하는 코드를 추가한다.

 

TransferVolume = CreateDefaultSubobject<UBoxComponent>(TEXT("TransferVolume"));
RootComponent = TransferVolume;
TransferVolume->SetCollisionProfileName(TEXT("OverlapOnlyPawn"));

 

NotifyActorBeginOverlap() 함수를 구현한다.

 

void ALevelTransferVolume::NotifyActorBeginOverlap(AActor * OtherActor)
{
    APawn* Pawn = Cast<APawn>(OtherActor);
    if (Pawn != nullptr)
    {
        UGameplayStatics::OpenLevel(this, TransferLevelName);
    }
}

 

UGamePlayStatics 클래스의 OpenLevel() 함수가 이번 섹션의 주 목적이다. OpenLevel() 함수를 호출해서 이동하고자 하는 레벨의 이름을 넣어주면 원하는 레벨로 이동이 가능하다.

 

코드 작성이 완료되면 프로젝트를 빌드하고 언리얼 에디터로 돌아간다.

 

기본 맵의 새로 뚫어놓은 위치에 볼륨을 배치한 뒤 Transfer Level Name을 SecondMap으로 설정한다.

 

 

플레이해서 볼륨을 배치한 곳으로 캐릭터를 이동시키면 지정해준 다른 맵으로 이동하는 것을 확인할 수 있다.

 

 

반응형
반응형

유니티 에디터에서 플레이 중에 씬 전환시 다음 씬에서 라이트가 어두워지는 버그(5.6)


유니티 5에는 고질적인 버그가 하나 있다. 유니티 에디터에서 게임 테스트를 위해서 플레이 버튼을 눌러서 플레이할 때 다른 씬으로 이동하면 라이트가 어둡게 보이는 문제가 바로 그것이다.



의도한 씬의 밝기는 첫 번째 그림과 같지만 유니티 에디터에서 플레이 버튼을 눌러서 게임을 실행한 뒤에 플레이 도중에 씬을 넘어가면 라이트의 밝기가 두 번째 그림처럼 어두워 진다. 이러한 현상은 게임을 빌드해서 실행했을 경우에는 발생하지 않지만, 라이팅 테스트 하나만을 위해 매번 게임을 새로 빌드해서 실행하는 것은 매우 번거로운 일이다.


이 문제를 해결하는 방법은 다음과 같다.



상단 메뉴에서 Window > Lighting > Setting 을 선택하면 라이팅 세팅을 할 수 있는 창이 열린다.




열린 Lighting Setting 창의 가장 아래쪽에서 Auto Generate가 체크되어 있는 것을 볼 수 있는데 이 체크를 해제하면 옆의 Generate Lighting 버튼이 활성화된다. 이 버튼을 클릭하면 유니티 에디터에서 테스트를 진행할 때에도 빛이 제대로 들어오는 것을 확인할 수 있다.


반응형

+ Recent posts