Так уж получается что (почти) все мои последние посты за год связаны с моим участием в Ludum Dare... И вот опять.
Сам ивент проходил 1-3 декабря. Я снова участвовал в "Компо" (сольная категория, требующая публиковать исходный код и запрещающая пользоваться заранее созданными или скачанными вещами - за исключением игрового движка, который проходит как инструмент).
Тема попалась "The more you have, the worse it is". По русски это примерно "чем больше у тебя есть тем оно хуже".
С одной стороны я за эту тему голосовал. Тем допускает широкую трактовку. Все вроде бы хорошо...
Но вот только я перед оглашением темы заглянул одним глазком в один из спонтанных "экзит полов" (открытых голосовалок чтоыб спрогнозировать итог основного голосования) и там лидировала с большим отрывом совсем другая тема. И под эту тему из экзит-пола у меня родилась идея, которая пару дней варилась в голове и заняла приличный кусок моих мыслей...
И эта идея ну совсем никак не вписалась в победившую в итоге тему, так что у меня получился легкий шок от внезамного облома. Ну... в целом справедливая расплата за попытку фальстарта.
Рождение идеиРождение идеи.
Предыдущие мои игры напрягали в основном головной мозг. Что вполне закономерно вылилось в низкий рейтинг в графе "Fun". Ну не весело мозгой скрипеть.
Не весело. Поэтому в этот раз я хотел сделать что-то более нацеленное на спинной мозг.
Две основные попытки вписать "активный" жанр в тему были сделать "Dungeon Crawler" с системой штрафов за перегурженность предметами или платформер, карта уровня которого меняется в зависимости от прогресса.
У первого варианта очевидный недостаток в том что это весьма сложный жанр, который с нуля за 48 часов сделать... не слишком просто. Он предполагает множество систем, которые должны между собой взаимодействовать, а это не только много дезайна, кодинга и дебагинга, но и неюллюзорный шанс что взятый наобум балланс сделает игру неиграбельной...
У второго варианта тоже был золидний минус. Платформеры как жанр как правило предполагают линейные уровни. А если уровень линеен, то как-то не особо заметна разница между уровнем изменившемся в результате действий игрока, и тем что так было изначально. А "метроидвания" это уже таки сложный жанр, который см. выше.
И тут меня посетила идея скрестить эти два варианта! Взять за основу изечение лабиринта - как в Dungeon Crawler но даже не закладываться на продвинутые системы типа инвентаря, прокачки и т.п. Зато встроить изменение уровня в зависимости от действий игрока.
В первоначальном варианте предполагалось что в лабиринте будет Н-тое количество монстров, которые изначально спят. Но по мере получения игроком предметов нуобходимых чтобы выиграть, они просыпаются и соответственно концентрация мобов растет...
Кроме этого часть проходов в лабиринте будут закрываться по мере нахождения ключей. Но так чтобы даже когда все ключи собраны, из любой комнаты с ключем можно было добраться до комнаты с выходом - в чем собственно и заключается смысл игры.
В хоже оценки сил быстро стало понятно что монстры потребуют много сил на то чтобы сделать модельки, анимации и ИИ. Поэтому они были отложены в ящик "если времени хватит". Spoiler Alert: Не хватило. 
Отдельно упомяну идею, которую предложила подключившаяся к мозгоштурму Omella - сделать CD-man'а (Pac-manа) на диете. То есть задача не сожрать все точки, а раоборот, дойти до выхода съев не больше критической массы, после которой наступает геймовер.СубботаСуббота.
Определившись с идеей, я начал разработку. Первым делом я понял что я, конечно могу сделать фиксированный лабиринт с вручную настроенными завалами,
но мне лень. ((И это не говоря уже о том что прейтестя его в 146й раз я приду к выводу что лабиринт плохой и перечеркну пару часов работы...))
Поэтому я начал думать о том как процесс автоматизировать. Есть несколько алгоритмов для построения случайного лабиринта с нуля, но я раньше их не пытался программирвоать. Смотрел пару туториалов, но вот на практике не реализовывал...
Первоначальная идея состояла в том чтобы создать несколько заготовок типа "комната" и "корридор" из которых скрип как из конструктора составил лабиринт. Но тут снова подняла голову лень. Такой подход предполагал изготовление нескольких предметов. Как минимум два вашеупомянутых. А еще бывают перекрестки, и повороты, да и комнат хотелось бы несколько вариантов. Хотя бы по числу и ориентации выходов. Поэтому в качестве строительных блоков появились клетки у которых обязательно был пол, а вот стенки появлялись бы только там где есть соседняя клетка.
Эту системы достаточно просто запрограммировать на самом деле, но вот только она сильно повышает количество степеней свободы генератора, а следовательно и вероятность на выходе получить бред, если не ограничить его полет фантазии.
И вот чтобы ограничить этот самый полет, я решил строить сам лабиринт не совсем случайно а по чертежу, считываемому из карты. Пиксельной карты нарисованной в Paint'е. Белый клетки пустые. Черные клетки - проходы и комнаты. Все корридоры не шире одной клетки (чтобы было достаточно перекрыть одну клетку чтобы сделать корридок непроходимым).
Далее пришла идея на этой же пиксельной карте цветами показать место для появления игрока, центральную кочку для победы, и "ключи" которые нужно в лабиринте найти.
Можно было, конечно, еще и вручную разметить что тут перекрываемые корридоры, но я захотел чтобы карты можно было легко создавать новые, и поэтому заставил скрипт самостоятельно находить на этом чертеже проходы.
Описание алгоритма распознавания проходов на чертеже заняло у меня полторы страницы рукописного текста. Далее шла короткое описание цикла, который сперва строит библиотеку маршрутов движения по лабиринту и размещает блоки так чтобы из каждой комнаты с ключем оставался неблокированный путь. ((На самом деле он строит список корридоков, которые "критичны" для комнаты, так как через него проходят все оставшиеся маршруты, далее выбирает жертву среди корридоров, которые не критичны, блокирует его, убивает все маршруты проходившие через него и повторяет процесс пока не останутся только критичные корридоры.))
Алгоритм распознавания сработал как часы. Но вот когда дело дошло до построения маршрутов, компьютер призадумался крепко... настолько крепко, что я решил что попал в бесконечный цикл и срубил все негуманно. Как минимум час времени был убит на поиски бесконечного цикла, которого не было. Просто счет возможных маршрутов через лабиринт шел на сотни тысяч, даже для моего не слишеом сложного лабиринта. Поняв это я потратил остаток вечера на то чтобы это алгоритм оптимизировать...ВоскресеньеВоскресенье
Сон принес мне несколько новых идей для оптимизации рассчета, которые позволили сократить время обсчета до десятков секунд, что было уже приемлимо.
Но в целях улучшения восприятия игры, я решил прикрыть простой игры на время рассчетов, "экраном загрузки" с ползущей полосочкой, показывающей прогресс.
До сего момента я никогда такое не программировал, и особую пикантность процессу добавляло то что ради того чтобы полосочка ползла, пришлось перенести основную часть вычислений в отдельную Нить...
Все эти излишества отъели еще примерно пару часов и вот около полудня в воскресенье у меня был играемый прототип... С графикой типа "герой-шарик"
и простыми гладкими стенками. И полом раскрашенным в заыисимости от того корридор это или комната, а также есть на нем блок или нет. Но это был уже прототип, который теоретически можно публиковать.
В этот момент к процессу разработки подключился мой "первый плейтестер" и "почетный зритель" - Максим.
Сынуля добросовестно заблудился несколько раз в лабиринте, обрадовался когда смог найти несколько ключей. Но явно остался доволен. Что стоило мне около часа времени, пока компьютер был занят, но показало что игра понравилась.
После этого я приспупил к созданию графической части. И... честно говоря сильно тупанул. Вместо того чтобы сначала сделать и вылизать "прастые" модели типа пола и стен, я взялся за модель персонажа. Гуманоидную.
Не сказать чтобы это был первый раз когда я начинал работать над гуманоидом, но в случае успеха это бы точно был первый раз когда я закончил. Spoiler Alert - не закончил.
Вернее модель-то как раз я закончил. В какой-то мере. И даже наградил скелетом для анимации... Но на все это ушло почти все воскресенье. Я взялся за анимации с базового "шагания" и смог сделать два сносных цикла, один для шага, воторой для бега, но попытка вставить их в игру привела сначала к тому что персонаж старательно шагал/бежал на месте. А попытка анимировать перемещение закончилась тем что перемещение анимировалось и в "стоящую" анимацию...
Пока я был занят этим увлекательным процессом, от конкурса осталось 2 часа... Пока было закругляться.
Я быстренько - на коленке - сделал текстуры для пола. Их же, растянув вдвое по горизонтали, использовал для стен... и в таком виде игра и пошла в народ...
Для интересующихся результатом, все ссылки тут:
ldjam.com/events/ludum-dare/40/maze-crawlerчитать дальшеПосле публикации я не остановил разработку.
Небольшой допилке подвергся алгоритм построения дабиринта. Так например часть проходов не просто отвечается для блокирования, а тупо удаляется. Целиком и сразу. Поэтом при загрузке одного и того же уровня раскладка получается немного разной.
Я таки смог освоить как делать аримации движения и как из подключать в игру. Осталось доделать еще парочку направлений (для более гладкого смешивания) и можно будет таки добавить в игру персонажа.
Кроме этого я придумал несколько способов доработать основной геймплей чтобы поощрить backtracking. Да, он не всегда считается хорошей вещью, но здесь основной концепт будет работать лучше если у игрока будет повод повторно посещать те же места.
Однако эта доработанная версия пока нигде не опубликована...
Но у меня и так походу больше проектов чем я могу прожевать. Начинать еще один это уже как-то... наверное не сейчас.