Menu
CoddyTech

Сохранить между сценами

Часть раздела Создание настоящих игр путешествия по Unity на Coddy. Урок 20 из 56.

Загрузка сцены уничтожает каждый объект старой сцены, но некоторые вещи должны сохраняться на протяжении всей игры, например монеты, которые игрок переносит с уровня на уровень. DontDestroyOnLoad(gameObject) перемещает объект из его сцены в отдельную сцену, которая в Hierarchy во время игры называется DontDestroyOnLoad и которую загрузки сцен никогда не затрагивают. Это работает только с корневым объектом: для дочернего объекта Unity выдаёт предупреждение и ничего не сохраняет. Дочерние объекты сохраняемого объекта остаются вместе с ним, а объекты, на которые он только ссылается, — нет: после загрузки поле, которое ссылалось на объект старой сцены, содержит уничтоженный объект, который при сравнении равен null.

public class GameSession : MonoBehaviour
{
    public static GameSession Instance;
    public int coins;

    void Awake()
    {
        if (Instance != null)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
        DontDestroyOnLoad(gameObject);
    }
}

Этот GameSession находится в сцене Menu. Во время игры происходит переход из Menu в Level1, а затем обратно в Menu, и Menu заново создаёт все свои объекты, включая новый GameSession, тогда как сохранённый экземпляр всё ещё существует. Без проверки в Awake было бы два экземпляра, и новый присвоил бы Instance значение с 0 монетами. С проверкой новый экземпляр обнаруживает, что Instance уже задан, уничтожает себя и завершает работу, а экземпляр сессии, прошедший через уровни, остаётся.

return здесь важен, потому что Destroy дожидается конца кадра. Без него оставшаяся часть Awake всё равно выполнилась бы для нового экземпляра: Instance = this; направил бы все скрипты на объект, который вскоре будет уничтожен.

challenge icon

Задание

Легко

Секундомер для спидрана. Объект RunClock в Level1 отсчитывает секунды всего прохождения и записывает промежуточное время каждый раз, когда уровень пройден. Золотая цель Level1 загружает Level2, а цель Level2 снова загружает Level1, и каждая цель сначала вызывает RunClock.Instance.Split(). У каждого уровня есть собственный HUD с ClockText; в Inspector объекту RunClock назначен тот, что находится в Level1. Стрелки перемещают Player. Измени RunClock.cs, чтобы:

  • секундомер продолжал работать при каждой загрузке сцены, не начиная отсчёт заново;
  • время всегда отображалось в ClockText открытого уровня в формате Time: 12.3;
  • при возвращении в Level1, где есть собственный RunClock, оставался только один секундомер — тот, который уже работал, — и именно он был RunClock.Instance.

Нажми Play, перейди в Level2 и вернись, затем снова пройди Level1 и нажми Check.

Попробуйте сами

using UnityEngine;
using TMPro;

public class RunClock : MonoBehaviour
{
    public static RunClock Instance;
    public TMP_Text clockText;
    private float seconds;

    void Awake()
    {
        Instance = this;
    }

    void Update()
    {
        seconds += Time.deltaTime;
        clockText.text = "Time: " + seconds.ToString("0.0");
    }

    public void Split()
    {
        Debug.Log("Split: " + seconds.ToString("0.0"));
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Создание настоящих игр