Unity 6.3
0 онлайн 78 гостей 3 в системе
Вход
Программирование в Unity Шаг 89 из 210

Перейти в режим воспроизведения с отключенной перезагрузкой домена

Домен скриптов, также известный как область применения, или просто домен, является основной особенностью UnityДомен представляет собой изолированную секцию памяти, предназначенную для конкретного приложения, которая содержит компилированные типы, требуемые приложением, сгруппированные в логические единицы, называемые ассамблеи. Он также содержит данные, отражающие текущее состояние приложения: значения переменных и ссылки на объекты в различных разделах управляемая память.

По умолчанию при входе в режим Play Unity перезагружает домен, чтобы сбросить состояние приложения. Сброс состояния перед входом в Play часто желателен: приложение стартует так же, как при запуске новой сборки. Например, статические счётчики, увеличенные в предыдущем сеансе Play, в следующем снова начнут с нуля. Однако перезагрузка домена занимает время и ухудшает скорость итераций, если вы часто переключаетесь между режимами Edit и Play. Чтобы ускорить итерации, можно отключить перезагрузку домена при входе в режим воспроизведения, но вы должны затем вручную сбросить состояние из кода.

Примечание: Unity также выполняет перезагрузку домена как часть обновления базы данных ассетов, когда он обнаруживает изменения в скриптах. Это происходит даже в том случае, если перезагрузка домена при входе в режим Игры отключена. Более подробную информацию о том, когда и как происходит обновление базы данных ассетов, см. в Обновление базы данных активов.

Последствия отключения перезагрузки домена при входе в режим воспроизведения

При отключении перезагрузки домена:

  • Несериализированные поля сохраняют значения, присвоенные им в режиме воспроизведения, при возвращении в режим редактирования. Это относится к полям всех типов скриптов, включая MonoBehaviours (включая те, что на префаб ассетах), ScriptableObjects, и ваши собственные пользовательские типы C#. Подробную информацию о том, что является и не является сериализированным в различных контекстах, см. в Правила сериализации.
  • Статические переменные сохраняют свои значения между сеансами режима воспроизведения.
  • Статические события сохраняют своих зарегистрированных подписчиков между сеансами режима воспроизведения.
  • Нет дополнительных вызовов OnDisable или OnEnable для скриптов, помеченных [ExecuteInEditMode] или [ExecuteAlways].

Чтобы компенсировать такое сохранение данных между сеансами режима воспроизведения и входить в режим воспроизведения с чистым состоянием приложения, вы должны сбросить состояние из кода.

Более подробную информацию о последствиях отключения домена и перезагрузки сцены см. в Подробности отключения домена и перезагрузки сцены.

Сброс состояния из кода

Когда перезагрузка домена отключена, значения static fields а обработчики, назначенные статическим событиям, сохраняются между запусками режима Play. В примере кода ниже есть статический счётчик, который увеличивается при нажатии любой клавиши. Код также регистрирует метод-обработчик статического события EditorApplication.playModeStateChanged.

Если перезагрузка домена включена, Unity переинициализирует этот код при входе в режим воспроизведения, стирая состояние предыдущего запуска, включая значение счётчика и регистрацию обработчика событий. Если перезагрузка домена отключена, значение счётчика и регистрация обработчика событий сохраняются с предыдущего запуска. При следующем запуске режима воспроизведения счётчик начинается со значения, на котором закончился предыдущий запуск, а метод-обработчик вызывается несколько раз на одно возникновение события, порождая в консоли несколько сообщений «Выход из режима воспроизведения!».

// Copy-paste this code into a MonoBehaviour script attached to a GameObject in your project.
// Run it with domain reload enabled and then with domain reload disabled and note the different behavior.

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

public class StateResetExample : MonoBehaviour
{
    // With domain reload disabled this counter won't reset to zero on exiting Play mode
    static int counter = 0;

    void Start()
    {
    // Register handler
#if UNITY_EDITOR
        EditorApplication.playModeStateChanged += OnExitPlayMode;
#endif    
    }

    void Update()
    {
        if (Input.anyKeyDown)
        {
            counter++;
            Debug.Log("Counter: " + counter);
        }
    }
#if UNITY_EDITOR
    private static void OnExitPlayMode(PlayModeStateChange state)
    {
        if(state == PlayModeStateChange.ExitingPlayMode)
        {
            // With domain reload disabled this message prints multiple times after the first Play mode run
            Debug.Log("Exiting Play mode!");
        }
    }
#endif
}

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

Сброс состояния при выходе из режима воспроизведения

Зачастую эффективнее сбрасывать состояние при выходе из режима воспроизведения, а не при входе в него. EditorApplication.playModeStateChanged событие и его последствия ExitingPlayMode значение перечисления, чтобы перехватить событие выхода из режима Play и сбросить состояние в этот момент. В примере ниже обработчик события выхода из Play отменяет собственную регистрацию, но здесь же можно отменить регистрацию и любых других статических обработчиков событий:

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

public class StateResetOnExit : MonoBehaviour
{
    static int counter = 0;

    void Start()
    {
#if UNITY_EDITOR
        EditorApplication.playModeStateChanged += OnExitPlayMode;
#endif        
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.anyKeyDown)
        {
            counter++;
            Debug.Log("Counter: " + counter);
        }
    }

#if UNITY_EDITOR
    private static void OnExitPlayMode(PlayModeStateChange state)
    {
        if(state == PlayModeStateChange.ExitingPlayMode)
        {
            Debug.Log("Exiting Play mode!");
            Debug.Log("Unregistering handler.");
            // Unregister the handler so it doesn't affect the next Play mode run
            EditorApplication.playModeStateChanged -= OnExitPlayMode;
            Debug.Log("Resetting counter.");
            // Reset the counter so it starts from 0 on the next Play mode run
            counter = 0;
        }
    }
#endif
}

EditorApplication.playModeStateChanged находится в пространстве имен UnityEditor, поэтому работает только в режиме Play в Unity Editor, а не в автономной сборке Player.

Если ваш код выполняется не только в режиме Play, но и в режиме Edit, полагаться на сброс состояния при выходе из Play нельзя. Код может изменить статическую переменную в режиме Edit, поэтому её необходимо сбрасывать при входе в режим Play вместо этого.

Примечание: Для скриптов, выполняющихся в режиме Редактирования, отключение перезагрузки домена пропускает MonoBehaviour.OnDisable и отключение перезагрузки сцены пропускает MonoBehaviour.OnDestroy, из-за чего эти методы не подходят для сброса состояния в таких скриптах. Подробнее см. Подробности отключения перезагрузки домена и сцены.

Сброс состояния при входе в режим воспроизведения

Состояние можно сбрасывать и при входе в режим Play. Это удобная альтернатива, если вы столкнулись с проблемами конкретной платформы при перехвате события выхода из Play или если ваш код выполняется и в режиме Edit. Для наглядности в примере ниже при входе в режим Play сбрасываются статические переменные и отменяется регистрация статических обработчиков событий. Хорошая практика — всегда отменять регистрацию статических обработчиков событий при выходе из режима Play. Ожидание следующего сеанса режима воспроизведения может вызвать проблемы со ссылками на объекты, которые приведут к отмене регистрации неправильного обработчика.

Unity имеет собственные атрибуты инициализации как в пространствах имен UnityEngine, так и UnityEditor, которые можно использовать для инициализации, в том числе для ручного сброса состояния. Какой атрибут применять, зависит от того, выполняется ли код в режиме редактирования или в режиме воспроизведения. Чтобы сбрасывать состояние при входе в режим воспроизведения для скриптов плеера (времени выполнения), подойдёт атрибут [RuntimeInitializeOnLoadMethod] с параметром RuntimeInitializeLoadType.SubsystemRegistration:

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

public class StateResetOnEnter : MonoBehaviour
{
    static int counter = 0;

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
    static void Init()
    {
        Debug.Log("Unregistering handler.");
        // Unregister the handler so it doesn't affect the next Play mode run
        EditorApplication.playModeStateChanged -= OnExitPlayMode;
        Debug.Log("Resetting counter.");
        // Reset the counter so it starts from 0 on the next Play mode run
        counter = 0;  
    }

    void Start()
    {
#if UNITY_EDITOR
        EditorApplication.playModeStateChanged += OnExitPlayMode;
#endif
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.anyKeyDown)
        {
            counter++;
            Debug.Log("Counter: " + counter);
        }
    }
#if UNITY_EDITOR
    private static void OnExitPlayMode(PlayModeStateChange state)
    {
        if(state == PlayModeStateChange.ExitingPlayMode)
        {
            Debug.Log("Exiting Play mode!");
        }
    }
#endif
}

[RuntimeInitializeOnLoadMethod] находится в пространстве имен UnityEngine, поэтому доступен только в режиме воспроизведения. Для скриптов, доступных только редактору, таких как настраиваемые окна редактора или инспекторы, использующие статику, вы можете выполнить одно из следующих действий:

Примечание: В то время как предыдущие примеры кода используют MonoBehaviour классов, вы также можете использовать эти атрибуты инициализации в скриптах любого типа, включая те, которые наследуют от ScriptableObject или ваша собственная таможня C# типов.

Дополнительные ресурсы