Menu
CoddyTech

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

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

Загрузка сцены уничтожает все объекты старой сцены, но некоторые вещи должны сохраняться на протяжении всей игры, например монеты, которые игрок переносит с уровня на уровень. 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, обнулив количество монет. Благодаря проверке новый объект обнаруживает, что Instance уже задан, уничтожает себя и останавливается, а сессия, прошедшая через уровни, сохраняется.

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

challenge icon

Задание

Легко

Сохрани один таймер работающим при каждой загрузке сцены.

  1. В Awake(), если Instance != null, вызови Destroy(gameObject); и return;. Иначе присвой значение Instance и вызови DontDestroyOnLoad(gameObject);.
  2. При загрузке уничтожается текст старого уровня, поэтому в Update(), перед строкой с текстом, если clockText == null, присвой clockText = GameObject.Find("ClockText").GetComponent<TMP_Text>();.

Нажми «Играть», перейди на Level2 и обратно: один таймер продолжит отсчёт. Затем нажми «Проверить».

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

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"));
    }
}

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

Потренируйтесь самостоятельно: Песочница Unity