이전에 유니티에서 씬을 전환할 때 LoadScene이 아닌 LoadSceneAsync가 권장된다는(? 자주 쓰인다는?) 이야기를 들은 적이 있다. 그 때는 제대로 이해하지 못하고 그런가보다... 했는데
✨이번 프로젝트에서 로딩씬을 담당하면서 씬 전환을 더 뜯어보게 되었다.
🎥 씬 로드
- BaseScene을 상속받는 각 씬의 스크립트
씬 내부에 필요한 오브젝트를 미리 넣어두는 것이 아니라, 씬이 시작될 때 프리팹을 로드해서 쓰는 방식을 활용하고 있다.
하나의 씬에 여러 UI나 오브젝트가 존재해야 할 때 인스펙터창에 일일이 넣다 보면 오류가 생기게 되고, 씬이 바뀔 경우(테스트씬 -> 본씬)에 모두 다시 지정해야 하기 때문에 협업 상황에서도 효율이 떨어진다.
따라서 각 씬의 스크립트들이 각 씬이 시작될 때, 씬에서 나갈 때 필요한 작업들을 모아서 저장한다.
그리고 BaseScene부터 파생된 클래스까지 Clear 메서드를 가지고 있는데, 이 때 씬을 나가면서 매니저 클래스들에 전달해주어야 하는 정보들을 한 번에 전달가능하도록 하고 있다.
🎬 씬 관리
- SceneManagerM (유니티엔진의 SceneManager 말고 만들어 활용 중인 매니저)
각 씬의 스크립트들은 씬에 대한 정보를 Enum으로 가지고 있다.
이 Enum 값은 Scene의 실제 이름과 동일해야 하며, 각 씬을 구분하기 위해 쓰인다.
Enum을 활용하다보니 각 씬에 대한 다른 처리를 위해 조건을 달아줄 때 직관적이면서도 string보다 효율적으로 처리할 수 있게 된다.
Scene의 실제 이름과 동일하다는 조건 하에, 이 Enum을 string으로 변환해서 실제 씬 전환 (LoadScene, LoadSceneAsync)에서도 활용 가능하다.
또한 현재 씬에서 BaseScene을 기반으로 하는 클래스들을 찾아서 저장한다.
저장한 값을 바탕으로 씬이 전환될 때, 위에서 언급한 해당 클래스의 Clear 메서드를 호출한다.
씬매니저는, 현재 씬에 대한 정보를 저장하고 있으며, 자체적인 씬 전환 메서드를 가진다.
이동하고자 하는 씬 정보를 파라미터로 받고 있는데, 여기서 파라미터를 두 개 받음으로써 로딩씬으로의 전환, 그리고 그 다음 씬을 동시에 호출하는 것이 가능해진다.
📸 씬 전환
- SceneManager.LoadSceneAsync
public void LoadScene(Scenes sceneType, Scenes nextSceneType = Scenes.Unknown)
{
CurrentScene.Clear();
_curSceneType = sceneType;
if (_curSceneType == Scenes.LoadingScene) _nextSceneType = nextSceneType;
SceneManager.LoadScene(_curSceneType.ToString());
}
요것이 씬 매니저의 자체적인 씬 전환 메서드
첫 번째 인자로 받은 _curSceneType의 씬은 일반적인 동기 방식으로 로드된다. (SceneManager.LoadScene)
두 번째 인자는 _nextSceneType 변수에 저장되며, NextSceneType 프로퍼티를 통해 접근 가능하다.
첫 번째 인자로 로딩씬이 들어오면 로딩씬으로 넘어가게 된다.
그러면 로딩씬은 로드되자마자 씬 매니저의 _nextSceneType 정보를 바탕으로 다음 씬을 로드한다.
public AsyncOperation LoadSceneAsync(Scenes sceneType)
{
CurrentScene.Clear();
_curSceneType = sceneType;
return SceneManager.LoadSceneAsync(_curSceneType.ToString());
}
이 때 씬 매니저의 비동기 씬 전환 메서드를 활용하는데, 로딩씬에서의 처리가 일어나는 동시에 다음씬을 로드할 수 있게 된다.
IEnumerator LoadSceneProcess()
{
AsyncOperation op = SceneManagerEx.Instance.LoadSceneAsync(_nextSceneType);
op.allowSceneActivation = false;
float destination = _moveArea.rectTransform.rect.width;
float timer = 0f;
while (!op.isDone)
{
timer += Time.unscaledDeltaTime;
float newX = Mathf.Min(timer, 3f) / 3f * destination - (destination / 2);
_bunny.rectTransform.anchoredPosition = new Vector2(newX, _bunny.rectTransform.anchoredPosition.y);
if (timer > 3)
{
_animator.SetBool(_fallHash, true);
}
if (timer > 5f)
{
op.allowSceneActivation = true;
}
yield return null;
}
}
- 로딩씬의 길이는 5초, 토끼가 움직이는 시간은 3초로 설정
- 다음 씬의 로드가 완료되었더라도 allowSceneActivation을 통해 5초가 지난 후에 실제로 씬이 활성화되도록 설정
- 토끼가 움직일 수 있는 영역을 부모 오브젝트의 크기로 잡아두고, 부모의 왼쪽 끝부터 오른쪽 끝까지 이동하도록
- Mathf.Min(timer, 3f) / 3f는 시간의 경과를 비율로 나타낸 것. 여기에 destination (도착지점까지의 길이)을 곱해서 실제 이동 영역을 반영한다.
- rectTransform의 앵커프리셋을 왼쪽으로 맞춰뒀기 때문에 Mathf.Min(timer, 3f) / 3f * destination에서 destination(부모 길이)의 절반을 빼도록 했다.
- 이 작업을 하지 않으면 부모의 중심을 기준으로 토끼의 위치가 설정되므로 영역을 벗어나게 된다.
이런 식으로 토끼의 rectTransform을 직접 변경하는 노가다를 했는데
끝나니까 팀원분이 그거 슬라이더로 하면 된다고...
왜 그 생각을 못했을까요? 멋슥;;
슬프지만 슬라이더를 쓰지 않고 처음부터 만들었다는 것 자체가! 의미가 있다고 생각!
지금은 로딩씬의 로딩 게이지(토끼의 이동)를 등속으로 변경시키고 있는데 - (애니메이션을 예쁘게 보여주기 위함)
다음 씬이 로드된 정도를 반영하는 방법도 있는 것으로 알고 있다. - AsyncOperation.progress 값을 활용
[Unity3D] 로딩 씬(Loading Scene) 구현하기
로딩 씬(Loading Scene) 구현하기 이 글은 과거 버전의 글로 최신 버전의 글로 다시 작성되었다. 최신 버전의 글은 씬 교체 방식과 커튼 방식, 두 가지로 구현되었다. 본문 게임의 장르와 배경들의 종
wergia.tistory.com
아무튼 실제로 LoadSceneAsync를 써야만 하는 상황에 놓이니 해당 메서드에 대해 더 잘 이해할 수 있었다.
시각적인 효과 외에도 사운드 등의 작업이 끊기지 않게 하기 위해서는 LoadSceneAsync를 활용하는 것이 좋겠다는 생각이 들었다.
20000 총총..
'👾 내일배움캠프 > 🎮 TIL & WIL' 카테고리의 다른 글
내일배움캠프 71일차 TIL - IEnumerable & IEnumerator (코루틴) (1) | 2023.11.24 |
---|---|
내일배움캠프 61일차 TIL - 델리게이트 / 이벤트 / 람다 (0) | 2023.11.09 |
내일배움캠프 60일차 TIL - 플레이어와 주방기구 상호작용 (1) | 2023.11.08 |
내일배움캠프 55일차 TIL - InputSystem과 가상 조이스틱 (On-Screen Button / On-Screen Stick) (0) | 2023.10.31 |
내일배움캠프 54일차 TIL - struct와 class (0) | 2023.10.29 |