Scene은 게임의 오브젝트가 포함되어있다. 각 씬에는 환경과 장애물, 장식을 배치하고 게임을 세세하게 디잔인하고자 한다.
우리는 이러한 씬을 코드를 통해서 옮겨다닐라고 한다.
Scene을 변경하는 것은 이미 Unity에서 모두 구현되어있다.
SceneManager (UnityEngine.SceneManagement)을 이용하면 된다.
Method | Description |
---|---|
GetActiveScene | 현재 활성화된 씬을 리턴한다. |
GetSceneByName | 이름으로 BuildSettings 창에 표시되는 씬을 찾는다. 이때 .unity 확장자는 없어야 한다. |
LoadScene | 씬을 로드한다. 이때 Build Settings에서 index나 name으로 찾는다. |
LoadSceneAsync | 백그라운드에서 비동기적으로 씬을 로드한다. |
MergeScenes | source Scene에서 Dest Scene으로 합친다. |
MoveGameObjectToScene | 게임오브젝트를 현재 씬에서 새로운 씬으로 이동시킨다. |
SetActiveScene | 활성화시킬 씬을 선택한다. |
UnloadSceneAsync | 씬에 관련된 모든 게임 오브젝트를 비동기적으로 삭제한다. |
위의 메소드는 static 메서드로 일반적으로 호출할 수 있다.
Events | Description |
---|---|
activeSceneChanged | active scene이 변경될 때 발생하는 이벤트이다. |
sceneLoaded | 로드가 완료됐을 때 호출되는 이벤트이다. |
sceneUnloaded | 언로드가 완료됐을 때 호출되는 이벤트이다. |
해당 이벤트 사용법은 delegate로, SceneManager.sceneLoaded += 함수, 와 같이 사용하면 된다.
예로 Scene 1, 2를 만들어보겠다.
각 Scene에 스크립트를 만들겠다.
// Scene1Script
using UnityEngine;
using UnityEngine.SceneManagement;
public class Scene1Script : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
Debug.Log("Load Scene1");
SceneManager.LoadScene("Scene2");
}
}
// Scene2Script
using UnityEngine;
public class Scene2Script : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
Debug.Log("Load Scene2");
}
}
실행시켜보면 에러가 뜰 것이다. 이는 BuildSetting에서 Scene 목록을 선택안해줬기때문이다.
다음과 같이 Scene을 끌어당겨 추가 후 빌드해주면 된다.
그리고 Scene1으로 실행시켜보면 Scene2로 바로 넘어가진다.
이처럼 LoadScene은 바로 로드시켜준다.
하지만 LoadSceneAsync를 응용하면 게임 시작할 때 미리 게임 오브젝트들을 조금씩 갖고오고 원할 때 넘어갈 수 있다.
// Scene1Script.cs
using UnityEngine;
using UnityEngine.SceneManagement;
public class Scene1Script : MonoBehaviour
{
AsyncOperation asyncOperation;
// Start is called before the first frame update
void Start()
{
Debug.Log("Load Scene1");
asyncOperation = SceneManager.LoadSceneAsync("Scene2");
asyncOperation.allowSceneActivation = false;
// 장면이 준비된 즉시 장면이 활성화 여부이다.
SceneManager.sceneLoaded += (Scene scene, LoadSceneMode mode) =>
{
Debug.Log($"Finish Loading {scene.name}");
};
}
private void OnDisable()
{
asyncOperation.allowSceneActivation = true;
}
}
실행 시 다음과 같이 되어있다.
이제 스크립트르 갖고있는 오브젝트를 Disable로 만들어보자.
참고로 AsyncOperation.isDone()을 통해서 Load가 완료됐는지 확인할 수 있다.
그렇다면 Scene Manager가 이미 구현되어있는데 왜 만드느냐?
그것은 Scene의 초기세팅과 관련된 부분을 Scene의 스크립트 및 Scene GameObject를 만들어서 코드로 관리하기 위해서이며, Scene Manager로 Load 시에 String으로 넘기는 것에 실수를 방지하기 위해서 enum으로 관리하기 위해서이다.
public class Define
{
public enum Scene
{
Unknown,
Scene1,
Scene2
}
}
먼저 다음과 같이 Scene과 이름이 똑같은 Enum을 만든다.
그리고 Scene에 관련된 스크립트를 작성하겠다.
// BaseScene.cs
using UnityEngine;
public abstract class BaseScene : MonoBehaviour
{
public Define.Scene SceneType { get; protected set; } = Define.Scene.Unknown;
void Awake()
{
Init();
}
protected virtual void Init()
{
// 모든 씬에서 공통적으로 처리해야 하는 것들 코드
// 예를 들어 UI의 EventSystem같은 것을 씬에 없으면 추가.
}
public abstract void Clear();
}
// Scene1.cs
public class Scene1 : BaseScene
{
protected override void Init()
{
base.Init();
SceneType = Define.Scene.Scene1;
// Scene1에서 처리할 내용.
}
public override void Clear()
{
// Scene 종료 시 처리할 내용.
}
}
// Scene2.cs
public class Scene2 : BaseScene
{
protected override void Init()
{
base.Init();
SceneType = Define.Scene.Scene2;
// Scene2에서 처리할 내용.
}
public override void Clear()
{
// Scene 종료 시 처리할 내용.
}
}
해당 스크립트는 꼭 Scene에 추가되어있어야 한다.
using UnityEngine;
using UnityEngine.SceneManagement;
public class SceneManagerEX : MonoBehaviour
{
public BaseScene CurrentScene { get { return GameObject.FindObjectOfType<BaseScene>(); } }
public void LoadScene(Define.Scene type)
{
Clear();
SceneManager.LoadScene(getSceneName(type));
}
public string getSceneName(Define.Scene type)
{
return System.Enum.GetName(typeof(Define.Scene), type);
}
public void Clear()
{
CurrentScene.Clear();
}
}