Post Lists

2019년 4월 1일 월요일

unity scene manager 사용하는 방법

http://myriadgamesstudio.com/how-to-use-the-unity-scenemanager/

여기자료 번역

========================================
유니티 SceneManager 사용방법
이 블로그 포스트에서, 나는 모든 강력한 single scene에 대한 대안의 방법을 보여줄 것이다. synchronous and asynchronous scene loading, transition(변환) effects, additive(부가) scenes같은 기법들은 Unity의 SceneManager로 모두 가능하다. 놀랍게도, 이 기능의 어떠한 것을 구현하는데 있어서 복잡한 Scene 관리 스크립트가 요구되지 않는다. Additive scenes는 또한 매우 adaptive(조정할 수 있는)한데, 여러가지 게임 scenes, configuration scenes, debugging scenes, 그리고 많은 다른 유용한 대안들을 불러오는데 사용될 수 있다.

나는 원래 플레이어가 게임 월드를 탐험하고 있을 때 실시간으로 scenes을 로딩하는 것을 얼마나 잘 처리할지를 보기위해 Unity에서의 scene management를 조사했었다. 몇 가지 실험후에, 우리는 이제 모든 우리의 게임 잼에서 SceneLoader script를 사용할 뿐만 아니라, 우리의 main title에서도 사용한다. 그리고 윌는 그 결과에 매우 만족한다. 나는 이 글이 너에게 scene manager에 대한 유용한 입문을 주고, 그것의 가장 기본적인 기능의 사용방법을 주기를 바란다.

나는 모든 예제에 대한 깃헙 저장소를 만들었다. 그래서 편하게 끝으로 가서, 그 데모 프로젝트를 다운받아라. 여기에 내가 만든 (Alisha가 아트 작업을 한) 프로토타입의 좋지 않은 퀄리티의 GIF가 있다. 이것은 나의 첫 번째 scene loading 구현에 대해 작업한 것이다.

The Why
여기 Myriad Games Studio에서, 우리는 많은 게임 잼들을 한다. 아마도, 2달마다 잼을 한다. Myriad에서 하는 내부적인 것 또는 Tasjam events 같은 구성된 이벤트 둘 중 하나이다. 내부 잼은 일반적으로 우리가 좀 더 배우길 원하는 어떤 기능을 구현하는 게임을 만드는 것에 집중한다. TasJam 이벤트는 우리가 우리의 design muscles을 뽐내고, 특정한 제약 내에서 흥미로운 게임을 만들려고 하는 곳이다.

게임잼들은 일반적으로 우리가 어떤 분야에서 좀 더 지식을 얻어야 할 필요가 있는지 보는 것을 도와준다. 나에게 있어서 가장 주요한 분야 중의 하나는 game의 scenes을 어떻게 설정하는지와 그러한 scenes들을 부드럽게 load하는 것이다. 팀과 토론한 후에, 나는 scene loading solution을 연구하고 불러올 몇 주의 시간을 확보했다. 그 최종 scripts는 간단하고 우리가 만드는 어떤 게임에서 든지 추가하기에 간단하다. 모든 게임 잼은 이제 엄청난 절약된 시간을 갖는데, 우리가 유연한 scene setup과 pre-made(미리 만들어진) scene loading solution을 가지기 때문이다. 이것은 우리가 설계 단계에서 좀 더 많은 시간을 쓸 수 있다는 것을 의미한다 - 게임 개발의 가장 어려운 부분!

SceneManager를 사용하는 것의 다른 좀 더 구체적인 이유들 중의 몇몇과 너의 게임을 여러 개의 scenes으로 나누는 것의 장점은 :

  • loading times과 memory 사용이 줄어든다.
  • 팀 멤버들은 별개의 scenes에 작업할 수 있고, 그러므로 source control 갈등의 개수를 줄인다.
  • 실제로 거대한 open-world games이 가능하다.
  • scene 변환과 screens을 불러오는 것이 만들어서 추가하는 것이 매우 간단하다.
  • 쉽게 "manager" type scripts와 objects를 너의 게임에 추가할 수 있다.
The Process
여기에서, 나는 menu scene, loading scene, game scene을 가지고 Unity의 SceneManager class의 간단한 어플리케이션을 설명할 것이다. 나중에, 나는 asynchronous scene transitions을 포함하는 좀 더 고급 버전을 다룰 것이다.

scene manager를 사용하여 작업하는 프로토타입을 얻기 위해서, 우리는 다음을 할 필요가 있다:
  1. scenes을 설정하고, 그것들에 build settings을 추가한다.
  2. project에 Singleton script를 추가하고, "SceneLoader.cs"라고 불려지는 새로운 script를 생성한다.
  3. 새로운 scenes을 불러올 필요가 있는 scripts를 SceneLoader를 사용하여 추가해라. GUI buttons, in-game scenes, 또는 어디든지에서 처리될 수 있다.
이것은 어떤 순서에서 든지 한 scene or scenes들을 불러오기 위해 SceneLoader class에 대해 간단하고 쉬운 호출로 불러와질 수 있는 scenes들을 만들어낼 것이다.

Scene Setup
scenes의 setup은 매우 간단하다. 우리는 다음에 대한 유사한 포맷을 가진 3개의 scenes이 필요하다;

- Camera, UI button(s)을 포함하는 Menu Scene
- Camera, UI (background) image, Animated sprites/loading bar를 포함하는 Loading        Scene
- Camera, Playable character, UI button (Menu scene으로 돌아가기 위한)를 포함하는 Game Scene

Note : 너는 너의 scenes들을 scene hierarchy에 drag and drop할 수 있고, 한 번에 여러개의 scenes을 보일 수 있다! 이것이 내가 여기에서 한 것이다 - 테스팅을 위해 매우 유용하다.

SceneManager Namespace
Unity의 오래된 버전에서, 어떤 scene management는 Application class를 사용해서 처리됐는데, 이것은 generic static methods의 뒤범벅된 것을 가지고 있다. 고맙게도, Unity는 Scene과 관련된 methods들을 그것들 자신의 namespace로 강화시켰다 - UnityEngine.SceneManagement. 이 namespace는 scenes을 loading하고 unloading하는데 대부분의 유용한 methods들을 포함한다.

Note : Application class에 있는 모든 scene과 관련된 메소드들은 이제 구식이다.

SceneManager는 scenes을 동기적으로/비동기적으로 불러오는 메소드들을 포함한다. 그래서 만약 우리가 추가적으로 노력을 한다면, 우리는 게임이 멈추게 하지 않고 background에서 scenes loading을 할 수 있다. 그래서 코드로 들어가서, SceneLoader라고 불려지는 우리 자신의 클래스를 작성해보자. 우리는 synchronous versions을 작성하여 시작할 것인데, 그것들이 어떻게 작성하고, async versions을 구현하는 것으로 부터 얼마나 많은 이익들을 얻을 수 있는지를 보기 위해서이다.

나는 singleton으로 나의 SceneLoader class를 작성했고, 이것은 MonoBehaviour로부터 상속받는다. 이것은 많은 이유들이 때문에 하게 되었는다; 첫 째로, singletons은 그들 스스로 manager-type classes들에 빌려준다(?), 왜냐하면 너는 일반적으로 한 개의 인스턴스만을 원하기 때문이다. 둘 째로, 내가 사용하는 singleton implementation은 게임 오브젝트가 scene이 unloaded될 때 결코 파괴되지 않는 것을 보장한다. 마지막으로, MonoBehaviour 상속은 그 singleton이 Awake, Start, and Update같은 상속된 메소드들에 대한 접근을 허용하게 한다. 이것은 또한 너가 다른 게임 오브젝트들과 스크립트들을 매니저에 붙이도록 한다. 만약 필요하다면.

그 singleton implementation은 깃헙 저장소에 포함되어있지만, 여기에서 볼 수 있다. 그것은 Unity community에 의해 쓰인 클래스인다 (어떤 다른 좋은 코드를 거기에서 또 참고해라).


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

public class SceneLoader : Singleton<SceneLoader>
{
 // guarantee this will be always a singleton only - can't use the constructor!
 protected SceneLoader() { }

 public void LoadeScene()
 {
  SceneManager.LoadScene("Loading");

  StartCoroutine(LoadAfterTimer());
 }

 private IEnumerator LoadAfterTimer()
 {
  // the reason we use a coroutine is simply to avoid a quick "flash" of the
  // loading screen by introducing an artificial minimum load time
  yield return new WaitForSeconds(2.0f);

  LoadScene("Game");
 }

 private void LoadScene(string sceneName)
 {
  SceneManager.LoadScene(sceneName);
 }
}

이것은 매우 기본적인 구현이지만, 그것은 우리가 원하는 것을 성취하고, 우리는 이제 구성할 기반을 가지게된다. 또한, 우리는 이러한 manager methods들을 singleton Instance를 참조하여 한 script로부터 접근할 수 있다, 이렇게 : SceneLoader.Instance.DisplayLoadingScene();

Note : 너가 모든 scenes들을 build settings에 추가하도록 해라.

LoadScene (line 10)을 호출하는 것은 Loading scene을 load하고, 그러고나서 Game scene을 불러올 것이다. 코루틴이 이 경우에 사용되는 이유는, 간단한 추가될 휴식시간을 허용하게 하기 위해서이다. 이것이 없다면, 그 Game Scene은 불러와질 것이고, 그 플레이어가 적절히 Loading scene을 볼 기회를 갖기전에 보여진다. Unity는 작은 scenes들을 매우 빠르게 불러온다.

너는 아마도 이 코드에 또 다른 문제를 발견할 수 잇다. 우리는 우리의 scenes들의 loading을 추적할 방법이 없다 (적어도, 어떤 tracker/subscriber system의 종류를 만드는 문제들을 하지 않느 이상). 그 게임 scene이 실제로 완료되었는지 알 방법이 없다. 만약 그것이 준비되지 않았다면, 그러면 그것을 불러오는 것은 우리의 loading screen을 얼린것 처럼 보일 것이다. Synchronous methods는 그 main thread를 lock할 것이다 - 즉, 모든 것이 scene이 loading 되는 걸 멈출 때 까지 반응하는 것을 멈출 것이다. 만약 너가 animated loading scene을 가지고 있따면, 이것은 큰 문제이다 (그러나 static image를 보여주는 것은 괜찮을 것이다).

fix? 우리는 물론 asynchronous methods로 넘어간다. 이것은 우리의 코드를 좀 더 복잡하게 만들 것이지만, 그것은 확실히 장기간으로 보상을 해준다.



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

public class SceneLoaderAsync : Singleton<SceneLoader>
{
 // Loading Progreess : private setter, public getter
 private float _loadingProgress;
 public float LoadingProgress { get {return _loadingProgress;} }

 public void LoadScene()
 {
  // kick-off the one co-routine to rule them all
  StartCoroutine(LoadScenesInOrder());
 }

 private IEnumerator LoadScenesInOrder()
 {
  // LoadSceneAsync() returns an AsyncOperation,
  // so will only continue past this point when the Operation has finished
  yield return SceneManager.LoadSceneAsync("Loading");

  // as soon as we've finished loading the loading screen, start loading the game scene
  yield return StartCoroutine(LoadScene("Game");
 }

 private IEnumerator LoadScene(string sceneName)
 {
  var asyncScene = SceneManager.LoadSceneAsync(sceneName);
  
  // this value stops the scene from displaying when it's finished loading
  asyncScene.allowSceneActivation = false;

  while(!asyncScene.isDone)
  {
   // loading bar progress
   _loadingProgress = Mathf.Clamp01(asyncScene.progress / 0.9f) * 100;

   // scene has loaded as much as possible, the last 10% can't be multi-threaded
   if(asyncScene.progress >= 0.9f)
   {
    // we finally show the scene
    asynScene.allowSceneActivation = true;
   }

   yield return null;
  }
 }
}

클래스를 위에서 밑에까지 보아서, 우리는 
























댓글 없음:

댓글 쓰기