tgoop.com/logofalprog/49
Last Update:
Сериализация. Часть 1: Предисловие
#кодище
На прошлый мой пост были комментарии в чатике о том, что как-то слишком заморочено всё у меня. Поэтому я решил вас не томить, и начать уже рассказывать о действительно мудрёной системе — о нашей сериализации. Здесь уж ни в какие лимиты 4000 знаков я точно не влезу, поэтому рассказ будет разбит на несколько частей.
Начнём с предисловия. Пожалуй, самая сложная проблема создания RPG с пошаговым боем с точки зрения кода — это сохранение игры. Действительно, здесь почти нет ничего реалтаймового, что нужно как-то синхронизировать или тяжело отлаживать, как в каком-нибудь физическом платформере или мультиплеерном шутере; но зато при сохранении игры нельзя обойтись банальными чекпоинтами или ручным сохранением координат объектов. Нужно честно и автоматически уметь сохранять весь мир. Можно делать это дампом памяти или через сериализацию, но так или иначе, организация всего остального кода будет плясать от того, как мы сохраняем данные. Поэтому об этом надо думать на самых ранних этапах проекта.
Первым делом, конечно, проверил, не поменялось ли чего в стандартной сериализации Unity. А там без изменений: мрак и тихий ужас. Плохо всё: жёсткая привязка к UnityScriptableObject, нет поддержки абстрактных или базовых классов (то есть никакого наследования, только конкретные типы), нет поддержки свойств, отвратительный резолвер ссылок, который всегда резервирует место под целый объект. А если объект содержит ссылки на объект того же типа, то резервирует место до 7 уровня вложенности. А это выделение места под 2187 объектов только для трёх ссылок (3⁷)! Я правда не понимаю, как на этом можно делать что-то сколько бы то ни было серьёзное.
Расширять это безумие можно только жутчайшими костылями в виде скрытого string поля, которое заполняется байтами и парсится самостоятельно на ISerializationCallback’и. Именно так поступает популярный плагин Odin, на которого я поначалу возлагал большие надежды. Да, приходится наследоваться от его классов; да, твои классы от этого пухнут байт на 20 каждый (даже мелкие), но я готов был мириться даже с этим, лишь бы это сэкономило нам время, если бы не одно «но». Как выяснилось, поддержка сериализации свойств в Odin’е распространяется только на auto-properties, то есть на те свойства, что имеют дефолтный геттер и сеттер; а никаких кастомных действий в сериализацию пропихнуть не удастся. Без чего вся затея теряет всякий смысл. И это, заметьте, самое лучшее решение из Asset Store, которое сами Unity Technologies отмечали. Так что от него и ему подобных пришлось отказаться.
В этом месте средний программист прикрутил бы первую попавшуюся json-библиотечку (и потом бы мучался с ней в той или иной степени), но большинство из них меня по разным причинам не устраивали: где-то нет исходников, где-то уродская атрибуция, где-то нет бинарного формата. А если и есть, то имена полей всё равно пишутся много раз текстом в файл.
То ли дело Protobuf (протокол сериализации от google), который решает все эти проблемы и даже больше. Для тех, кто не знает, его фишка в том, что все сериализуемые поля класса помечаются атрибутом с уникальным номером. Привязка полей идёт именно по этому номеру, поэтому можно спокойно переименовывать, добавлять или удалять любые поля, не беспокоясь при этом об обратной совместимости сейвов. К тому же я уже работал с ним в .Net и горя не знал (кроме пары мелочей). Но беда пришла откуда не ждали. Мне потребовалось сделать условную сериализацию: ноды верхнего уровня сериализуются полностью, а вложенные объекты сохраняются как ссылки (об этом в следующих частях). И в protobuf просто не оказалось способа задать такое поведение без копипаста или появления лишнего редиректа. Казалось бы, не бывает слова «нельзя» — возьми исходники да поправь — но выяснилось, что dll написана на C# 7, а в Unity даже 6 был тогда только в бете. Пересобирать её каждый раз на стороне жуть как не хотелось, поэтому я продолжил поиски.
To be continued… Обсудить
BY Log of Alprog
Share with your friend now:
tgoop.com/logofalprog/49