Новые виды врагов в игре
Добавил ещё несколько типов врагов, которые войдут в следующую сборку:
Электрический баг:
![Новые виды врагов в игре Инди игра, Gamedev, Инди, Разработка, Программирование, Рогалик, Roguelike, Pixel Art, Гифка, Длиннопост](https://cs14.pikabu.ru/post_img/2023/10/18/7/1697628257126294350.jpg)
Улучшенная версия стрелка, безвреден в одиночку, но, начиная от двух, создают электрические линии, которые наносят очень серьёзный урон.
Баг с минометом:
![Новые виды врагов в игре Инди игра, Gamedev, Инди, Разработка, Программирование, Рогалик, Roguelike, Pixel Art, Гифка, Длиннопост](https://cs13.pikabu.ru/post_img/2023/10/18/7/1697628335151152580.jpg)
Еще одна улучшенная версия. Периодически выпускает мощные снаряды по области, в то время как сам старается держать дистанцию.
Бронированный червь:
![Новые виды врагов в игре Инди игра, Gamedev, Инди, Разработка, Программирование, Рогалик, Roguelike, Pixel Art, Гифка, Длиннопост](https://cs14.pikabu.ru/post_img/2023/10/18/7/1697628383118742883.jpg)
Улучшенная версия червя. Начинает стремительно бежать на персонажа (подобно быку), когда подходит достаточно близко.
Новая фоновая текстура, которая куда лучше вписывается в игровой лор
![Новая фоновая текстура, которая куда лучше вписывается в игровой лор](https://cs14.pikabu.ru/post_img/2023/08/26/5/1693034092149048313.jpg)
Персонаж геймплейно - аватар программиста, который борется с разными проблемами в компе.
Поиграем в бизнесменов?
Одна вакансия, два кандидата. Сможете выбрать лучшего? И так пять раз.
Игра для кодеров. HackeRPG DevLog #1
Идея:
Около полгода я вынашивал идею игры в которой управление будет построено полностью на программировании.
Изначально задумка была крайне примитивной:
Жанр: Roguelite арена. Референсом послужил недавно нашумевший Brotato, но управление будет полностью кодом. А ещё можно будет писать свои функции, создавать переменные, потоки, демоны (не те, которые из потустороннего мира, хотя для новичков в программировании они могут быть куда страшнее).
Так выглядит игра референс. Порекомендовал бы её фанатам жанра, но думаю все и любители жанра и так знают эту игру.
В дальнейшем я несколько подробнее расписал своё видение и выделил механики, на которых будет базироваться интересный геймплей. Но это были лишь идеи, которые только предстоит протестировать. Ведь на бумаге идея GTA для Android с возможностью грабить караваны кажется крутой, но суровые реалии опыта игроков все расставляют на свои места.
Подготовка:
Для начала мне нужно определиться с инструментарием. В целом, мой опыт в программировании довольно насыщенный, поэтому критерий "сложность в освоении" был на меня на одном из последних мест.
Самыми важными для меня были:
Удобство.
Многим начинающим большое количество элементов визуального программирования и возможность руками расставлять объекты на сцене и на экране кажется крайне удобным, но не мне. Мне больше нравится всё делать через код. Поэтому уже на этом этапе я исключил для себя титанов вроде UE, Unity и Godot.
2. Лицензия.
Кодеры любят писать код и не любят делать что-то ещё (вот я например не люблю писать этот пост сейчас, но боль от того, что я буду пилить игру год и про неё никто не узнает куда сильнее, поэтому держусь). Лишние проблемы с лицензиями и авторскими правами мне ни к чему, поэтому UE, насчёт которого я ещё сомневался после первого пункта вылетает.
3. Перформанс и устойчивость к ошибкам.
Эти два пункта я объединил в один, чтобы точнее передать муки выбора - большая часть моего опыта - Kotlin и Java. На них можно создавать довольно хороший код с точки зрения ошибкоустойчивости + у меня есть небольшой опыт использования библиотеки LibGDX (популярный игровой движок на Java). Но, учитывая тот факт, что я тот ещё геймдевер, а JVM не самое лучшее решение для производительных игр, я решил отбросить этот вариант.
После чего задумался о языках семейства C, узнал о наличии довольно большого количества движков, но понял, что с моими навыками писать безопасный код (они на самом деле неплохие, просто холодно сейчас), это - не лучшая идея и я сел думать.
Так идея и продолжала консервироваться, т.к. я не мог выбрать идеальные для себя инструменты, до того момента, как однажды я не увидел статью про то, что Microsoft стремительно переписывает свою любимую ОС на Rust. Я немного касался этого языка, но по сути не знал о нём ничего и мне стало интересно.
Закинул себе на Kindle вот эту книгу и с каждой страницей всё больше начинал влюбляться в этот язык. Я подумал: "Вот оно!". И принялся искать игровые движки на этом языке, и довольно быстро наткнулся на Bevy.
Движок был относительно молодой и опенсорсный, что внушало некоторое опасение. Я изучил сайт (движок поддерживается неплохой, по меркам смертных, копеечкой), принцип ECS, что значит Data-Driven, пообщался с ребятами в дискорде и понял - это оно!
Как пробу пера я набросал небольшую визуальную новеллу (сейчас понимаю, что это была отличная идея, т.к. для адепта ООП такая парадигма разработки ломает привычное понимание и код получается ужасным), после чего понял, что мне подходит этот инструмент и можно браться за дело.
Вот так выглядела игра, арты любезно сгенерил старина Кандинский.
Прототип (Версия 0.0.1)
Начать разработку я решил с создания простого прототипа, который позволит тестировать механики и отбрасывать то, что не работает без лишних временных затрат. На первых порах я решил использовать бесплатные пискель арты, но очень быстро от них отказался, набросав сам все арты в игре (дешево, сердито, не идеально, но с душой).
Геймплей хотелось немного обосновать логически и я придумал для себя некоторую сюжетную основу игры: мы играем за кодера, который исправляет различные проблемы в системе (баги, вирусы и тд). Так и пришла идея первых двух видов врагов (жуки и черви).
По визуальному стилю я выбрал пиксель арт + стереотипный кодерский стиль (черный фон, зеленый текст). Изначально я хотел сделать всю игровую палитру одноцветной, но враги и снаряды персонажа терялись и было сложно адекватно реагировать на игру, из-за чего враги перекрасились в красный цвет, как и положено ошибкам в коде. После некоторого времени работы у меня получилось что-то такое:
Главное меню с окном в стиле MacOS и возможность почитать описание основных игровых команд.
А вот и наш игровой персонаж.
Крайне интересным опытом было написание своего интерпретатора, наконец получилось воспользоваться всей мощностью регулярных выражений. Помимо простой обработки команд, в первую версию так же вошла возможность добавлять свои функции по мере прокачки. Интерпретатор способен обрабатывать и их. Пока это решение ещё предстоит отполировать, но я уже очень доволен результатом.
Первое геймплейное видео.
Проблемы первой версии
Как это часто бывает, пока я тестировал игру, мой глаз замылился: я набил руку в управлении, из-за чего мне хотелось постоянно увеличивать сложность, все механики казались мне интуитивными, но плейтесты моих товарищей спустили меня с небес на землю.
Сложна, страшна, паника
Частая проблемы была в том, что когда противников становиться больше люди делали ошибки, начинали паниковать и получали много урона, не в силах камбэкнуть в игру, проблема серьёзная и я понял, что её нужно обязательно решать, поэтому добавил эту задачу к себе в бэклог.
Медленно, нет экшона, нудно
Идея управлять всеми действиями кодом изначально показалась крутой: некоторые действия могут быть сложными, тогда для них пишешь функцию, которая упрощает этот процесс. В будущем я планировал добавить больше инструментов, чтобы сделать ощущения прогресса ещё более сильным, но проблема в том, что сейчас таких инструментов мало, а нажимать около 10 клавиш, чтобы пройти пару шагов - очень медленно и надоедает. И даже наличие истории прошлого ввода (при нажатии стрелки вверх) особо эту проблему не решало. Проблема так же улетает в бэклог.
Обучение игре
Обучение управлению в процессе - довольно сложная штука, учитывая, что для игры в идеале использовать 6 команд. Однако освоение проходило довольно быстро, поэтому эту проблему я решил отложить на более позднее время.
Помимо этих проблем было ещё много вещей, которые я изначально (до плейтестов) хотел исправить, поэтому бэклог всё увеличивался. В версии 0.0.2 я решил взять прицел на улучшение пользовательского опыта, но об этом - в следующем посте.
Спасибо за уделённое время, потрогать игру можно по ссылке
Процедурная генерация простого подземелья в 150 строк (C#)
Всем привет. Решил поделиться кодом для создания подземелья, который я использую в своём рогалике. Код опубликовал год назад, но добавить пояснения и комментарии для начинающих руки дошли только сейчас.
Примеры генерации, как настроить канвас и прикрепить скрипт (первая минута): https://www.youtube.com/watch?v=_Vz9FDigioU
В игре есть тропический остров, на котором расположены руины, после обнаружения которых игрок спускается в подземелье состоящее из нескольких этажей (есть опция начать сразу с подземелья). Собственно с помощью кода создаётся основа для каждого этажа (но можно использовать и для создания интерьеров в наземных прямоугольных строениях). Работа алгоритма построения проста - сначала строится горизонтальная или вертикальная стена и в ней отмечается дверь (или несколько), таким образом пространство делится на две части, в каждом из которых происходит последующее разделение на две части и т.д. За основу взят метод построения правильных лабиринтов “Recursive Division”, посмотреть интерактивные примеры (в том числе и для десятка других алгоритмов) и код для ruby можно здесь: http://www.jamisbuck.org/mazes/
Чтобы в подземелье были «комнаты» и возможность нарезать круги добавлено ограничение на минимальный размер комнаты (int roomMinHorizontal, roomMinVertical) и шанс построения дополнительной двери (int extraDoorChance, minLengthForExtraDoor - минимальная длинна стены для дополнительной двери). В коде всего три метода, давайте посмотрим что они выполняют.
CreateDungeon() - главный метод, вызывается по нажатию пробела, строит подземелье и отрисовывает его на спрайте. В начале метода происходит проверка размера карты - по горизонтали и вертикали должны быть только нечётные числа. Затем идёт инициализация двумерного массива int[,] maze в котором и будет храниться карта. Далее - вызов метода разделения начальной пустой области Division(). Отрисовка полученной карты идёт с 39 по 56 строку - создается текстура и спрайт, текстуре присваивается цветовая информация (стены, пол, двери). Этот фрагмент можно использовать для отображения в игре мини-карты.
Division(int startX, int endX, int startY, int endY) - определение как надо разделить переданную область от стартовых координат (int startX, int startY) до конечных (int endX, int endY) на две части. В методе производится проверка габаритов области и если она слишком мала то выходим из метода через return - в этом случае дальнейшие действия в этом прямоугольнике не производятся. Если нет - создаём новую область через CreateRoom().
CreateRoom(bool isHorizontal, int startX, int endX, int startY, int endY) - непосредственно построение стены и дверей в определенной «комнате». isHorizontal - область по горизонтали шире чем по вертикали, т.е. будет ли построена вертикальная или горизонтальная стена. Сделано для того, чтобы комнаты не были слишком растянуты в одном из направлений. Это определяется в предыдущем методе через if (endX - startX > endY - startY), для квадратных комнат используется рандомное расположение стены. После этого проводится стена в массиве maze[verticalWall, i] = 1 (0 - пол, 1 - стена, 2 - дверь), через int doorNumbers определяем количество дверей в ней - как минимум должна быть одна дверь (но вы можете усложнить не добавляя на этом этапе дверей и создав телепорты для несвязанных частей подземелья). Затем вызывается Division() для каждой из двух областей разделённых стеной и происходит дальнейшее деление, пока не останется неразмеченных областей, после чего продолжится выполнение метода CreateDungeon() и будет визуально отрисована карта получившегося подземелья.
Итак, какие есть плюсы и минусы данного алгоритма. Построение комнат элементарно, при этом они все связаны и при дополнительных дверях появляется нелинейность прохождения. Также игровое пространство используется по максимуму, а с получившимися прямоугольниками в дальнейшем легко работать, расставляя в них мобов и дополнительный интерьер. В то же время, простота алгоритма определяет и его недостатки - некоторая однообразность и предсказуемость - в подземелье будет как минимум одна стена идущая от края до края. Более интересных и «пещеристых» подземелий можно добиться с помощью модификации этого алгоритма под названием "Blobby" Recursive Subdivision Algorithm (в ссылке выше примеры идут сразу после Recursive Devision). Однако, для своего проекта решил остановиться на первом варианте, поскольку мне удобнее работать с прямоугольниками, а вот внутри уже них я применял другие алгоритмы для создания самых разнообразных помещений, пещерок и коридоров. Вот примеры того как таким образом можно усложнить подземелье (игрок на карте отмечен зелёным кружочком):
Здесь коридоры на самом деле тоже находятся внутри прямоугольных комнат - они просто соединяют клетки дверей граничащих комнат, а остальное пространство заливается стенами.
Пару слов о рогалике Tzakol in Exile. Разработку веду с 2021 года, планировал опубликовать в прошлом году, но притормозил и отодвинул дату до лучших времён. Сейчас периодически добавляю новый контент и то, что раньше было демоверсией постепенно повышаю до уровня lite - бесплатной версии. В ней есть два режима - с мета-прогрессией и в стиле традиционных рогаликов (доступны за исключением нескольких последний этажей и сражения с финальным боссом). Планирую добавить ещё блиц-режим, который будет представлен в lite-версии полностью. Скачать демо можно в стиме: https://store.steampowered.com/app/1764840/Tzakol_in_Exile/
Если есть вопросы по коду или по игре - пишите в комментариях, постараюсь ответить.
Попытка разработать свою игру
В общем, история такая. Захотелось попробовать этот коддинг и геймдев своими руками. Сам я далек от програмирования. Даже от математики. Даже дос боксом лень было учиться пользоваться, всегда искал готовые сборки. Но, в общем и целом, добиться чтобы игра с первой соньки или с ПК, но 80-х годов у меня запустилась я обычно мог.
В итоге стало интересно, а как создавать игры. И да, оказывается, за пару дней с нуля, на движке юнити или гейм-мейкер вполне можно научиться создавать что то похожее на заготовку игры. Есть ещё, если не изменяет память, рпг-мейкер, там вообще всё предельно легко, но получится только j-rpg.
Спрайтовая анимация, скелетная анимация, физика объектов, работа с пресловутым техножреческим неологизмом "если это, то это", поиск не правильных запятых в простом коротком коде, поиск готовых бесплатных спрайтов, т.к. рисовать, а уж тем более на компьютере я не умею. В общем, интересный опыт, мне понравилось☺ правда, как создавать случайно сгенерированную карту я так и не смог. Хотя по многим другим вопросам на том же ютубе куча информации.
И всё шло классно. Оказалось что с нуля, за пару дней можно разобраться в базовых вещах. А, разобравшись, внезапно осознать, насколько титанический труд требуется даже для простенькой игры. Не то чтобы примитивной, всё же хочется сделать что то интересное, но и не навороченной.
А задумка была такая. Рогалик с видом сверху. ГГ должен пройти несколько этажей данжа и дойти до "финиша". До условного лича на дне катакомб. Прокачки нет, лута нет. Игрок управляет волшебником. Слаб в ближнем бою, но на каждый уровень случайным образом получает несколько заклинаний. Желательно не банальный файер бол, а нечто хоть немного специфическое. Ослепление врагов, телепортация в случайное место уровня. Что то в таком духе.
Спелы можно использовать ограниченное число раз. Каждый каст имеет шанс того, что на экране появится страшная надпись "Я ТЕБЯ ВИЖУ" и на уровне, за пределами взгляда игрока начнут спамиться враги, движущиеся к приключенцу.
А также на каждый уровень даётся определённое количество ходов, после чего надпись "Я ТЕБЯ ВИЖУ" появляется в любом случае.
Так что оптимально проходить уровни по стелсу, не вступая в долгий ближний бой и кастуя спелы только в крайних случаях.
Если даже, будучи обнаруженный личем, ГГ успевает добежать до спуска на следующий уровень, то всё ок. Жизни восстанавливаются, появляются новые спелы.
Вот такой стелс рогалик про логику в условиях рандома хотелось сделать. Но чет кажется не вывезу я это.
И на словах то задумка звучит простецки, но на деле не только лишь всем это по плечу.
Процедурная генерация для рогаликов: Миллионы, миллионы, миллионы pixel shop!
Вы когда-нибудь играли в Daggerfall? В классические рогалики? В Dwarf Fortress? Вам было интересно, как именно создаются в них здания, города или даже миры? Если да, то этот цикл (надеюсь) статей для вас!
Я принимаю участие в разработке Python-библиотеки для процедурной генерации roguelike-карт. После моих первых принятых в основной код генераторов карт я решила записать видео- и текстовые туториалы, которые подробно объясняют процесс работы кода.
Давайте начнем!
Каждый магазин представляет собой прямоугольную карту из клеток. Длина и ширина здания лежит в определенных допустимых интервалах, поэтому до запуска генерации мы проверяем входные параметры длины/ширины и кидаем исключение, если они слишком маленькие или большие.
Благодаря исключениям ТАКОЕ не сгенерируется никогда.
После проверок размеров идет большой блок с проверками типов стен, полов и магазина. Стены могут быть деревянными, каменными или кирпичными. Полы, в свою очередь, могут быть из паркета, из камня или из грязи.
Тип магазина может быть следующим:
- С едой
- С драгоценностями
- С одеждой
- С оружием
- С броней
- С зельями
- С инструментами
- С магическими предметами
Если тип стены, пола или магазина не указан, то он выбирается случайным образом.
С полученными типами стен и пола генерируем большую пустую комнату, которая и будет нашим магазином. Создаем переменные, которые будут разделять магазин на торговый зал и жилую комнату с мини-складом. После чего вызываем функцию создания торгового зала и передаем в нее эти переменные как длину и ширину зала.
Как работает эта функция?
Создаем новую пустую комнату. Причем так как мы УЖЕ проверили весь магазин на корректность размеров и материалы, для этих комнат ничего проверять не надо. Расставляем на карте выход из магазина, масляную лампу, прилавок и кучку монет (эти объекты генерируются в каждом магазине). После этого определяем словарь, в котором мы выбираем для каждого типа магазина определенные типы предметов.
Что у нас будет на этот момент.
Далее немного магии:
Создаем список, который хранит в себе все координаты для предметов в определенной части магазина. Далее для каждого предмета мы запускаем бесконечный цикл. В нем мы случайно выбираем координату для него. Если этой координаты НЕТ в списке существующих, то мы:
- Кладем предмет в эту клетку
- Добавляем координату в список
- Выходим из цикла
Картинка для тех, кто понимает клингонский питонячий.
Зачем вообще такие сложности? Допустим нам надо разместить на площади в пять на пять клеток двадцать предметов. Если мы каждый раз будем случайно выбирать клетку и класть в нее предмет, то у нас будет две проблемы:
- Первая: будет много пустых клеток
- Вторая: будет много клеток с двумя, а то и более предметами.
Поэтому мы запоминаем все координаты всех предметов, и каждый раз выбираем случайную клетку до тех пор, пока не выберем пустую, после чего кладем в нее предмет. Правда этот код уйдет в бесконечный цикл, если предметов больше, чем свободных клеток. Но такой случай можно закрыть совмещением этих двух картинок:
Описанным выше алгоритмом мы создаем витрину с товарами около дальней стены торгового зала. Потом так же раскидываем по магазину еще несколько предметов и кошку и возвращаем из функции карту торгового зала, которую записываем в отдельную переменную.
![Процедурная генерация для рогаликов: Миллионы, миллионы, миллионы pixel shop! Python, Процедурная генерация, Roguelike, Gamedev, Программирование, Indiedev, Туториал, Гифка, Видео, Длиннопост](https://cs10.pikabu.ru/post_img/2020/11/18/8/1605703936125691551.jpg)
Дальше мы вызываем функцию генерации жилой комнаты и мини-склада. Она работает практически так же, как и прошлая, так что ее можно описать и анимировать вкратце:
- Создаем комнату.
- Делим ее пополам.
- Создаем стены: теперь у нас две комнаты.
- Создаем двери.
- Расставляем по спальне кровать, факел и сундук.
- В зависимости от типа магазина, случайно раскидываем по складу определенные товары.
- И возвращаем карту.
![Процедурная генерация для рогаликов: Миллионы, миллионы, миллионы pixel shop! Python, Процедурная генерация, Roguelike, Gamedev, Программирование, Indiedev, Туториал, Гифка, Видео, Длиннопост](https://cs9.pikabu.ru/post_img/2020/11/18/8/1605703247129233561.jpg)
Мы опять попадаем в главную функции генерации магазина. Вклеиваем в него СНАЧАЛА торговый зал, а потом склад со спальней. Почему важен порядок? Дверь из торгового зала в склад была создана на карте со складом, так что если вклеить сначала склад, а потом торговый зал, то дверь просто исчезнет. Дальше при желании мы можем создать экстерьер магазина и перевернуть здание. Это даст нам в пару раза больше разнообразия с минимальными усилиями.
![Процедурная генерация для рогаликов: Миллионы, миллионы, миллионы pixel shop! Python, Процедурная генерация, Roguelike, Gamedev, Программирование, Indiedev, Туториал, Гифка, Видео, Длиннопост](https://cs10.pikabu.ru/post_img/2020/11/18/8/160570248118961998.jpg)
Готово! Теперь у нас есть генератор средневековых или фэнтези магазинов, каждый из которых будет узнаваем, но при этом уникален. Примерно таким же образом создаются процедурно генерируемые здания в различных играх.
Ссылка на исходный код генератора: воть. Также можно быстро и в красивом виде посмотреть, какие именно видео генерируются, в этом видео:
А с вами была Мурмия, всем спасибо, всем пока!