Ren
> или переплюнуть Вастерленд 2, который на мой взгляд не очень удачный?
Yupy
> Очень интересно услышать про нововведения в Wasteland 2. Хотя бы три.
На всякий случай уточню - это я вполне искренне спросил, а не чтобы задеть. По моим оценкам W2 действительно более современная игра, но только по технологии и дате выхода. По неким "вибрациям" пост-апокалипсиса мне все еще больше нравится F1. Поэтому честно интересно какие фичи или изменения в W2 вы считаете удачными. Кто знает, может быть и мы их реализуем.
С сериализацией целая эпопея у нас.
Стандартную юнити-сериализацию использовать не стали, думаю, по понятным причинам: она полный трэш, ад и Израиль во всех смыслах, начиная от проблем со ссылками на класс того же типа (который она резолвит резервацией места до 7 уровня вложенности); заканчивая жёсткой привязкой ко всему юнитёвому дерьму, типа UnityScriptableObject. Я уж молчу про расширяемость, которую без жутчайших костылей, вроде скрытых полей у класса (которые ещё и оверхедят по памяти) и не сделаешь. Одно время была надежда на плагинчик Odin, который помимо всего прочего, через аналогичные костыли завозит более-менее сносную сериализацию кастомных классов. Я даже готов был мириться с кучей лишних полей, которыми он засирает ваши классы по чём зря, лишь бы это сэкономило моё время. И всё бы ничего, да только выяснилось, что он умеет сериализовать только автопроперти; а те свойства, что имеют дефиницию геттера или сеттера — нет. Без чего вся затея теряет смысл. А это как бы самое лучшее решение в своём классе в ассет сторе, с кучей наград от Unity и всё такое. Так что на помойку его и ему подобных.
Следующим логичным шагом было прикрутить какую-нибудь известную сериализацию. Бесчисленные json-решения (и уж тем более xml) я не очень люблю. По разным причинам. Где-то херово пашет в юньке, где-то нет исходников, где-то атрибуция уродская, где-то нет бинарного формата. А если и есть, то имена полей текстом всё равно в файл попадают. То ли дело протобуф. Бинарный, компактный, шустрый, с версионностью, от гугла. К тому же я уже имел с ним дело, причём как раз в дотнете, и горя не знал. Ну, разве что текстового формата нет (вернее его крайне непросто получить), но это терпимо. А так всё хорошо. Да только в этот раз беда пришла откуда не ждали. Мне потребовалось сделать условную сериализацию: ноды верхнего уровня сериализуются полностью, а вложенные объекты сохраняются как ссылки (в файл записывается только их guid, а сам объект может быть сохранён в другом файле). И протобуф тут всосал. Просто нет способа задать такое поведение без копипаста пропертей или без появления лишнего редиректа, который при этом ещё и читабельность портит. И даже костыль толком не вбить, потому что схему нельзя на лету менять. Казалось бы, не бывает слово нельзя — возьми исходники да поправь — но оказалось, что дллка сбилжена где-то на стороне, и что в исходниках там C#7.0, а юнити даже в бетке только 6-ой умеет... Ой, всё, короче. Ну его на фиг этот протобуф. Тем более всегда бесило, что он null от пустой коллекции не может отличить. Чё там ещё бывает молодёжного?
А самое клёвое, что я нашёл, это сериализаторы чувака по имени Yoshifumi Kawai. У него их целый выводок под разные задачи: ZeroFormatter, MessagePack, UTF8Json. Причём зверски под капотом оптимизированные. Без аллокаций, с кодогенерацией прямо оп-кодов IL. Короче, поехавший, но крутой японец. Под сейвы игр лучше всего подходит MessagePack. На низком уровне там вообще всё круто, а вот на высоком я опять нашёл фатальные недостатки. Полиморфизм наследования при сериализации работает только для абстрактных классов, а нумерация полей для версионности должна быть сквозная, что либо неудобно, либо ведёт к резкому росту бинаря (из-за больших разрывов в нумерации).
Благо, это MIT, поэтому можно положить исходники и написать какие угодно расширения. Я написал свою атрибуцию и, соответственно, её разбор и запись в файл кастомного формата. Правда, без кодогенерации. У меня поддерживается наследование, причём нумерация полей (а, значит, и версионность) для каждого класса своя (отдельно у родителя и у потомка). Резолвятся циклические ссылки, и даже есть возможность сериализовать данные в разные файлы с перекрёстными ссылками друг на друга через guid'ы объектов, а потом всё корректно обратно десериализовать. Очень полезно, когда нужно менять баланс игры не влияя на файл сейвгейма, а также во время разработки, когда каждая сущность сейвится в свой файл для минимизации конфликтов.
Атрибуция в конце концов пришла к следующему виду. Класс, чьи поля будут сохраняться, помечается атрибутом DarkContract (от Dark Crystal Games). В атрибут также передаётся инт-номер класса (отдельно заведён enum):
[DarkContract(DarkType.Entity)]
Некоторым классам не нужно знать их инт-номер при сериализации, тогда он не указывается, но чтобы не ошибиться конструктор DarkContract без аргументов запрещён, мы должны явно указать причину:
[AbstractDarkContract] // абстрактный класс [StaticDarkContract] // структура или прямое наследование, без ветвления
Ещё есть
[SealedContract(DarkType.Entity)]
В этом случае номер реального класса нужно записывать, если мы сериализуем базовый класс, но не надо, если сам класс (мы точно знаем тип во время десериализации, так как от него нельзя унаследоваться). Строго говоря, этот атрибут избыточен, так как мы и так через рефлекшн знаем, что класс sealed, но я решил оставить для консистентности.
Поля и проперти каждого класса помечаются просто [Key(0)], [Key(1)], [Key(2)] и так далее. Безопасно переименовывать, добавлять и удалять. MessagePack пишет всё в бинарном виде, но структурно это JSON, и есть возможность преобразовать в текстовый вид.
Формат уже чуть поменялся, но суть картинка передаёт. В другой раз, возможно, расскажу, чё у нас с компонентной системой, скриптами и евентами.
Alprog
> Камера: Полное 3D, но закос под изометрию
Арты в стартовом посте у вас шикарные. Просто 10 из 10.
А можно немного побыть вангой? Когда их попробуете затолкать в 3д - станет хуже. Заметно и сильно хуже. Надеюсь конечно что вы обойдете этот момент, но вот такое есть наблюдение.
Alprog
Спасибо, было интересно почитать про сериализацию!
IL
тут не только про "сэйв". Здесь, как я понял и про базу баланса. Вы же не будете хардкодить все в код? Все должно быть вынесено, что бы можно было на лету менять и не пересобирать билд. По-крайней мере, у меня именно так, что все вынесено.
dmonin
Да, я поэтому и спросил как баланс игры может влиять на файл сейвгейма.
IL
по-сути легко, как минимум таблицей ссылок на перки персонажа и т.д.
122
Спасибо за отзыв. К сожалению ты прав и мы сами это отлично понимаем. Да - арты минимум в два раза лучше ингейм графики. Но они много решают задач - исследования компонентов, которые нужно для построения локации, создание настроения, вдохновения как для команды так и для тех кто следит. Поэтому вот такие - достаточно проработанные.
IL
> А разве баланс в продакшене может быть зашит в сейв файл?
Ну вот в том-то и дело, что баланс зашит в отдельный файл. Фишка тут в том, что если в балансе описан статус-эффект "отравление", то мы не думаем о том, где он описан, запоминаем в ран-тайме ссылку на него (именно C#-мембер на него, а не имя или id), а при сериализации всё распихается по разным файлам, как нужно. Можно будет даже переименовать эффект или даже сам класс, описывающий эффект. Всё равно продолжит работать :)
Крутые концепты. Желаю успехов!
Скажите, камеру в игре можно будет вращать?
122
>А можно немного побыть вангой? Когда их попробуете затолкать в 3д - станет хуже.
С чего это вдруг?) В юньке из коробки не самые плохие шейдера, да и свет в целом можно докрутить нормально.
А качество моделей и текстур зависят уже от художников.
Keypa
> Скажите, камеру в игре можно будет вращать?
Больной вопрос. Разрешить-то не проблема (по крайней мере, чит такой я себе точно сделаю), а вот с точки зрения левел- и геймдизайна непонятно.
Пока сложно сказать, какие это проблемы за собой повлечёт. Надо тестить. Воможно, будет вращение, но на ограниченный угол какой-то.
Начал работать над графикой. Юнити знатно потрепала мне нервы сегодня. Всё, что касается матриц рендера (проекция, скиссорс, вьюпорт), если работать через стандартные камеры, а не городить свой рендер, превращается в адский геморой*. Особенно, в части, где это касается взаимодействия с шадоумапами. А вообще у нас появились зайчатки исчезания стен.
Итак, после кучи времени проведённого в график-дебаггере, кажется придумал финальное решение (третье или четвёртое по счёту уже):
1. Отключаем источники света.
2. Рендерим центральную часть сцены в маленький рендер-таргет (512?) с выставленным nearClipPlane близко к персонажу. Чтобы ракурс совпадал, копируем матрицу проекции основной камеры, но домножаем её на свою матрицу scissors.
2.1. Перед этапом освещения с помощью CommandBuffer'a воруем содержимое gBuffer себе на будущее.
3. Включаем источники освещения
4. Рендерим основную камеру.
4.1. Перед началом этапа освещения по кастомной грейскейл-маске блендим содержимое нашего сохранённого gBuffer в соответствующие каналы текущего.
4.2. Также поступаем с цветом и глубиной, но глубину не блендим, а выбираем одну из двух по некому порогу грейскейл-альфы (0.5?)
4.3. После завершения рендера каждой скринспейс шадоумапы блюрим в ней места, соответствующие грейскейл переходу около порогового значения (возможно лишний пункт, надо будет посмотреть, как смотрится).
5. Выделенные объекты, которые должны подсвечиваться силуэтом и обводкой рисуются с учётом Z в отдельный полноэкранный буфер-трафарет. Если Z фейлится, но это происходит в месте полупрозрачной маски, в трафарет пишется соответствующая порция альфы.
6. В ещё один буфер рисуются разными цветами силуэты этих объектов. Постэффектом добавляется обводка, если нужно размывается.
7. Силуэты вместе с обводкой накладываются сверху на всё, отрезаясь по трафарету.
А ещё кастомный шейдер для ScreenSpace теней нужно сделать. Чтобы блендить каскады между собой, а не делать резкий переход (воможно, кстати, подобие блюра делать уже на этом этапе сразу).
Alprog
Какие новости? Сделал задуманное? Интересно же
Alprog
> Итак, после кучи времени проведённого в график-дебаггере, кажется придумал
> финальное решение
Я нихрена не понял, кроме того что юнити - крутой и удобный движок.
Тема в архиве.
Тема закрыта.