공부/인프런 - Rookiss

Part 3-8-1. Scene : Scene Manager

셩잇님 2023. 8. 26. 12:40
반응형

 

 

Scene

구조도

 

여태까지는 📜PlayerController에 Manager로부터 GameManager 스러운 일들을 실행시켰는데, 플레이어도 씬에 동적으로 생성되는 존재라면 📜PlayerController에 Manager를 호출하는 것은 바람직하지 않을 것이다. 생각해보면 이는 당연한 것이다. 🤔 따라서 게임 실행 내내 살아있는 선봉대 역할을 할 존재를 만들고 그에게 Manager를 호출하는 일을 맡겨야 한다. 이 선봉대 역할을 📜Scene에서 하는 것이다.

씬들. 각각 씬들만의 함수

 

📜BaseScene

  • 📜GameScene
  • 📜LoginScene

 

 

여러 씬들 한꺼번에 관리 👉 📜SceneManager

 

빈 오브젝트 @Scene 만들어서 그 곳에 Scene 종류가 되는 스크립트(📜GameScene 혹은 📜LoginScene) 붙임. 이 @Scene는 동적으로 생성되는 존재가 아니게 할 것.

 

 

그리고 씬 전환을 하려면 씬들이 반드시 Build Setting 에 등록이 되어 있어야 한다. 이 후 로그인씬에서 아이디와 비밀번호 입력 후 이를 서버에 보내서 서버로부터 OK 사인을 받으면 이제 인게임 씬으로 넘어가는 이런식으로 구현하면 된다.

📜Define

    public enum Scene
    {
        Unknown, // 디폴트
        Login, // 로그인 화면 씬
        Lobby, // 로비 씬
        Game, // 인게임 씬
    }

 

📜BaseScene

여러가지 종류의 씬들이 상속받을 스크립트. 즉, 씬들이 공통적으로 갖고 있을 부분 모음

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public abstract class BaseScene : MonoBehaviour
{
    public Define.Scene SceneType { get; protected set; } = Define.Scene.Unknown; // 디폴트로 Unknow 이라고 초기화

	void Awake()
	{
		Init();
	}

	protected virtual void Init()
    {
        Object obj = GameObject.FindObjectOfType(typeof(EventSystem));
        if (obj == null)
            Managers.Resource.Instantiate("UI/EventSystem").name = "@EventSystem";
    }

    public abstract void Clear();
}

 

SceneType

1. 이 씬은 어떤 타입의 씬인지를 알려줄 정보 (from 📜Define의 Scene enum)

2. 자식 씬들에게 상속

3. get 은 ScreenType 프로퍼티의 접근지정자 따라 public 하게, set 은 protected 한 프로퍼티로 설정

 

Init

1. 가상 함수로 자식 씬들에서 base.Init()으로 부분적으로 공통적으로 실행시켜야 하는 부분들을 정의

2. UI는 반드시 EventSystem이 필요하기 때문에 꼭! 만들어주어야 한다. EventSystem을 만들어주는 작업.

3. EventSystem도 그냥 프리팹으로 만들어버리고 이를 생성시켜 준다.

 

 

Clear
1. 추상 함수로 Clear 내용은 전적으로 자식 씬들에게 맡김
2. 자식들마다 다 각자의 다른 방식으로 오버라이딩

 

Start 대신 Awake 를 사용하는 이유

Awkae는 Start보다 먼저 실행되며, 오브젝트만 활성화되어 있다면 스크립트가 꺼져있어도 실행된다. 또한 Awake 는 자식들에게도 상속된다. 😎

 

 


 

 

📜 GameScene

“GameScene” 씬의 @Scene 오브젝트에 붙는다.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameScene : BaseScene
{
	protected override void Init() // 상속 받은 Awake() 안에서 실행됨. "GameScene"씬 초기화
    {
        base.Init(); // 📜BaseScene의 Init()

        SceneType = Define.Scene.Game; // 📜GameScene의 씬 종류는 GameScene

        Managers.UI.ShowSceneUI<UI_Inven>(); // 인벤토리 UI 생성

		for (int i = 0; i < 5; i++) // 📜GameScene의 씬 초기화시 UnityChan 프리팹을 5개 생성한다! 
			Managers.Resource.Instantiate("UnityChan");
	}

    public override void Clear()
    {
        
    }
}

 

📜 LoginScene

“LoginScene” 씬의 @Scene 오브젝트에 붙는다.

 

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

public class LoginScene : BaseScene
{
    protected override void Init()  // 상속 받은 Awake() 안에서 실행됨. "LoginScene"씬 초기화
    {
        base.Init();

        SceneType = Define.Scene.Login; // 📜LoginScene의 씬 종류는 LoginScene

        List<GameObject> list = new List<GameObject>();

        for (int i = 0; i < 5; i++)
            list.Add(Managers.Resource.Instantiate("UnityChan"));

        foreach (GameObject obj in list)
        {
            Managers.Resource.Destroy(obj);
        }
    }

    private void Update() 
    {
        if (Input.GetKeyDown(KeyCode.Q))
        {
            Managers.Scene.LoadScene(Define.Scene.Game);
        }
    }

    public override void Clear()
    {
        Debug.Log("LoginScene Clear!");
    }
}

 

Update
키보드의 Q 입력이 들어오면 📜SceneManagerEx(밑에 참고)의 LoadScene 함수로 “GameScene” 씬을 로드하도록 한다.

 

 


 

 

SceneManager
📜SceneManagerEx 👉 이미 SceneManager가 유니티 엔진에 정의되어 있기 때문에 Ex 붙여서 구분해준다.

 

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

public class SceneManagerEx
{
    public BaseScene CurrentScene { get { return GameObject.FindObjectOfType<BaseScene>(); } }

    string GetSceneName(Define.Scene type)
    {
        string name = System.Enum.GetName(typeof(Define.Scene), type); // C#의 Reflection. Scene enum의 
        return name;
    }

	public void LoadScene(Define.Scene type)
    {
        Managers.Clear();

        SceneManager.LoadScene(GetSceneName(type)); // SceneManager는 UnityEngine의 SceneManager
    }

    public void Clear()
    {
        CurrentScene.Clear();
    }
}

 

CurrentScene는 그저 GameObject.FindObjectOfType<BaseScene>(); 리턴 값을 받으면 땡! 📜GameScene 혹은 📜LoginScene이 할당 될 것이다. 둘 다 📜BaseScene의 자식들이기 때문에.

📜Manager

SceneManagerEx _scene = new SceneManagerEx();
public static SceneManagerEx Scene { get { return Instance._scene; } }

 

 

 

반응형