Мам, на меня смотрят тёлки
#лайт
Выступал вчера с докладом на местом геймдев-митапе. Рассказывал, каково это быть техдиром на удалёнке. Записей никаких нет, но есть мнение, что я топ-докладчик. У меня это уже третий опыт подобных выступлений и с каждым разом заходит лучше и лучше. Я изначально довольно скептически отношусь к полезности 99% докладов на конференциях (включая свои), поэтому упор делаю на подачу: пусть людям будет хотя бы интересно это слушать. Нет, толика смысловой нагрузки, конечно, в моих докладах имеется, но я всё приправляю шуточками и смешными слайдами. Шутки при этом стараюсь встраивать нативно в рассказ, а слайдами играю в основном на неожиданности. И ещё громкими тезисами кидаюсь, чтобы не засыпал народ. Думаю, у меня хорошо получается чувствовать аудиторию. Правда, речь хромает: всё ещё много экаю.
Ну а ещё по фоткам я наконец-то увидел, как растолстела моя ряха за последние полгода. Надо завязывать с доставками еды и налягать на баскетбол.
#лайт
Выступал вчера с докладом на местом геймдев-митапе. Рассказывал, каково это быть техдиром на удалёнке. Записей никаких нет, но есть мнение, что я топ-докладчик. У меня это уже третий опыт подобных выступлений и с каждым разом заходит лучше и лучше. Я изначально довольно скептически отношусь к полезности 99% докладов на конференциях (включая свои), поэтому упор делаю на подачу: пусть людям будет хотя бы интересно это слушать. Нет, толика смысловой нагрузки, конечно, в моих докладах имеется, но я всё приправляю шуточками и смешными слайдами. Шутки при этом стараюсь встраивать нативно в рассказ, а слайдами играю в основном на неожиданности. И ещё громкими тезисами кидаюсь, чтобы не засыпал народ. Думаю, у меня хорошо получается чувствовать аудиторию. Правда, речь хромает: всё ещё много экаю.
Ну а ещё по фоткам я наконец-то увидел, как растолстела моя ряха за последние полгода. Надо завязывать с доставками еды и налягать на баскетбол.
Девлог #0
#лайт
Начали снимать дневники разработки по Encased. В этом выпуске общая информация про игру, а также рассказываем о работе с комьюнити. Ребята на видео как-то подозрительно много улыбаются. Наверное, потому что меня пока нет в офисе. Ну, ничего — в августе навещу их снова!
https://www.youtube.com/watch?v=z5lMainBskQ
#лайт
Начали снимать дневники разработки по Encased. В этом выпуске общая информация про игру, а также рассказываем о работе с комьюнити. Ребята на видео как-то подозрительно много улыбаются. Наверное, потому что меня пока нет в офисе. Ну, ничего — в августе навещу их снова!
https://www.youtube.com/watch?v=z5lMainBskQ
YouTube
Encased RPG - Дневник Разработки 0 - "Вовлекая Сообщество"
Заполни анкету персонажа и оставь свой след в игре Encased: https://cronus.darkcrystalgames.com/ru/.
Это — наш пилотный выпуск дневника разработки. В нём мы немного расскажем о предыстории игры и о том, как сообщество помогает нам создавать персонажей, монстров…
Это — наш пилотный выпуск дневника разработки. В нём мы немного расскажем о предыстории игры и о том, как сообщество помогает нам создавать персонажей, монстров…
Сумбурная объяснялка про тайлики и рамочки
#код
Обещался побольше рассказывать про код Encased, а не пишу совсем. Нехорошо. Будем исправляться. Сегодня незначительная, но довольно замороченная тема: как рисуется у нас рамка для зон досягаемости под ногами персонажа.
Начать придётся с конца. Ходят персонажи у нас не по 2D-пространству, а по сложному 3D ландшафту со всякими ямочками, холмиками, мостиками через канаву и вторыми этажами. А потому игровая сетка — штука сложная: не массив, а самый настоящий граф. В большинстве случаев одна игровая клеточка это ровно один квад, но иногда, на всяких неровных поверхностях, это 16 квадиков помельче (4x4). Так или иначе, вертексы этих квадиков имеют две текстурные координаты. Одна хранит локальные координаты внутри клетки, другая же — индекс клетки. Ну просто порядковый номер клетки (интовый), так как граф может иметь самую разнообразную форму и 2D-координаты на него не натянешь.
Ещё есть специальная текстура, которая динамически меняется во время игры и где каждый тексель (по порядковому номеру) указывает шейдеру на то, какой спрайт нужно выводить в этой клетке. У текселя 4 компоненты RGBA, которые прекрасно кодируют Rect спрайта внутри атласа. Более того, как несложно заметить, переставив каналы местами, мы бесплатно получаем ещё и любые зеркальные отражения спрайтов.
Осталось эти спрайты нагенерить. Но не рисовать же все варианты спрайта, правда же? Поэтому делим спрайт на 4 части и рисуем варианты только для левого-верхнего уголочка. У меня получилось 5 видов: пустой, горизонтальная грань, вертикальная грань, обе грани, уголочек (см. рисунок). Это кодируется тремя битами. Кусочков 4, так что полностью спрайт определяется 12-битным числом. Если оставить только возможные комбинации, то их 625 (5^4). Пробегаемся по этим вариантам, проверяем их на валидность (биты горизонтальной и вертикальной линии у соседних кусочков должны совпадать) и генерим спрайты. Попутно, разумеется, чекаем их на зеркальность и составляем мапу (12-битное число -> 12-битное число + вид зеркального отображения). И получается всего навсего 20 уникальных спрайтов.
Далее зашиваем это всё в атлас вместе со всякими другими спрайтами для других режимов отображения (само собой, своей собственной зашивалкой). Ну и потом в игре вычисляем зону досягаемости, смотрим на соседей каждой клетки и составляем для неё 12-битный индекс, лезем в мапу, достаём оттуда другой индекс и режим отображения, из данных атласа получаем соответствующий рект, пишем это дело в нужный тексель текстуры, а в шейдере читаем и рисуем на нужном месте. Готово.
Если вы уследили за моей мыслью, держите пятюню. Если в какой-то момент поплыли — буду рад фидбеку, где именно я пишу непонятно. А если я у вас отбил всякое желание стать графическим программистом, то я не специально.
#код
Обещался побольше рассказывать про код Encased, а не пишу совсем. Нехорошо. Будем исправляться. Сегодня незначительная, но довольно замороченная тема: как рисуется у нас рамка для зон досягаемости под ногами персонажа.
Начать придётся с конца. Ходят персонажи у нас не по 2D-пространству, а по сложному 3D ландшафту со всякими ямочками, холмиками, мостиками через канаву и вторыми этажами. А потому игровая сетка — штука сложная: не массив, а самый настоящий граф. В большинстве случаев одна игровая клеточка это ровно один квад, но иногда, на всяких неровных поверхностях, это 16 квадиков помельче (4x4). Так или иначе, вертексы этих квадиков имеют две текстурные координаты. Одна хранит локальные координаты внутри клетки, другая же — индекс клетки. Ну просто порядковый номер клетки (интовый), так как граф может иметь самую разнообразную форму и 2D-координаты на него не натянешь.
Ещё есть специальная текстура, которая динамически меняется во время игры и где каждый тексель (по порядковому номеру) указывает шейдеру на то, какой спрайт нужно выводить в этой клетке. У текселя 4 компоненты RGBA, которые прекрасно кодируют Rect спрайта внутри атласа. Более того, как несложно заметить, переставив каналы местами, мы бесплатно получаем ещё и любые зеркальные отражения спрайтов.
Осталось эти спрайты нагенерить. Но не рисовать же все варианты спрайта, правда же? Поэтому делим спрайт на 4 части и рисуем варианты только для левого-верхнего уголочка. У меня получилось 5 видов: пустой, горизонтальная грань, вертикальная грань, обе грани, уголочек (см. рисунок). Это кодируется тремя битами. Кусочков 4, так что полностью спрайт определяется 12-битным числом. Если оставить только возможные комбинации, то их 625 (5^4). Пробегаемся по этим вариантам, проверяем их на валидность (биты горизонтальной и вертикальной линии у соседних кусочков должны совпадать) и генерим спрайты. Попутно, разумеется, чекаем их на зеркальность и составляем мапу (12-битное число -> 12-битное число + вид зеркального отображения). И получается всего навсего 20 уникальных спрайтов.
Далее зашиваем это всё в атлас вместе со всякими другими спрайтами для других режимов отображения (само собой, своей собственной зашивалкой). Ну и потом в игре вычисляем зону досягаемости, смотрим на соседей каждой клетки и составляем для неё 12-битный индекс, лезем в мапу, достаём оттуда другой индекс и режим отображения, из данных атласа получаем соответствующий рект, пишем это дело в нужный тексель текстуры, а в шейдере читаем и рисуем на нужном месте. Готово.
Если вы уследили за моей мыслью, держите пятюню. Если в какой-то момент поплыли — буду рад фидбеку, где именно я пишу непонятно. А если я у вас отбил всякое желание стать графическим программистом, то я не специально.
Феерическая расстановка точек над Unity
#лайт
На прошлых выходных занесло меня на re:indiehive в Минске. Это такое сборище инди-разработчиков на веранде бара, где каждый может подойти к микрофону и показать на большом экране свой замечательный шедевр. В конце организаторы устроили круглый стол «Что выбрать: Unreal, Unity или Defold?»; и кто знаком со мной лично, тот уже знает, с какими примерно речами я туда влез. А вот большинство читателей моего канала об этом ещё не слышали, а потому я надумал наконец внятно сформулировать свою теорию в виде поста.
Если коротко: Unity — зло, нанесшее непоправимый урон индустрии и уничтожившее целое поколение программистов. Если же мысль разворачивать, то мой хейт этого движка имеет две основных составляющих: техническую и идеологическую. О технических проблемах сказано уже немало слов, да и с появлением таких вещей, как ECS и ScriptableRenderPipeline, ситуация последнее время стала улучшаться. Поэтому я остановлюсь на второй и основной претензии — идеологической.
Зададимся вопросом: почему бизнес выбирает Unity? Не потому что на нём удобно разрабатывать, не потому, что он имеет какие-то технические преимущества; бизнес выбирает Unity в первую очередь из-за того, что у него огромное количество успешных шоукейсов, а на рынке полно специалистов: их легко найти и в случае чего безболезненно заменить. С точки зрения ведения бизнеса, это идеально. Да и разработчикам, в принципе, неплохо: достаточно изучить один простой движок и ты уже востребованный специалист. Больше разработчиков Unity — больше вакансий. Больше вакансий — больше разработчиков. Ком растёт и в какой-то момент превращается в чёрную дыру, из которой уже ничего не возвращается.
В один период своей карьеры, ваш покорный слуга, некогда разносторонний программист, тоже чуть было не был поглощён этой дырой. Я перестал развиваться и вращался только на орбите Unity, медленно приближаясь к горизонту событий. Вырваться мне помогло лишь моё движкописательство. Я смог найти проекты на С++ и спасся. Но большинство тех, кто помоложе, оказалось менее удачливо и превратилось в Unity-программистов.
— А что, собственно, плохого в том, чтобы знать только Unity? — спросите вы.
Просто посмотрите, какие требования к программистам были в индустрии 10 лет назад. Достаточно пробежаться глазами по вопросам знаменитой статьи Бориса Баткина «интервью глазами пострадавшего» и убедиться, что мы бы не ответили и на половину. И сравнить это с сегодняшним днём, когда на собеседования приходят так называемые Unity-программисты с опытом разработки 3D игр более двух лет и неспособные ответить на вопрос «Что такое Depth Buffer?»
И ведь нельзя сказать, что люди стали тупее. Им просто негде стало расти. Кругом только мобильные проекты на Unity. И соответствующего уровня разработчики. Открой сейчас крутейший офис в Москве и дай денег на разработку Uncharted 5, нанять будет некого. У нас не наберётся столько спецов такого уровня. И дальше будет только хуже, петля затягивается.
Вомгла 2008 года это болезнь индустрии, а Unity — антибиотик. Но если все начнут применять антибиотики по пустякам, их действие ослабевает и появляются суперштаммы, устойчивые даже к самым сильным антибиотикам. Выбирая Unity, вы упрощаете жизнь себе сейчас, но усугубляете ситуацию в индустрии в целом в будущем.
Хотел бы я сказать, что мы в Dark Crystal Games поступаем более ответственно, но, увы, наша первая игра тоже на Unity; и не факт, что мы сможем соскочить с этой иглы. Особенно, если следующий проект будет похож на нынешний.
Верю ли я в то, что ситуация в российском геймдеве когда-нибудь изменится? Да. Знаю ли я, как это сделать? Нет.
#лайт
На прошлых выходных занесло меня на re:indiehive в Минске. Это такое сборище инди-разработчиков на веранде бара, где каждый может подойти к микрофону и показать на большом экране свой замечательный шедевр. В конце организаторы устроили круглый стол «Что выбрать: Unreal, Unity или Defold?»; и кто знаком со мной лично, тот уже знает, с какими примерно речами я туда влез. А вот большинство читателей моего канала об этом ещё не слышали, а потому я надумал наконец внятно сформулировать свою теорию в виде поста.
Если коротко: Unity — зло, нанесшее непоправимый урон индустрии и уничтожившее целое поколение программистов. Если же мысль разворачивать, то мой хейт этого движка имеет две основных составляющих: техническую и идеологическую. О технических проблемах сказано уже немало слов, да и с появлением таких вещей, как ECS и ScriptableRenderPipeline, ситуация последнее время стала улучшаться. Поэтому я остановлюсь на второй и основной претензии — идеологической.
Зададимся вопросом: почему бизнес выбирает Unity? Не потому что на нём удобно разрабатывать, не потому, что он имеет какие-то технические преимущества; бизнес выбирает Unity в первую очередь из-за того, что у него огромное количество успешных шоукейсов, а на рынке полно специалистов: их легко найти и в случае чего безболезненно заменить. С точки зрения ведения бизнеса, это идеально. Да и разработчикам, в принципе, неплохо: достаточно изучить один простой движок и ты уже востребованный специалист. Больше разработчиков Unity — больше вакансий. Больше вакансий — больше разработчиков. Ком растёт и в какой-то момент превращается в чёрную дыру, из которой уже ничего не возвращается.
В один период своей карьеры, ваш покорный слуга, некогда разносторонний программист, тоже чуть было не был поглощён этой дырой. Я перестал развиваться и вращался только на орбите Unity, медленно приближаясь к горизонту событий. Вырваться мне помогло лишь моё движкописательство. Я смог найти проекты на С++ и спасся. Но большинство тех, кто помоложе, оказалось менее удачливо и превратилось в Unity-программистов.
— А что, собственно, плохого в том, чтобы знать только Unity? — спросите вы.
Просто посмотрите, какие требования к программистам были в индустрии 10 лет назад. Достаточно пробежаться глазами по вопросам знаменитой статьи Бориса Баткина «интервью глазами пострадавшего» и убедиться, что мы бы не ответили и на половину. И сравнить это с сегодняшним днём, когда на собеседования приходят так называемые Unity-программисты с опытом разработки 3D игр более двух лет и неспособные ответить на вопрос «Что такое Depth Buffer?»
И ведь нельзя сказать, что люди стали тупее. Им просто негде стало расти. Кругом только мобильные проекты на Unity. И соответствующего уровня разработчики. Открой сейчас крутейший офис в Москве и дай денег на разработку Uncharted 5, нанять будет некого. У нас не наберётся столько спецов такого уровня. И дальше будет только хуже, петля затягивается.
Вомгла 2008 года это болезнь индустрии, а Unity — антибиотик. Но если все начнут применять антибиотики по пустякам, их действие ослабевает и появляются суперштаммы, устойчивые даже к самым сильным антибиотикам. Выбирая Unity, вы упрощаете жизнь себе сейчас, но усугубляете ситуацию в индустрии в целом в будущем.
Хотел бы я сказать, что мы в Dark Crystal Games поступаем более ответственно, но, увы, наша первая игра тоже на Unity; и не факт, что мы сможем соскочить с этой иглы. Особенно, если следующий проект будет похож на нынешний.
Верю ли я в то, что ситуация в российском геймдеве когда-нибудь изменится? Да. Знаю ли я, как это сделать? Нет.
Счастливый отзыв о серебряной пуле в мире VCS
#лайт
Не пытайтесь вспомнить другую расшифровку аббревиатуры VCS, речь в этой заметке действительно пойдёт о системах контроля версий. Казалось бы, ну сколько можно уже? Вроде бы программисты всего мира давно уже определились и решили, что для распределённой разработки ничего лучше git’а не существует. Есть, правда, горстка староверов, которые по собственной воле продолжают истязать себя SVN, но, надеюсь, среди моих читателей таких немного (фу такими быть). В наше время всё же большинство пилит софт в git’е и горя не знает. Везде, кроме геймдева. Потому что у нас, помимо непосредственно девелоперов, есть ещё артисты. И с ними вечно какие-то проблемы. Как говорится, курица не птица, артист — не разработчик.
Проблемы с художниками две. Во-первых, их тонкая душевная организация плохо сочетается с философией git’а. Будем честны, не каждый программист в неё вникает сразу, чего уж ждать от людей творческих. Справляются с этой проблемой в разных студиях по-разному: кто-то заводит для артистов отдельный SVN репозиторий; кто-то вставляет за ними графику руками; есть даже те, кто утверждает, что ценой невероятных усилий умудряется обучать гиту своих художников (до конца не верю!); кому-то боль облегчает git-flow; но в той или иной степени, страдают все.
Вторая проблема с артистами заключается в том, что они имеют обыкновение коммитить бинарный арт килотоннами, и на том же bitbucket’е место заканчивается мгновенно, а решения типа LFS помогают слабо, т.к. требуют соединения с сервером и убивают этим всю суть распределённой системы (по ревизиям уже просто так не попрыгаешь локально).
И вот обе эти проблемы, как по волшебству, решает PlasticSCM. Это VCS, созданная специально для нужд геймдева. У неё есть два режима работы. Первый как в git: у вас есть рабочая копия репозитория со всей историей, вы можете локально делать ветки, мержи, стэши и всё остальное, к чему привыкли. И второй режим для артистов: всё то же самое, но репозиторий храниться не у вас, а в облаке. То есть у вас commit всегда сопряжён вместе с push, а update с pull’ом. Это уже само по себе отсекает половину ситуаций, вызывающих сложности. Но плюс к этому, в пластике ветки являются не легковесными метками, а полноценными объектами. Каждый changeset (ревизия по здешнему) жёстко привязан к какой-то конкретной ветке. Другими словами, объекты, которыми оперирует пользователь, точно отражают термины, в которых он думает (по крайней мере, если вы работаете по схеме git-flow). В такой системе гораздо ниже вероятность совершить ошибку. Здесь в принципе не может возникнуть ситуации, вроде оторванной головы, которую сложно решить, не понимая низкий уровень. И как показывает практика, контевики тут вполне осваиваются и справляются даже с ветками.
Есть ещё в пластике третий режим. Это отдельный клиент, где нет веток вообще, а пользователь всегда сидит на последней ревизии main’а. Но этот режим совсем для слабаков и мы его не используем. Также есть ещё lock-и файлов, как в Perforce, но Unity плевать хотело на readonly флаги, поэтому ими мы тоже не пользуемся.
Что касается проблемы с большими файлами, то примерно за те же деньги, что на Bitbucket дают 100 GiB LFS, в PlasticSCM дают 100 GiB основного репозитория. Да, рабочие копии у программистов пухнут, но это меньшее из зол. Обрезать историю, к сожалению, возможности нет, но можно выкачивать отдельные ветки. То есть, если будет совсем невмоготу, в качестве костыля всегда можно сделать бранч от начала проекта и залить туда текущий слепок проекта.
Мы работаем с пластиком уже 5 месяцев и очень довольны. Но для своих личных проектов, я, понятное дело, всё равно буду использовать git. Впрочем, ещё до того, как я узнал о существовании PlasticSCM, я начинал делать клиент для git, реализующий похожие идеи в интерфейсе по упрощению работы с ветками. Называться он должен был GitTern. Может быть, ещё вернусь к нему, когда загруз по работе пройдёт. Но это уже совсем другая история.
#лайт
Не пытайтесь вспомнить другую расшифровку аббревиатуры VCS, речь в этой заметке действительно пойдёт о системах контроля версий. Казалось бы, ну сколько можно уже? Вроде бы программисты всего мира давно уже определились и решили, что для распределённой разработки ничего лучше git’а не существует. Есть, правда, горстка староверов, которые по собственной воле продолжают истязать себя SVN, но, надеюсь, среди моих читателей таких немного (фу такими быть). В наше время всё же большинство пилит софт в git’е и горя не знает. Везде, кроме геймдева. Потому что у нас, помимо непосредственно девелоперов, есть ещё артисты. И с ними вечно какие-то проблемы. Как говорится, курица не птица, артист — не разработчик.
Проблемы с художниками две. Во-первых, их тонкая душевная организация плохо сочетается с философией git’а. Будем честны, не каждый программист в неё вникает сразу, чего уж ждать от людей творческих. Справляются с этой проблемой в разных студиях по-разному: кто-то заводит для артистов отдельный SVN репозиторий; кто-то вставляет за ними графику руками; есть даже те, кто утверждает, что ценой невероятных усилий умудряется обучать гиту своих художников (до конца не верю!); кому-то боль облегчает git-flow; но в той или иной степени, страдают все.
Вторая проблема с артистами заключается в том, что они имеют обыкновение коммитить бинарный арт килотоннами, и на том же bitbucket’е место заканчивается мгновенно, а решения типа LFS помогают слабо, т.к. требуют соединения с сервером и убивают этим всю суть распределённой системы (по ревизиям уже просто так не попрыгаешь локально).
И вот обе эти проблемы, как по волшебству, решает PlasticSCM. Это VCS, созданная специально для нужд геймдева. У неё есть два режима работы. Первый как в git: у вас есть рабочая копия репозитория со всей историей, вы можете локально делать ветки, мержи, стэши и всё остальное, к чему привыкли. И второй режим для артистов: всё то же самое, но репозиторий храниться не у вас, а в облаке. То есть у вас commit всегда сопряжён вместе с push, а update с pull’ом. Это уже само по себе отсекает половину ситуаций, вызывающих сложности. Но плюс к этому, в пластике ветки являются не легковесными метками, а полноценными объектами. Каждый changeset (ревизия по здешнему) жёстко привязан к какой-то конкретной ветке. Другими словами, объекты, которыми оперирует пользователь, точно отражают термины, в которых он думает (по крайней мере, если вы работаете по схеме git-flow). В такой системе гораздо ниже вероятность совершить ошибку. Здесь в принципе не может возникнуть ситуации, вроде оторванной головы, которую сложно решить, не понимая низкий уровень. И как показывает практика, контевики тут вполне осваиваются и справляются даже с ветками.
Есть ещё в пластике третий режим. Это отдельный клиент, где нет веток вообще, а пользователь всегда сидит на последней ревизии main’а. Но этот режим совсем для слабаков и мы его не используем. Также есть ещё lock-и файлов, как в Perforce, но Unity плевать хотело на readonly флаги, поэтому ими мы тоже не пользуемся.
Что касается проблемы с большими файлами, то примерно за те же деньги, что на Bitbucket дают 100 GiB LFS, в PlasticSCM дают 100 GiB основного репозитория. Да, рабочие копии у программистов пухнут, но это меньшее из зол. Обрезать историю, к сожалению, возможности нет, но можно выкачивать отдельные ветки. То есть, если будет совсем невмоготу, в качестве костыля всегда можно сделать бранч от начала проекта и залить туда текущий слепок проекта.
Мы работаем с пластиком уже 5 месяцев и очень довольны. Но для своих личных проектов, я, понятное дело, всё равно буду использовать git. Впрочем, ещё до того, как я узнал о существовании PlasticSCM, я начинал делать клиент для git, реализующий похожие идеи в интерфейсе по упрощению работы с ветками. Называться он должен был GitTern. Может быть, ещё вернусь к нему, когда загруз по работе пройдёт. Но это уже совсем другая история.
Развлекалочка для программистов
#кодище
Последняя запись на канале заканчивалась тем, что у нас загруз на работе; поэтому я и пропадал так долго. Но вот мы наконец-то залили в последнюю минуту билд, его сейчас показывают всяким разным игрожурам в Сан-Франциско, а я могу вздохнуть свободно и оглядеться по сторонам. Писать обстоятельный пост про какую-нибудь нашу подсистему у меня сейчас нет никаких сил, поэтому развлеку вас пока небольшой приколюхой для программистов.
Думаю, после нормальной сишечки каждого ужасно бесит, что в шарпе нельзя ссылочные типы проверять на null простым условием:
а приходится писать
Классическое решение — неявный оператор каста в bool. Я вот почти все свои классики наследую от Boolable:
Догадываетесь, что может пойти не так? А кейс на самом деле достаточно интересный. Вот есть у вас два класса, наследуемых от Boolable: A и B, и вам надо в каком-то месте проверить равенство объектов:
но вы маленько опечатались и сравнили объекты разных типов:
Не смотря на то, что мы передаём разные объекты, условие будет всегда срабатывать, если объекты Boolable. Счастливой отладки, как говорится :)
#кодище
Последняя запись на канале заканчивалась тем, что у нас загруз на работе; поэтому я и пропадал так долго. Но вот мы наконец-то залили в последнюю минуту билд, его сейчас показывают всяким разным игрожурам в Сан-Франциско, а я могу вздохнуть свободно и оглядеться по сторонам. Писать обстоятельный пост про какую-нибудь нашу подсистему у меня сейчас нет никаких сил, поэтому развлеку вас пока небольшой приколюхой для программистов.
Думаю, после нормальной сишечки каждого ужасно бесит, что в шарпе нельзя ссылочные типы проверять на null простым условием:
if (refObject)
а приходится писать
if (refObject != null)
Классическое решение — неявный оператор каста в bool. Я вот почти все свои классики наследую от Boolable:
public class Boolable
{
public static implicit operator bool(Boolable ref)
{
return ref != null;
}
}
Догадываетесь, что может пойти не так? А кейс на самом деле достаточно интересный. Вот есть у вас два класса, наследуемых от Boolable: A и B, и вам надо в каком-то месте проверить равенство объектов:
if (foo1.objectA == foo2.objectA)
но вы маленько опечатались и сравнили объекты разных типов:
if (foo1.objectA == foo2.objectB)
Не смотря на то, что мы передаём разные объекты, условие будет всегда срабатывать, если объекты Boolable. Счастливой отладки, как говорится :)
Пиарчик симпатичного канала
#код
Нравятся вам мои лонгриды с пометкой #код? Если да, то вам стоит также глянуть канал @gamedev_architecture. Это, пожалуй, самый близкий ко мне по контенту канал из тех, что я знаю. Автор тоже фокусируется на всяких интересных решениях в геймдеве с точки зрения программиста, но делает это не в виде фронтовых писем и баек, а оформляет в большие обстоятельные статьи по конкретной проблеме. Технические статьи, признаться, я у него не всегда осиливаю, но философские опусы мне, как правило, заходят. В частности, про командную работу замечательную колонку от 15 мая рекомендую к прочтению людям как с опытом, так и без.
#код
Нравятся вам мои лонгриды с пометкой #код? Если да, то вам стоит также глянуть канал @gamedev_architecture. Это, пожалуй, самый близкий ко мне по контенту канал из тех, что я знаю. Автор тоже фокусируется на всяких интересных решениях в геймдеве с точки зрения программиста, но делает это не в виде фронтовых писем и баек, а оформляет в большие обстоятельные статьи по конкретной проблеме. Технические статьи, признаться, я у него не всегда осиливаю, но философские опусы мне, как правило, заходят. В частности, про командную работу замечательную колонку от 15 мая рекомендую к прочтению людям как с опытом, так и без.
Позитовый петух или новый формат вещественных чисел
#кодище
Как говорится, никто не умеет пользоваться числами с плавающей точкой. Одни люди недооценивают опасности, которые они в себе таят; другие, напротив, боятся слишком сильно и делают много лишних проверок. Реальное же положение дел где-то посередине. Нюансов работы с ними так много, что порой хочется обобщить одной фразой. В определённых кругах достаточно сказать «плавающий петух», многозначительно вздохнуть, и все всё поймут.
И вот, когда мы, казалось бы, привыкли к одному, в птичнике появился новый обитатель. Таинственный, непонятный. Позитовый петух. Речь идёт про так называемые posit’ы. Формат вещественных чисел, который придумал в прошлом году Джон Густафсон, и который в теории будет сильно круче обычных флоатов, если реализовать его в железе.
Я постараюсь сейчас дать краткий обзор формата и объяснить, почему он крутой. Чтобы что-то понять, вы должны уже иметь общее представление о бинарном устройстве обычных флоатов (если вы такими вещами никогда не интересовались, то вам стоит пропускать пока мои посты с пометкой #кодище).
Итак, позиты задаются двумя числами: n = общее количество бит в переменной, и es = количество бит, отведённых на фиксированную часть экспоненты (об этом позже). Также как и во флоатах, в позитах сначала идёт бит знака, затем экспонента и в конце мантисса. Но экспонента здесь имеет переменную длину. Соответственно при различных значениях экспоненты будет оставаться разное количество бит под мантиссу. То есть максимальная точность будет меняться в зависимости от значения экспоненты.
Экспонента имеет разную длину, потому что она записывается в особом формате. Выделяется фиксированное число (то самое, заданное по условию) младших бит экспоненты, которые остаются без изменений. Но старшие лидирующие биты называются «режим», и записываются с помощью run-length encoding. Понять принцип можно из таблички:
Из таблицы понятно, что серия из n повторяющихся единиц до первого нуля означают n - 1; а серия из m нулей, означает -m. Если серия закончилась раньше обычного, то фиксированная часть экспоненты тоже сместится влево. А следом за ней и мантисса, благодаря чему она получит дополнительные биты точности. Если же серия закончилась позже обычного, то она, напротив, отожрёт биты у мантиссы. Более того, если и этого не хватит, серия начнёт отнимать младшие биты экспоненты. Вплоть до полного заполнения всей ширины числа (младшие биты экспоненты, которые не влезли считаются равными нулю).
Легко увидеть, что точность добавляется у чисел близких к единице и теряется у очень больших значений. Что обычно и требуется на практике. Более того, благодаря возможности заполнить всю ширину числа одним только режимом, позиты начинают хранить значения очень близкие к 0 без хаков с денормализованной формой (два -6 в табличке выше у флоатов — это не опечатка).
Также позиты не содержат кучи NaN’ов и прочей ерунды. Из особых значений есть только 0 и ±∞. Причём в отличие от флоатов, нет отрицательного или положительного нуля. И бесконечность тоже одна, которая за одно ещё и NaN’ом служит в случае чего. Идея в том, что на практике не нужно всего этого зоопарка, а ошибку надо ловить сразу, с чем я совершенно согласен.
В добавок, все значения в позитах идут по порядку от самого маленького до самого большого через переполнение по кругу. Что, как вы понимаете, ещё упрощает сравнения и поиск ближайших значений.
Ну разве не восхитительно?
Фух, еле уложился в 4000 знаков. Полную инфу читайте здесь:
https://posithub.org/docs/BeatingFloatingPoint.pdf
#кодище
Как говорится, никто не умеет пользоваться числами с плавающей точкой. Одни люди недооценивают опасности, которые они в себе таят; другие, напротив, боятся слишком сильно и делают много лишних проверок. Реальное же положение дел где-то посередине. Нюансов работы с ними так много, что порой хочется обобщить одной фразой. В определённых кругах достаточно сказать «плавающий петух», многозначительно вздохнуть, и все всё поймут.
И вот, когда мы, казалось бы, привыкли к одному, в птичнике появился новый обитатель. Таинственный, непонятный. Позитовый петух. Речь идёт про так называемые posit’ы. Формат вещественных чисел, который придумал в прошлом году Джон Густафсон, и который в теории будет сильно круче обычных флоатов, если реализовать его в железе.
Я постараюсь сейчас дать краткий обзор формата и объяснить, почему он крутой. Чтобы что-то понять, вы должны уже иметь общее представление о бинарном устройстве обычных флоатов (если вы такими вещами никогда не интересовались, то вам стоит пропускать пока мои посты с пометкой #кодище).
Итак, позиты задаются двумя числами: n = общее количество бит в переменной, и es = количество бит, отведённых на фиксированную часть экспоненты (об этом позже). Также как и во флоатах, в позитах сначала идёт бит знака, затем экспонента и в конце мантисса. Но экспонента здесь имеет переменную длину. Соответственно при различных значениях экспоненты будет оставаться разное количество бит под мантиссу. То есть максимальная точность будет меняться в зависимости от значения экспоненты.
Экспонента имеет разную длину, потому что она записывается в особом формате. Выделяется фиксированное число (то самое, заданное по условию) младших бит экспоненты, которые остаются без изменений. Но старшие лидирующие биты называются «режим», и записываются с помощью run-length encoding. Понять принцип можно из таблички:
1111 8 1111 3Две левые колонки это бинарное представление бит экспоненты флоатов и их значение. Две правые — то же самое для режима позитов, где символ «.» означает «не имеет значения».
1110 7 1110 2
1101 6 110. 1
1100 5 110. 1
1011 4 10.. 0
1010 3 10.. 0
1001 2 10.. 0
1000 1 10.. 0
0111 0 01.. -1
0110 -1 01.. -1
0101 -2 01.. -1
0100 -3 01.. -1
0011 -4 001. -2
0010 -5 001. -2
0001 -6 0001 -3
0000 -6 0000 -4
Из таблицы понятно, что серия из n повторяющихся единиц до первого нуля означают n - 1; а серия из m нулей, означает -m. Если серия закончилась раньше обычного, то фиксированная часть экспоненты тоже сместится влево. А следом за ней и мантисса, благодаря чему она получит дополнительные биты точности. Если же серия закончилась позже обычного, то она, напротив, отожрёт биты у мантиссы. Более того, если и этого не хватит, серия начнёт отнимать младшие биты экспоненты. Вплоть до полного заполнения всей ширины числа (младшие биты экспоненты, которые не влезли считаются равными нулю).
Легко увидеть, что точность добавляется у чисел близких к единице и теряется у очень больших значений. Что обычно и требуется на практике. Более того, благодаря возможности заполнить всю ширину числа одним только режимом, позиты начинают хранить значения очень близкие к 0 без хаков с денормализованной формой (два -6 в табличке выше у флоатов — это не опечатка).
Также позиты не содержат кучи NaN’ов и прочей ерунды. Из особых значений есть только 0 и ±∞. Причём в отличие от флоатов, нет отрицательного или положительного нуля. И бесконечность тоже одна, которая за одно ещё и NaN’ом служит в случае чего. Идея в том, что на практике не нужно всего этого зоопарка, а ошибку надо ловить сразу, с чем я совершенно согласен.
В добавок, все значения в позитах идут по порядку от самого маленького до самого большого через переполнение по кругу. Что, как вы понимаете, ещё упрощает сравнения и поиск ближайших значений.
Ну разве не восхитительно?
Фух, еле уложился в 4000 знаков. Полную инфу читайте здесь:
https://posithub.org/docs/BeatingFloatingPoint.pdf
Kickstarter стартовал!
#лайт
Между тем мы позавчера вышли на Kickstarter!
Это и есть главаная причина, почему мы кранчили так сильно последнее время. Особо ничего по этому поводу добавить нечего, все подробности по проекту на странице кампании. Буду рад, если поможете собрать сумму или зашарите куда-нибудь. А мы теперь работаем над апдейтами и первой публичной демкой (а там прилично мне работы по оптимизации, сборке билдов и локализации). Stay tuned :)
#лайт
Между тем мы позавчера вышли на Kickstarter!
Это и есть главаная причина, почему мы кранчили так сильно последнее время. Особо ничего по этому поводу добавить нечего, все подробности по проекту на странице кампании. Буду рад, если поможете собрать сумму или зашарите куда-нибудь. А мы теперь работаем над апдейтами и первой публичной демкой (а там прилично мне работы по оптимизации, сборке билдов и локализации). Stay tuned :)
YouTube
Encased RPG - On Kickstarter now!
Encased is an old-school sci-fi post-apocalyptic RPG in development.
Buy on Steam: https://store.steampowered.com/app/921800
Buy on GOG: https://www.gog.com/game/encased_a_scifi_postapocalyptic_rpg
Join our Discord server:
https://discord.gg/56RFZTV
Early…
Buy on Steam: https://store.steampowered.com/app/921800
Buy on GOG: https://www.gog.com/game/encased_a_scifi_postapocalyptic_rpg
Join our Discord server:
https://discord.gg/56RFZTV
Early…
Поздравляшки, чатик, обнимашки
#лайт
Выше на канале я грозился, что худею, но сейчас мне можно тортик: я праздную тысячу подписчиков. Во-первых, это психологически важная отметка, после которой у меня отпали вопросы о том, стоит ли продолжать вести блог дальше или это никому неинтересно. Во-вторых, я решил в честь этого завести чатик. Я и сам подумывал так сделать, но сегодня меня напрямую попросили об этом в сообществе игры Encased. Так что я сходил в магазин, купил тортик, и вуаля: группа Alprog I/O. Теперь те, кто писал свои вопросы и комментарии мне в личку (за что им, кстати, большое спасибо), смогут это делать в специально отведённой группе.
А ещё сегодня мы набрали 50% от основной цели на Kickstarter, так что тем более незазорно отпраздновать. Вы добавляйтесь там в чатик, а я пока тортик съем.
#лайт
Выше на канале я грозился, что худею, но сейчас мне можно тортик: я праздную тысячу подписчиков. Во-первых, это психологически важная отметка, после которой у меня отпали вопросы о том, стоит ли продолжать вести блог дальше или это никому неинтересно. Во-вторых, я решил в честь этого завести чатик. Я и сам подумывал так сделать, но сегодня меня напрямую попросили об этом в сообществе игры Encased. Так что я сходил в магазин, купил тортик, и вуаля: группа Alprog I/O. Теперь те, кто писал свои вопросы и комментарии мне в личку (за что им, кстати, большое спасибо), смогут это делать в специально отведённой группе.
А ещё сегодня мы набрали 50% от основной цели на Kickstarter, так что тем более незазорно отпраздновать. Вы добавляйтесь там в чатик, а я пока тортик съем.
Что-то вроде ECS, но вообще не оно
#код
Этот пост я пишу из Исландии. Коротко сформулировать впечатления от этой страны можно так: красиво, но бессмысленно. Это не в обиду исландцам сказано, просто здесь меня постоянно преследует немой риторический вопрос «зачем вообще селиться на этом суровом куске земли?» Местность вокруг часто больше походит на терраформированный Марс, чем, собственно, на Землю. Но сейчас я «под куполом», то есть в тёплой гостинице, а потому хочется поговорить о чём-то противоположном: пусть не очень красивом, но зато функциональном.
Таковой является, например, наша компонентная система. Последние полгода стоит кому-нибудь заговорить о компонентах, как все сразу вспоминают ECS (Entity Component System), и на первый взгляд то, что написал я, довольно похоже; но на самом деле не имеет с ним ничего общего.
Но давайте по порядку. В Unity много лет были GameObject’ы, которые содержали Component’ы. Это привычная всем, но неэффективная модель. Некоторые даже ошибочно называли её ECS, хотя ей там и не пахло. Настоящая же ECS предполагает 3 вещи: это, собственно, Entity (сущность), Component и System. Причём Entity, в отличие от GameObject’ов — это не объекты. Это просто целочисленные id. То есть просто номера сущностей, которые сами по себе никаких компонентов не хранят. Напротив, каждый компонент «знает», к какой сущности он закреплён. И что немаловажно, компоненты одного типа лежат в одном месте (в одном массиве) и представляют собой только данные. А вся логика находится в системах, которые обрабатывают компоненты только определённого типа. Таким образом логика всегда сводится к тому, чтобы пробежаться по одному или нескольким массивам компонент и выполнить над каждым элементом какие-то однообразные задачи. Благодаря тому, что однотипные данные лежат в памяти друг за другом последовательно, эта задача прекрасно параллелится за счёт векторизации и крайне cache-friendly, что очень и очень хорошо для производительности. В этом и есть основная суть ECS.
В Unity для её внедрения привлекли самого Майка Актона (одного из самых главных идеологов Data oriented design и ECS в частности). Я не смотрел, что у них в итоге получилось, но судя по имени, там теперь действительно всё грамотно в кои-то веки. Но мы стартовали проект, когда этого ещё не было в стабильной версии Unity, поэтому начали разработку на своих компонентах.
У нас тоже есть Entity, но это не номера, а полноценные объекты, которые содержат наши компоненты (мы их зовём модули), а систем нет вообще. По сути это больше даже похоже на старую модель компонентов Unity и тоже совершенно не про производительность. Зачем же тогда надо было это переизобретать? Дело в том, что одним из самых первых и главных стратегических решений в Encased было как можно сильнее отвязаться от сцен и gameobject’ов. Наши Entity и модули — это данные в чистом виде. Игра может играть сама в себя безо всякой визуализации (с небольшими оговорками), а также легко и безболезненно сохранять или загружать своё состояние. С gameobject’ами, как вы понимаете, это сделать было бы в разы сложнее, так как сериализация в Unity это та ещё Песнь пламени и льда (про наше решение как-нибудь напишу отдельный пост). А вся визуализация у нас происходит за счёт размещения на сцене Actor’ов, которые «играют» роль сущностей. У актора на каждый тип модуля сущности есть ModuleSync (если это необходимо), который синхронизирует визуальное представление. Причём могут быть различные Sync для Play- и EditMode.
Таким образом мы минимально завязаны на Unity, минимально имеем оверхедов от её классов, но при этом у нас удобная компонентная система. Потенциально удобная в первую очередь для дизайнеров, так как объекты в RPG играх могут содержать самые разные сочетания свойств, так что без чего-то подобного делать такой проект крайне сложно. К тому же недавно мы добавили возможность наследования компонент от родительской сущности, что теперь позволяет делать шаблоны оружия или предметов прямо на базе компонентной системы.
Обсудить
#код
Этот пост я пишу из Исландии. Коротко сформулировать впечатления от этой страны можно так: красиво, но бессмысленно. Это не в обиду исландцам сказано, просто здесь меня постоянно преследует немой риторический вопрос «зачем вообще селиться на этом суровом куске земли?» Местность вокруг часто больше походит на терраформированный Марс, чем, собственно, на Землю. Но сейчас я «под куполом», то есть в тёплой гостинице, а потому хочется поговорить о чём-то противоположном: пусть не очень красивом, но зато функциональном.
Таковой является, например, наша компонентная система. Последние полгода стоит кому-нибудь заговорить о компонентах, как все сразу вспоминают ECS (Entity Component System), и на первый взгляд то, что написал я, довольно похоже; но на самом деле не имеет с ним ничего общего.
Но давайте по порядку. В Unity много лет были GameObject’ы, которые содержали Component’ы. Это привычная всем, но неэффективная модель. Некоторые даже ошибочно называли её ECS, хотя ей там и не пахло. Настоящая же ECS предполагает 3 вещи: это, собственно, Entity (сущность), Component и System. Причём Entity, в отличие от GameObject’ов — это не объекты. Это просто целочисленные id. То есть просто номера сущностей, которые сами по себе никаких компонентов не хранят. Напротив, каждый компонент «знает», к какой сущности он закреплён. И что немаловажно, компоненты одного типа лежат в одном месте (в одном массиве) и представляют собой только данные. А вся логика находится в системах, которые обрабатывают компоненты только определённого типа. Таким образом логика всегда сводится к тому, чтобы пробежаться по одному или нескольким массивам компонент и выполнить над каждым элементом какие-то однообразные задачи. Благодаря тому, что однотипные данные лежат в памяти друг за другом последовательно, эта задача прекрасно параллелится за счёт векторизации и крайне cache-friendly, что очень и очень хорошо для производительности. В этом и есть основная суть ECS.
В Unity для её внедрения привлекли самого Майка Актона (одного из самых главных идеологов Data oriented design и ECS в частности). Я не смотрел, что у них в итоге получилось, но судя по имени, там теперь действительно всё грамотно в кои-то веки. Но мы стартовали проект, когда этого ещё не было в стабильной версии Unity, поэтому начали разработку на своих компонентах.
У нас тоже есть Entity, но это не номера, а полноценные объекты, которые содержат наши компоненты (мы их зовём модули), а систем нет вообще. По сути это больше даже похоже на старую модель компонентов Unity и тоже совершенно не про производительность. Зачем же тогда надо было это переизобретать? Дело в том, что одним из самых первых и главных стратегических решений в Encased было как можно сильнее отвязаться от сцен и gameobject’ов. Наши Entity и модули — это данные в чистом виде. Игра может играть сама в себя безо всякой визуализации (с небольшими оговорками), а также легко и безболезненно сохранять или загружать своё состояние. С gameobject’ами, как вы понимаете, это сделать было бы в разы сложнее, так как сериализация в Unity это та ещё Песнь пламени и льда (про наше решение как-нибудь напишу отдельный пост). А вся визуализация у нас происходит за счёт размещения на сцене Actor’ов, которые «играют» роль сущностей. У актора на каждый тип модуля сущности есть ModuleSync (если это необходимо), который синхронизирует визуальное представление. Причём могут быть различные Sync для Play- и EditMode.
Таким образом мы минимально завязаны на Unity, минимально имеем оверхедов от её классов, но при этом у нас удобная компонентная система. Потенциально удобная в первую очередь для дизайнеров, так как объекты в RPG играх могут содержать самые разные сочетания свойств, так что без чего-то подобного делать такой проект крайне сложно. К тому же недавно мы добавили возможность наследования компонент от родительской сущности, что теперь позволяет делать шаблоны оружия или предметов прямо на базе компонентной системы.
Обсудить
Сериализация. Часть 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… Обсудить
#кодище
На прошлый мой пост были комментарии в чатике о том, что как-то слишком заморочено всё у меня. Поэтому я решил вас не томить, и начать уже рассказывать о действительно мудрёной системе — о нашей сериализации. Здесь уж ни в какие лимиты 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… Обсудить