Челленджи, вайбкод и геймджем.
Вводные
Идея участия в каком-нить джеме не покидала меня уже последние пару месяцев, но то работа, то дела, то впадлу (последнее вычеркнуть), в итоге наткнулся на пост о Level 10 в каком-то из каналов в телеге (опа, нашёл какой😎) и решил, что можно попробовать. Как оказалось, сложнее всего было попробовать не забить, т.к. в конечном итоге за полноценную разработку игры я сел с пингом в 2 дня (28.06) и понимание, что могу не успеть первые пару дней жёстко дизморалило😔 Однако, собрав в кулак волю, свои идеи (и нейронку от гугла), решил, что надо идти до конца💪 Так, холодным 🍁летним🍁 вечером (Ало, это гидромет? Где тепло-то?) я сел за разработку.
Идея игры
Т.к. время и так поджимало, придумывать какой-то невероятно глубокий проект, который до глубины души пронзит прямо в сердце!..😻
Ну, вы поняли, крч.
Идея простенькой аркады в духе старых добрых браузерок с расчлененим несчастных людишек, играя за злобную хтонь, поселилась в голове почти сразу и, следуя принципу, что первая мысль часто лучшее решение, я приступил к её реализации.
А почему бы не усложнить итак сложное?
Почти сразу я для себя решил устроить небольшой челлендж: максимально использовать только то, что движок (Unity 6, если кто не понял) даёт по дефолту. На практике это означало, что всю графику придётся собирать в буквальном смысле из говна и палок (но разве не в этом дух индюшатины?🙃). И забегая немного вперёд: ЭТО БЫЛО АХ*ЕННОЙ ИДЕЕЙ.
Пошла жара [▶]
Схватив недавно созданный кастомный темплейт (спасибо Юнитекам, что наконец-то додумались до этого спустя 20+ лет🛐), чтобы не тратить время на рутинную настройку папок/систем ввода и прочей бурмалды, создал проект и тут же понял: а где мои 2д спрайты и тэдэ?.. Да, я забыл, что этот темплейт сделан под 3д игры🤦♂️ (И это ещё сыграет свою неожиданную роль). Ну, что ж, не расстраиваюсь, пересоздавать не хочу, бегу в PackageManager и быстренько гружу всё, что нужно для 2д. Закидываю всякие свои кастомные скрипты редактора (быстрый билд и т.п.), закидываю кастомную систему генератора шума, т.к. уже тогда решил делать процедурный уровень. Залетаю с двух ног в чат гугловской нейронке и начинаю уже разработку.
Само собой, расписывать здесь, как создавал персонажа, настраивал анимации и т.д. не вижу смысла, ибо это никому не интересно, потому сразу пробежимся по интересным моментам.
Персонаж-балванчик состоит из отдельных частей, каждая из которых участвует в анимации, получает отдельный урон и влияет на отдельные аспекты ИИ бота. Казалось бы, обычный костный регдолл, вот только я с ним никогда не работал😐 Собственно и тут решил не начинать, тем более, что обычные спрайты в юнити для этого не подойдут (не забываем про челлендж), потому вооружаюсь старыми добрыми child-parent трансформациями и делаю скелет из спрайтов-капсул, привязывая их через иерархию друг к другу.
На этом моменте те, кто шарят за Юнити уже поняли, на какой подводный камень я тут наткнулся: все дефолтные спрайты не масштабируемые и их пивот (точка трансформаций) находится в центре. Но ведь для вращения тех же рук/ног нужно, чтобы они вращались вокруг одного из своих скруглений. Не беда. Создаём копии дефолтных спрайтов в папке! (Я наверное первый, кто вообще использовал эту функцию движка...😑 Зачем она вообще?)
Дальше делаем их масштабируемыми, настраиваем пивот и вуаля! Красивые скруглённые палочки вращаются ровно как мне нужно. Осталось только настроить анимации и связать всё скриптами. Тут рассказывать нечего: [вайбкод -> фикс багов -> вайбкод -> объясняю нейронке, что она не права] * 10 раз = профит!
Далее реализовал того, за кого мы играем - злобная хтоническая бяка, закрывающая умирающее солнышко и изничтожающая остатки человеков. Рассказать здесь тоже особо нечего, т.к. по скриптам там тот же принцип, что и с челиками, но я решил, что будет забавно буквально закрыть этой штукой солнце (благо, в 3д проекте оно действительно ведёт себя как полноценное солнце). Так и родился Эклиптор (eclipse англ. - затмение). Таким образом я впервые обернул 3д проект для 2д игры в свою пользу (вспоминаем мой косяк с выбором темплейта).
Дальше сделал его живее, превратив в глаз, следящий за курсором, привязал к нему абилки и их анимации ну и собсна всё. Результат - кто играл, тот видел.
Следующий интересный аспект касался уровня. По сути ничего сложного: берём одномерный шум Перлина по X, умножаем на высоту уровня, получаем Y и заполняем столбец тайлами (дефолтный квадратный спрайт) с физикой. Разве что вместо унылого Mathf.Perlin, я использовал свой офигенный генератор шума на опенсорсном генераторе с гитхаба. Но тут сразу возникла очевидная проблема: любое изменение уровня (установка/удаление тайла) длиной 500+ блоков заставит физический движок пересчитывать весь коллайдер тайлмапы, а для уровня из десятков тысяч блоков - это смерть игры на добрые полсекунды. Само собой это не заставило меня отказаться от абилки камня (которую я придумал самой первой, хотя реализовал в итоге за 2 часа до дедлайна🙂), так что я решил что это надо решить.
В общем-то, решение было довольно простым и классическим: вместо генерации всего уровня сразу, мы отрисовываем только ту часть, которую видит камера (+ буферная зона слева/справа, чтобы делать там всякое, пока игрок не видит). Эдакий Frustum Culling для отдельно взятой механики. Подход этот старый, но рабочий. Из минусов: повышенная нагрузка в моменте, т.к. движку нужно постоянно ставить/удалять тайлы в буфере и пересчитывать физику пары-тройки сотен тайлов, что, конечно, съедает часть драгоценных кадров, но это ни в какое сравнение не идёт с зависанием на доли секунд при пересчёте десятков тысяч тайлов в единый коллайдер при любом изменении.
Следующая интересность - это город и горы на фоне. Взглянув на них, пожалуй, можно усомниться, что в игре не используется заранее нарисованных спрайтов, т.к. если город ещё можно представить как просто спавнящиеся дефолтные квадраты, растянутые вверх на разную высоту (что не так, само собой), то вот горы... Но если присмотреться, то можно заметить, что у них и нашего уровня есть кое-что общее. Конечно же, они сделаны по тому же принципу, что и процедурный уровень: шум и умножение на высоту = неповторяющийся реалистичный рельеф.
- Стоп, ты ведь не хочешь сказать, что у тебя на фоне постоянно ставятся сотни тысяч тайлов, чтобы рисовать горы?
- ...
- Да ведь?..
Ну... И да, и нет) На самом деле, когда мы с нейронкой навайбкодили генератор уровня, первой идеей было сделать горы именно так, но исключить из уравнения физику (она там всё равно не нужна), что в разы снизило бы нагрузку на ЦП, даже при установке сотен тайлов каждый кадр, однако даже так, это было бы очевидно плохим решением для производительности. И тут меня осенило: если нам нельзя делать подобную логику на медленном однозадачном процессоре, значит отдадим эту задачу видюхе! Да, то, что вы видите на фоне - это кастомный шейдер, собранный в ShaderGraph. При чём один и тот же и для гор, и для города. Разница только в настройках материала. (В который раз убеждаюсь, что математические шумы - лучшее, что придумало человечество, при всей моей нелюбви к математике)
Вдаваться в подробности работы этой штуковины не вижу смысла, достаточно упомянуть, что это абсолютно тот же принцип, что и для генерации уровня, только вместо сетки тайлмапы у нас полотно спрайта разделённое на пиксели (не буквально, а математически), а вместо тайлов закрашенная текстура. Привязываем шум к мировым координатам, чтоб при смещении спрайта текстура не ехала за ним - и всё! Результат: шикарный, как по мне, фон, без малейшего падения фпс (отрисовать такое для видеокарты - это микросекунды). С пост-обработкой через GlobalVolume выглядит вообще сочно, несмотря на минимализм. (Я на эту штуку залип на полчаса тупо наблюдая, как челик бежит на фоне этой красоты)
И тут тема фона у нас не заканчивается. Само собой, каким бы он красивым не был, будь он просто статичным набором слоёв (даже двигаясь вместе с уровнем) в нём не было бы и половины той сочности, которая есть в финальном билде. Само собой, я намекаю на эффект параллакса - когда объекты на фоне смещаются тем медленнее, чем дальше от наблюдателя. Собственно, именно параллакс создаёт тот самый объёмный фон в 2д играх, вроде Tarraria или RainWorld.
- Так, ну, динамичный фон - это красиво, но что интересного в параллаксе, как механике? Это же база базовая.
Да, в самом параллаксе как механике ничего интересного нет, т.к. есть уже миллион скриптов, которые позволяют этот самый параллакс делать в пару кликов. Да и написать такой скрипт - вопрос получаса работы даже без нейронки. И вот тут раздаётся громкий 💥ба-бах💥 - это чеховское ружьё моего 3д темплейта выстрелило и сделало мне параллакс... БЕЗ ЕДИНОЙ СТРОЧКИ КОДА! Всё что нужно: оставить перспективную камеру, вместо ортографической, сделать спрайты фона огромными и раскидать на разном растоянии от камеры - профит! И я бы мог сказать, что это я такой гений и специально так сделал, но обнаружил я это абсолютно случайно, пока игрался со слоями фона) Нет, про сам эффект-то я знал (очевидная вещь для 3д игры), просто напрочь забыл о нём, загнав себя в рамки 2д мышления.
Ну, собсна, на этом вроде и всё. Рассказывать про остальные механики и мои мучения с нейронкой (радуемся, нас пока не заменят, по крайней мере полностью) не буду, т.к. там уже не было каких-то интересных решений и ситуаций связанных с челленджем, обычный говнокод. Возможно потом дополню, если вспомню что-то ещё.
Поговорим о косяках
О плюсах проекта, как интересного опыта поговорили, теперь, для баланса вселенной, поговорим о минусах. Конечно, их в игре собранной на коленке гора и многие, естественно, вскроются в процессе тестирования уже игроками, но сейчас поговорим о явных, так сказать, взглядом со стороны автора.
1. Отсутствие разнообразия: все балванчики одинаковые, ведут себя одинаково, одинаково анимированы; мир красивый, но за 5-10 минут игры приедается и уже не впечатляет; геймплей тоже однообразный, т.к. успел реализовать только 3 абилки из 10(!) запланированных. Увы, времени, чтобы исправить эту проблему у меня не хватило.
2. Дьявол в мелочах: несмотря на мои попытки сделать мир живее за счёт мелких деталей (те же костры, которые ещё и тухнут, если рядом нет челиков или попали в бурю), уследить за всеми мелочами трудно, особенно когда значительную часть кода приходится переписывать по 5-10 раз. Так появляются вроде мелкие баги, но отвлекающие от погружения в процесс. Например: тайл с помощью абилки камня можно сломать под костром или ракетой (пытался исправить, но застрял и решил сконцентрироваться на других более важных доработках), челики, если заспавнятся дальше ракеты всё равно побегут от неё, т.к. нет смены направлений (добавлять означало бы переписать половину ИИ челиков - непозволительная роскошь при надвигающемся дедлайне). Само собой это не всё, но это то, что может сразу броситься в глаза и испортить впечатление, создавая эффект "непродуманности", хотя это именно недоработка в условиях ограниченного времени (в любом случае косяк).
3. Отсутствие сохранений: не полное, игра сохраняет лучший результат и настройки громкости, но непосредственно текущую игровую сессию - нет. При выходе, играть придётся сначала. Для джема этот минус не считаю критичным, т.к. чаще всего эти игры - не более чем демка (которая может перерасти во что-то большее, а может и нет), но упомянуть о нём всё же стоит. Пришлось пожертвовать ими в угоду приоритету других механик.
Cool story про WebGL билд
Помимо билда под винду, планировалось сделать ещё и веб-билд, т.к. это очевидно удобно для многих, но тут опять же сыграла роль моя невнимательность: при создании проекта я оставил у темплейта дефолтную версию Юнити (6000.3.чётотам), которую давненько пришлось скачивать из архива Юнити, т.к. были проблемы с UnityHub. Т.е. по сути, это был просто голый движок без единого модуля платформ, кроме базового модуля винды. И обнаружил я это, когда до дедлайна оставалось 20 минут. На этом моменте для меня стало очевидно, что про WebGL можно забыть, т.к. перейти на другую версию+установить модуль+создать билд (а веб билды обычно создаются чуть ли не дольше всего) - это минимум 30 минут, которых, увы, у меня уже не было.
Вывод: всегда проверяйте заранее, готов ли ваш движок к билдингу😉
Заключение
Независимо от результатов, я очень доволен, что решил поучаствовать. Это далеко не первый мой джем, но однозначно самый насыщенный в плане интересных решений при разработке проекта. Чувство, когда ты свой явный косяк оборачиваешь в свою пользу, когда решаешь тривиальные задачи максимально нетривиальными способами, дабы сэкономить драгоценные секунды, когда буквально из говна и палок получается собрать что-то стоящее - это ведь именно то, ради чего мы и участвуем в геймджемах, не так ли?)
Спасибо за внимание, у меня всё🖖


Комментарии (0)
Войдите, чтобы написать комментарий
Войти