Многократная десериализация MonoBehaviour

Программирование на Юнити.

Многократная десериализация MonoBehaviour

Сообщение Blizzard_jedi 30 авг 2018, 12:36

Добрый день!
Бьюсь над нижеобозначенной проблемой уже больше месяца. Нигде ничего по ней найти не смог.
В общем, есть приблизительно следующий код:

Синтаксис:
Используется csharp
[System.Serializable]
public class SerializableDataHolder
{
  //...
}

public class Foo : MonoBehaviour, ISerializationCallbackReceiver
{
  //...

  void Start ()
  {
    //...
  }

  public void OnBeforeSerialize()
  {
    // Prepare mSerializableData for serialization
  }

  public void OnAfterDeserialize()
  {
    // Finalize serialization
  }

  [SerializeField]
  private SerializableDataHolder mSerializableData;
}
 


Foo вешается на (это важно) единственный объект в сцене. Ради чистоты эксперимента я даже пробовал создавать новую чистую сцену и тестировался на ней.

Код прекрасно работал в более старых версиях юнити. Сейчас стало происходить что-то странное. Итак, запуск всего этого дела под дебаггером показал:
1) При запуске игры программа трижды попадает в брэйкпоинт в OnAfterDeserialize.
2) В первый из этих трёх раз всё везде правильно, mSerializableData загружена правильно, Foo правильный. А вот во второй и третий разы уже даже mSerializableData грузится неверно.
3) Программа попадает в брэйкпоинт в функции Start только на третий раз.

Что там творится, я решительно не понимаю. Помогите, пожалуйста. Может, есть какие-то идеи касательно того, как в целом работает десериализация объектов сцены в Юнити и где там может что-то не так пойти.
Blizzard_jedi
UNITрон
 
Сообщения: 235
Зарегистрирован: 21 июн 2013, 00:50
Откуда: Москва

Re: Многократная десериализация MonoBehaviour

Сообщение seaman 30 авг 2018, 22:11

В блоге Юнити есть несколько статей по сериализации в дополнение к мануалу.
https://blogs.unity3d.com/ru/2012/10/25 ... alization/
https://blogs.unity3d.com/ru/2014/06/24 ... -in-unity/
https://blogs.unity3d.com/ru/2015/02/03 ... ed-fields/
https://blogs.unity3d.com/ru/2016/06/06 ... unity-5-4/
Что на самом деле у Вас происходит не скажу. Могу (если не забуду) завтра посмотреть.
seaman
Адепт
 
Сообщения: 8352
Зарегистрирован: 24 янв 2011, 12:32
Откуда: Самара

Re: Многократная десериализация MonoBehaviour

Сообщение Blizzard_jedi 31 авг 2018, 12:41

seaman писал(а):В блоге Юнити есть несколько статей по сериализации в дополнение к мануалу. (...)


Спасибо, почитаю! Вчера обновился до последней версии Юнити. Приведу ниже дополнительные наблюдения.

Обозначим события попадания в брейкпоинт в OnAfterDeserialize (описанные в первом сообщении темы) через A1, A2 и A3. Событие с меньшим индексом случается раньше. Тогда:
1) Всегда либо в A1, либо в A2 всё десериализовано верно. Чаще это A1, но бывает, что A1 неверно, а A2 верно. В А3 всегда десериализация неверная.
2) А1 и А2 всегда происходят одно за другим, а А3 всегда происходит после приличной паузы. Такое впечатление, что Юнити грузит сцену, потом грузит её заново.

Ещё такое дополнение: у Foo есть довольно нетривиальный кастомный инспектор, который напрямую работает с полем Foo.mSerializableData. Я пробовал заполнять Foo данными, затем отключать инспектор, закомментировав код в OnInspectorGUI (), и затем грузить сцену - проблема не исчезла.

И ещё дополнение: SerializableDataHolder содержит нетривиальные данные, которые я руками сериализую с помощью BinaryFormatter. Впрочем, насколько я понимаю, это не объясняет, почему Foo загружается трижды. К тому же, одна из этих загрузок успешная, то есть схема-то у меня вроде как вполне рабочая.
Blizzard_jedi
UNITрон
 
Сообщения: 235
Зарегистрирован: 21 июн 2013, 00:50
Откуда: Москва

Re: Многократная десериализация MonoBehaviour

Сообщение Blizzard_jedi 20 сен 2018, 20:23

Совершенно случайно я обнаружил проблему и нашёл решение. Спешу поделиться. Чтобы на мои грабли больше никто не наступал.

Итак. Когда-то, ещё в прошлых версия юнити я сочинил такой вот простенький (и безобидный) скриптец:

Синтаксис:
Используется csharp
        public class InGameInterfaceRunScript : MonoBehaviour, IInGameInterfaceElement
        {
            public UnityEngine.UI.Button.ButtonClickedEvent _scriptsToRun;

            //...

            public void Activate()
            {
                if (_scriptsToRun != null)
                    _scriptsToRun.Invoke();
            }

            //...
        }
 


Смысл скрипта таков: он позволяет удобным способом, из интерфейса редактора создавать колбеки, а затем их вызывать единообразным образом - через метод Activate (). Фишка в том, что для ButtonClickedEvent разрабы юнити написали удобный редактор, который поддерживает и выбор метода для вызова из списка методов объекта, и задание для выбранного метода набора параметров. По сути, это небольшой хак, но он отлично себя показал и прекрасно работал...

...но это всё было в старых версиях юнити. А сейчас он перестал работать так, как задумывалось. И вот почему. (Дальше описывается моя гипотеза. Возможно она неверна.) Представим, как может быть реализован данный механизм. Пусть есть класс Foo : MonoBehaviour, у него есть метод Bar (). С помощью рефлексии достаём объект типа MethodInfo - информация о нужном нам методе. Из него можно, в частности, получить список параметров - ParameterInfo. По всей этой информации вполне можно соорудить сериализуемый вызов метода Bar () у класса Foo. Но это статическая часть информации для вызова. Есть ещё динамическая часть - экземпляр самого класса Foo. И тут возникает вопрос. У какого конкретно экземпляра мы будем осуществлять вызов? Кажется вполне естественно вызывать Bar () у того самого экземпляра, который мы запихнули в ButtonClickedEvent. Так вот в последних версиях юнити это, похоже, не так. Все те часы, которые я потратил на отладку всего этого дела, показывают, что, похоже, ButtonClickedEvent на старте игры создаёт внутри себя новый экземпляр класса Foo. И вызов Bar () происходит именно у него. Косвенно это подтверждается тем, что основанный на этой гипотезе фикс действительно исправил все мои ошибки.

Надеюсь, кому-то это будет полезно.
Blizzard_jedi
UNITрон
 
Сообщения: 235
Зарегистрирован: 21 июн 2013, 00:50
Откуда: Москва


Вернуться в Скрипты

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 8