Chronicles of cyberpunk — architecture

Home  /  Chronicles of Cyberpunk  /  Chronicles of cyberpunk — architecture

В игре всего 9 сцен, на которых непосредственно происходит игра + 3 дополнительные сцены: титры, главное меню, preload. Мы постоянно перемещаемся между этими сценами (как показано на рисунке), выполняем миссии. На каких-то появляемся один раз, на каких-то больше. И есть определенные сложности..

Когда в Unity ты загружаешь новую сцену, то все, что было на предыдущей, удаляется из памяти и ты не можешь обратиться к ее компонентам. Например, на одной сцене ты взял ключ от двери, которая находится на другой сцене. И придя на эту другую, нужно как-то сообщить игре, что у нас уже есть ключ. Вот с какими проблемами я столкнулся:

  • если игрок из главной сцены (города) зашел на второстепенную сцену, то когда он выйдет обратно, нужно разместить его у входа на эту сцену. На главной сцене нужно определять, откуда мы вышли;
  • если мы приходим на определенную сцену в первый раз, то выполняется определенная последовательность действий. При ее втором посещении нужно отключить определенные группы объектов и включить актуальные для текущей миссии (например, на сцене появились новые люди). Нужно определять, какая сейчас выполняется миссия.

Для этих целей я использую скрипт GameManager, который содержит функцию DontDestroyOnLoad(), которая инициализируется на самой первой сцене Preload. Инициализация этой функции — единственное назначение сцены Preload. После нее сразу автоматически загружается следующая. Объект, который содержит скрипт, помечен тегом Acts, поэтому, когда загружается новая сцена и инициализируются ее объекты, движок быстро находит нужный объект по тегу, после чего с ним можно работать. Такой скрипт существует на протяжении всей игры на всех сценах в единственном экземпляре. В нем я и храню все нужные состояния: номер сцены, на которой мы находимся, номер текущей миссии и т.п.

    public string nameOfLastLoadedScene;

    private GameObject player;
    public int currentActNumber { get; set; } // инкремент в конце каждого акта

                         // Главное меню
                         // Preload
    private Act_0 act0;
    private Act_1 act1;
    private Act_2 act2;
    //..
    private Act_2 act28;

   void Start ()
    {
        DontDestroyOnLoad(this);
        SceneManager.LoadScene("Home");
    }

    void OnLevelWasLoaded()
    {
        player = GameObject.FindGameObjectWithTag("PlayerOnMainScene");
        PlacingPlayerNearHouse();
    }

    // При загрузке главной сцены размещаем игрока рядом с домом, откуда он выходил
    void PlacingPlayerNearHouse()
    {
        switch (nameOfLastLoadedScene)
        {            
            case "": player.transform.position = new Vector3(-4.42f, 0.65f, 49.26f); break;           
            case "": player.transform.position = new Vector3(-14.8f, 0.65f,  -7.2f); break;
            case "": player.transform.position = new Vector3( 0.44f, 0.65f,  6.89f); break;
            case "": player.transform.position = new Vector3(36.63f, 0.65f,   6.9f); break;
            case "": player.transform.position = new Vector3( 9.45f, 0.65f,-36.73f); break;
            case "": player.transform.position = new Vector3(-0.32f, 0.65f, -9.34f); break;
            default: break;
        }
    }

В зависимости от номера текущего акта загружаем определенное поведение

    void Update()
    {
        ActManager();
    }

    // Запускать определенный акт в зависимости от currentActNumber
    void ActManager()
    {
        Debug.Log("currentActNumber: " + currentActNumber);
        if (SceneManager.GetActiveScene().name != "PRELOAD" &&
            SceneManager.GetActiveScene().name != "STARTSCREEN")
        {
            GameObject objWithActScripts = GameObject.FindGameObjectWithTag("Acts");

            switch (currentActNumber)
            {
                case 0: act0 = objWithActScripts.GetComponent<Act_0>();
                        act0.StartAct(); break;
                case 1: act1 = objWithActScripts.GetComponent<Act_1>();
                        act1.StartAct(); break;
                //..
            }
        }
    }

И такая схема позволяет программировать последовательность шагов для каждого акта отдельно

    public void StartAct1(MonoBehaviour mb)
    {
        switch (stepNumber)
        { 
            case 0:
                // проигрываем анимацию открытия глаз
                mb.StartCoroutine(OpenCloseEyesAnimation());
                break;
            case 1:
                // отображаем подсказку "нажмите любую клавишу, чтобы проснуться"
                ShowTip(contentToPrint.tipsTasks[0]);
                stepNumber++;
                break;
            case 2:
                // при нажатии на любую кнопку очищаем текст подсказки,
                // анимация закрытия глаз, аудио зевания
                if (Input.anyKey)
                {
                    audioYawn.Play();
                    ShowTip("");
                    mb.StartCoroutine(OpenCloseEyesAnimation());
                }
                break;
            case 3:
                // отключаем игрока в кровати и включаем основного игрока,
                // анимация открытия глаз
                PlayerInBedDisable();
                mb.StartCoroutine(OpenCloseEyesAnimation());
                break;
            case 4:
                // блокируем перемещение, отображаем диалог с дроном
                ShowUIAndPrintMessage(0, 0);                
                stepNumber++;
                break;
            //..
        }
    }

P.S. Я понимаю, что этот код кривой и более опытный программист может сделать его в сто раз лучше, но я довольно долго над ним сидел, чтобы он начал работать. Теперь он работает и я рад

Comments are closed.