Игромеханики для ARPG, или сферический диаблоид в вакууме

Подборка интересностей для диаблоподобных action-rpg, а также пример их реализации в игре со сферическим уровнем (вместе с советами по использованию игрового движка Godot 3).

Игромеханики для ARPG, или сферический диаблоид в вакууме

Замечу, что условным эталонным диаблоидом лично для меня является всё-таки первая Diablo, притом консольная версия, где двигаться нужно было стрелочками, а не мышкой. Поэтому диаблоиды на ПК мне обычно не слишком нравятся (включая D1 и D2) уже хотя бы по причине просто сумасшедшей нагрузки на мышь (вернее на кисть правой руки, бог с ней с мышью), с этими микроприцеливаниями во всё подряд. Руки должны быть равномерно загружены, а мышь должна знать своё место. Допустим, в League of Legends (путь это и не диаблоид) мышь используется довольно интенсивно, но сверхточные клики мышью не требуются, а нагрузка более менее распределена по обоим рукам, в то время как от управления Path of Exile и прочих "классических" представителей arpg жанра устаёшь довольно быстро.

Конечно, у первой Diablo много своих недостатков, например, на той же консоли было сложно заходить в инвентарь, устанавливать заклинания, сохраняться/загружаться, покупка товаров через список довольно неудобна, ещё какие-то мелочи. Но уже ко 2-й части многое хорошее куда-то выветрилось и более не возвращалось в серию. Например, сама концепция исследования подземелий/этажей, что мне очень нравится, в отличие от беготни по всяким полупустым полям/степям с заходами в неглубокие (как новосибирское метро) мини-подземельями. Магические книжки находить интереснее, чем тыкать фиксированные навыки в дереве прокачки. Нет такого огромного перегруза лишними вещами/параметрами/союзниками, как в D2. Одним словом, при всех несовершенствах интерфейса, сама базовая формула первой Diablo мне как-то больше импонирует и если на что и хотелось ориентироваться, то куда-то в ту сторону.

Концентрированное здоровье

Лично мне в диаблоидах изначально импонирует именно система расходуемых зелий. Не бесконечное зелье, не постоянный встроенный реген здоровья, не лечилки по кулдауну. В этом плане хотелось чего-то похожего на реализацию зелий из Diablo 1 и Diablo 2 - имеется больше одного вида бутылки здоровья, но и не слишком много видов. А также сделать все виды бутылок более менее востребованными на любом этапе игры, чтобы не было так, что зелье А однозначно хуже зелья Б, а оно в свою очередь всегда хуже зелья В. Эти соображения привели к такой концепции:

Шкала здоровья получает дополнительный параметр - концентрация (насыщенность). Чем выше концентрация, тем насыщеннее цвет шкалы. Концентрация падает с течением времени. От текущей степени насыщенности шкалы здоровья зависит то, как она реагирует на выпитое зелье. Естественно, сначала выпиваемое зелье добавляет шкале концентрацию (если оно повышало её), а потом уже определяется эффект:

Если насыщенность шкалы слабая, то здоровье восполняется с некоторой задержкой. Если насыщенность средняя, то здоровье восстановится мгновенно. Если насыщенность близка к предельным значениям, то выпитое зелье дополнительно вызывает эффект регенерации (фиксированное время или пока насыщенность не упадёт до средних значений).

<i>Здоровье низкой концентрации, средней и высокой</i>
Здоровье низкой концентрации, средней и высокой

Самих зелий может быть 2-3 вида, которые могут выглядеть следующим образом: простое зелье (не влияющее на концентрацию), среднее (незначительно повышает концентрацию) и сильноконцентрированное (значительно повышающее концентрацию). Таким образом сильноконцентрированные зелья не настолько необходимы, когда концентрации и так хватает - в такие моменты простые зелья становятся актуальнее. Для ещё более сильного сглаживания между степенью полезности различных зелий можно сделать так, что зелья попроще и здоровья прибавляют больше. Например, условно, простое зелье восполнит 50% шкалы, среднее 45%, а сильной концентрации - 40%.

<i>Зелье без влияния на концентрацию, слабого влияния и сильного влияния</i>
Зелье без влияния на концентрацию, слабого влияния и сильного влияния

По аналогичному принципу могут работать и бутылки/шкала маны (но не обязательно).

Для монстров и всяческих ловушек насыщенность шкалы тоже может являться одним из параметров, на которые они могут воздействовать. То есть какие-то монстры наносят повреждения, которые не влияют на концентрацию, а какие-то снижают её тоже. Определённые эффекты ловушек и вражеских заклинаний могут "отравить" концентрацию персонажа, тогда она будет падать намного быстрее. Либо временно "заморозить", мгновенно понизить её, "проклясть" героя (тогда любые зелья не дают концентрации, пока "проклятие" действует).

ID-способности

Систему заклинаний можно построить на том, чтобы герой находил особые камни, которые может складывать в различные комбинации, получая, например, активируемое заклинание из пары определённых установленных камней.

Это может быть реализовано через особую систему идентификаторов. У каждого камня есть свой идентификатор (двух-трёхзначные номера вида "21", "345", "119", "70"), когда они участвуют в комбинации, то идентификаторы перемножаются и получаем, как минимум, 9 возможных исходов (по первой цифре результата - от 1 до 9).

То есть, если герою доступно одно активируемое заклинание, которое получается от комбинации двух камней, то различные сочетания камней в такой системе дадут максимум 9 вариантов заклинаний - для простого диаблоида не так уж мало. Если у героя два доступных слота заклинаний (например, одно для левой кнопки мыши, другое для правой), то второе заклинание может иметь свои отдельные 9 вариантов и в сумме получится уже 18 заклинаний для героя (но в каждый момент времени он пользуется только двумя и из разных списков).

<i>Разные пары волшебных камней дают разные заклинания</i>
Разные пары волшебных камней дают разные заклинания

Конечно, эту концепцию можно масштабировать в космические дали, если, например, брать первые две цифры от результата сочетания пары камней. Тогда камней с различными идентификаторами желательно добавить побольше, а вместо 9 исходов получится до 89 (числа от 10 до 99) возможных = 89 разных заклинаний. Более простой вариант, как уже было описано выше - до 9 вариантов заклинаний для каждого отдельного слота активного заклинания.

Пассивные способности могут регулироваться теми же камнями и слоты под них тоже могут иметь свои уникальные списки. То есть, допустим, может быть всего 9 доступных пассивных способностей, из которых выбираешь две. Или всего 18, но выбираешь первую пассивную способность из первых 9, и вторую из других 9.

Кроме определения вида заклинания, числа результатов комбинаций камней могут определённым образом влиять на что-то ещё. Например, на форму этого самого заклинания. То есть, имеем результат комбинации 2397, первое число устанавливает заклинание (например, 2 - это огонь), а второе число говорит о форме (например, 1,2,3 - это летящий шар, 4,5 - дыхание в конусе, 7,8 - фрагмент стены, 9,0 - расходящаяся волна).

Враги сами могут иметь идентификаторы и рассчитывать последствия взаимодействия с идентификатором каждого заклинания. К тому же их реакция может быть установлена случайной для каждой новой игры, тогда игроку понадобится выяснять, к чему уязвимы те или иные монстры в каждой конкретной игре.

Эффект воздействия на монстров может быть и общим для них всех, при этом случайным для новой игры, то есть когда в скелета прилетает файерболл, то их идентификаторы умножаются и получается результат, например, 723392. А в текущем глобальном эффекте воздействия первое число 7 означает, например, эффект замедления монстра. Тогда, все такие же файерболлы в этой текущей игре будут всегда замедлять движение скелетов. Если игрок найдёт другие заклинания и монстров, которые тоже при взаимодействии будут выдавать результат начинающийся на 7 - те монстры тоже будут замедляться от таких заклинаний.

В следующей игре, например, первая 7 в результатах взаимодействия заклинаний и монстров будет означать ваншот - мгновенное уничтожение рядового монстра. В то время как первая 2 - уже знакомое замедление, 4 - минимальные повреждения, 5 - эффект отбрасывания, 6 - кровотечение и так далее.

Воздействие эффектов на монстров может быть более тонким, как некая, своего рода "магическая химия". Допустим, монстр запоминает первые две-три цифры каждого взаимодействия с заклинанием и "хранит в памяти" до 7 таких цифр. Тогда можно привязать какие-то эффекты к особенностям этой последовательности. Например, 3-ка "злит" монстра, если в этой последовательности накопится три цифры 3 (можно не подряд, а вобще), то монстр впадает в состояние берсерка. 5-ка, допустим, охлаждает конкретного монстра (пусть это будет некий стихийный элементаль, допустим, водяной) - две 5-ки в последовательности замедляют его, а три - замораживают. В то время как 8-ка того же монстра недраматически разогревает, то есть попавшие в последовательность 8-ки просто нивелируют охлаждающий эффект 5-ки. Или же разогревание тоже может быть драматическим, тогда две 8-ки разгонят элементаля, 3 сделают его кипящим, а 4 испарят. При этом каждая 5-ка тоже нейтрализует одну 8-ку. Ещё один монстр имеет волшебный щит. Допустим, три 7-ки отключают щит, после чего он открывается для эффекта двух 9-к, которые сразу убивают его. Таким образом можно развить эту систему, чтобы и сочетания заклинаний, последовательность их применения, играли роль - вроде того, что первым можно ослабить монстра и добить вторым.

"Органический" инвентарь

В различных диаблоидах и смежных с ними играх обычно встречается стандартная пара вариантов инвентаря - более "классический", когда предметы занимают разное количество клеток, и "упрощённый", где каждый предмет занимает одну ячейку.

Однажды, придумывая, как мог бы выглядеть инвентарь (трюм) у биомашинки, я вышел на концепцию некоей внутренней "органической" ёмкости, куда нужно компактно укладывать предметы, в то время как стенки этого контейнера прогибаются и деформируются, как бы обволакивая предметы. А когда предмет вынимается - некоторое время стенки контейнера сохраняют его форму.

Суть в следующем - базово "органический" инвентарь представляет собой версию "упрощённого", где предметы занимают одну ячейку, но здесь у каждого предмета имеется и некая зона влияния на окружающие ячейки инвентаря. От нулевой (тогда предмет занимает 1 ячейку), до 8 клеток (тогда предмет стремится занять все 8 примыкающих ячеек). Когда предмет перекладывается, то помещается в свободную ячейку (не занятую предметом или влиянием предмета), а на примыкающие ячейки распространяется зона его влияния - они становятся не пустыми и в них уже нельзя помещать предметы. Если предмет вынуть, его влияние с этих ячеек пропадает. Что всё это значит? По сути ничего нового - для того чтобы в инвентарь вошло больше предметов нужно перекладывать те, что уже там находятся, стараясь расположить зоны влияния предметов более экономным образом. Отличие в том, что такая перекладка выглядит менее квадратно-гнездовой и очевидной, более свободной и непредсказуемой, создающей впечатление именно засовывания вещей в рюкзак или мешок, таким образом, чтобы они расположились более компактно.

<i>Предмет слева имеет влияние на 3 клетки рядом с собой (выше, выше и правее и правую). </i><br />
Предмет слева имеет влияние на 3 клетки рядом с собой (выше, выше и правее и правую).

Поместим два таких предмета в инвентарь размером 4 на 4 клетки. Тот, что в центре - занял 4 клетки, свою и те, что заблокированы теперь его влиянием. Предмет вверху занял всего одну клетку, так как его влияние не затронуло прочие ячейки. Если расположить центральный предмет левее или ниже того, что в углу, то его влияние тоже частично уйдёт за пределы инвентаря, частично "наложится" на занятую другим предметом клетку.

К этой концепции можно прийти просто программируя инвентарь "классического" диаблоида - там на самом деле работает схожий принцип, просто правила немного другие. Перемещаемый предмет обладает такой же зоной влияния, но распространяющейся не вокруг, а вправо в вниз от корневой ячейки. Кроме этого там предмет не может быть перемещён, если в эту зону влияния хотя бы что-то попадает - она вся должна быть не занята. В "органическом" инвентаре, напротив, неважно, что окажется в зоне влияния - предмет может быть установлен в любую свободную ячейку, не занятую влиянием других предметов. В том числе и в ячейку с предметом, тогда они поменяются местами и перераспределят влияние.

Сферамида - сферический диаблоид на движке Godot

А вот и пример реализации вышеописанных игромеханик - одноуровневое демо игры SPHERAMYD. Можно играть в браузере или скачать билд для windows/linux:

Игромеханики для ARPG, или сферический диаблоид в вакууме

Герой может найти по 3 вида бутылочек маны и здоровья. Под использование каждой отведена отдельная кнопка в интерфейсе, срабатывающая пока есть зелья такого типа (как вариант, можно было сделать по 2 вида бутылок и вынести только 4 кнопки, или, например, всего один-два вида бутылок маны).

У шкал здоровья и маны есть параметр концентрации, который падает со временем (проценты, над шкалой). Простое зелье не даёт концентрации. Среднее - меньше ресурса, но и некоторое количество концентрации. Сильное - ещё меньше ресурса, но больше концентрации.

При низкой концентрации шкалы зелье восстанавливает ресурсы с задержкой. При средней концентрации - сразу. А при высокой - шкала постоянно регенерирует, пока концентрация не понизится до среднего значения.

<i>Маски и кольца пока не дают никаких параметров, а сами параметры героя пока не работают</i><br />
Маски и кольца пока не дают никаких параметров, а сами параметры героя пока не работают

Разные предметы блокируют разное количество пустых окружающих ячеек, попадая в инвентарь. Если же стараться укладывать предметы вплотную, то можно уменьшить количество заблокированных клеток, высвободив больше места. Зелья не имеют блокирующего влияния, то есть занимают 1 ячейку, не затрагивая прочие.

Отдельный вид предметов, которые могут лежать в инвентаре или в специальных слотах - это камни заклинаний. Комбинации камней в верхней строке этого отдельного мини-инвентаря определяют доступные герою заклинания. Первый камень и второй устанавливают заклинание на левую кнопку мыши, а второй камень с третьим дают заклинание на правую кнопку мыши. На данный момент комбинации дают одно из 4-х заклинаний.

Вот как всё это выглядит в процессе:

Прочие подробности о прототипе и его разработке

Сферческие подземелья

Сферический уровень собран из отдельных "комнаток". С точки зрения текстурирования "комнаты" есть цельные, пол и стены единой моделью с одной текстурой, а есть разделённые, где у пола отдельная тайловая трипланарно наложенная текстура.

<i>Отдельная "комнатка" с коллизией, она же без материала перед экспортом и множество комнат на уровне уже внутри редактора.</i>
Отдельная "комнатка" с коллизией, она же без материала перед экспортом и множество комнат на уровне уже внутри редактора.

Потолки сделаны отдельными моделями, и по задумке должны показываться только те их них, которые находятся перед игроком (исключая тот, что над ним). Пока что отрисовываются все, что попали в зону видимость камеры. Планируется сделать коллайдер, который будет включать нужные.

Отдельной .obj моделькой экспортируется коллизия со стенами, на ней развёртка не нужна, так как она не будет текстурироваться. Внутри Godot модель коллизии добавляется в MeshInstance, для которого вверху основного окна появляются инструменты создания коллайдера на основе полигональной сетки.

Кнопка действия

Предметы подбираются с пола автоматически, но для взаимодействия с некоторыми объектами требуется нажать кнопку E - сейчас это пустые капсулы-саркофаги. Также можно открывать двери, либо закрывать их. Для этого требуется зажать кнопку действия, тогда после некоторой задержки будет вызван метод открытия у последней обнаруженной поблизости двери.

Сами двери пока сделаны так - есть цельная модель каркаса и полностью закрытых дверей, есть модель с каркасом, где двери открыты. В момент открытия во время анимации также показываются дополнительные отдельные модельки створок, а одна цельная финальная модель меняется на другую. Таким образом, пока двери статичны - это всегда одна модель в кадре. С другой стороны, оптимальнее может быть оставлять каркас отдельной неизменной моделью, а для створок иметь цельную модель, когда они сведены. Естественно, можно было просто ограничиться всего 3-мя моделями - каркасом и отдельными створками, так как дверей в кадре всё равно не будет настолько много, чтобы лишние сетки стали какой-то заметной проблемой.

Утилитарная магия

При нажатии на пробел персонаж создаёт слабый магический выстрел, наносящий мало урона и медленно летящий. Он не тратит ману и всегда доступен, подходит для того, чтобы разбивать кувшины.

Враги

На данный момент имеется 3 вида противников - слизни, призраки и призраки-маги. Враги на уровне изначально пребывают в дезактивированном состоянии, но вокруг персонажа существует специальная большая зона, которая "размораживает" врагов при касании, или "замораживает" их обратно, если они вышли из её области действия.

Взаимодействие выстрелов персонажа с врагами устроено следующим образом - коллайдер выстрела настроен на режим мониторинга по маске, обозначающей противника. У монстриков коллайдер настроен только на обнаружение прочими, сам не следит за столкновениями, а коллизиях включен слой, обозначающий монстра. При столкновении выстрел вызывает в скрипте противника метод нанесения повреждений, а сам уничтожается.

<i>Сам выстрел не имеет слоя и прочие коллайдеры не отслеживают его</i>
Сам выстрел не имеет слоя и прочие коллайдеры не отслеживают его

Слизни просто путешествуют в том направлении, куда смотрят. Сталкиваясь с препятствиями меняют направление, а коснувшись игрока наносят ему повреждения. Тут стоит отметить, что на самом деле и персонаж и враги в этом сферическом мире не перемещаются линейно, а вращаются вокруг некоего общего центра.

Призраки мониторят окружающее пространство вращающимися рейкастами и, если обнаружат игрока - двигаются к нему (вернее в сторону столкновения). Если персонаж попадает в зону атаки, то призрак атакует. Призраки маги ведут себя похожим образом, но вместо ближних атак создают выстрелы и приближаются медленнее.

На самом деле призраки устроены таким образом, что в режиме обнаружения персонажа вращается сам их центр, расположенный в центре сферы уровня. Это нужно, чтобы враг разворачивался точно в сторону персонажа в момент попадания рейкаста. Для того, чтобы призраки не вертелись визуально вокруг своей оси, я сделал, чтобы сама моделька призрака компенсировала вращение центра, вращаясь в противоположную сторону (то есть призрак в режиме монторинга выглядит неподвижным).

Используемые скрипты, логика, практики

Способы связи с объектами

Несмотря на наличие в Godot сигналов, чаще всего используются сигналы встроенные, вроде добавления функции от события нажатой кнопки. Использовать самописные сигналы обычно не требуются, если только не идёт речь о том, что постоянно через код в сцене будет возникать что-то динамическое, требующее прикрепления к себе сигнала. Но даже в этом случае можно вместо сигнала просто передавать создаваемому объекту ссылку на родителя или другой управляющий скрипт, к которому тот может обращаться, вызывая какие-то методы.

Что касается получения получения ссылок на фиксированные объекты сцены, для дальнейшего манипулирования ими, то здесь имеется два основных ходовых варианта - прописать путь к узлу в инициализации скрипта или прописать там же поля экспорта, в которые можно будет скинуть ссылки на эти узлы в окне редактора.

Те объекты, которые сами имеют на себе скрипты, могут при первом появлении в сцене отправлять ссылку на себя в глобальный синглтон. Естественно, если там заведены соответствующие поля, изначально обозначенные как null. Например, это может сделать сам главный скрипт, чтобы прочие могли обращаться к его методам. Также удобно использовать такой тип создания ссылок для множественных единообразных элементов имеющих скрипт, например, для кучи однотипных кнопок. То есть для каждой кнопки через экспорт в окно редактора выводится её порядковый номер, а при первом появлении на сцене скрипт кнопки отправляет ссылку на кнопку в глобальный синглтон, в массив на позицию с её номером. Теперь можно иметь доступ к кнопкам, например, из главного скрипта - допустим, перерисовать их текст, при нажатии на смену языка в настройках.

<i>Например, под кнопки бутылок в глобальном скрипте заведён массив uibuttons_arr, куда они заносят ссылки на себя при собственной инициализации. </i><br />
Например, под кнопки бутылок в глобальном скрипте заведён массив uibuttons_arr, куда они заносят ссылки на себя при собственной инициализации.

Затем к ним можно обратиться, например к третьей по счёту кнопке - uibuttons_arr[3] (нумерация в массиве идёт с 0, поэтому в данном конкретном случае для удобства записи элементов 7, а первая ссылка всегда остаётся null, то есть uibuttons_arr[0] не используется )

Узлы-обёртки

В большинстве случаев удобнее крепить отдельные специфические элементы (модель, спрайт, кнопка, текстовое поле) к простому стандартному узлу, который будет выступать в качестве контейнера. Это очень пригодится, например, при создании анимаций средствами движка - вместо того, чтобы непосредственно анимировать какую-то модель (MeshInstance или префаб), анимируется узел-контейнер модели. Таким образом, если потребуется сменить модельки, то можно будет не переделывать анимацию заново, а подкорректировать уже имеющуюся. То есть вместо конкретной анимации "молоток ударяет по гвоздю", делается анимация вида "пустышка ударяет по пустышке", где в первую вложен "молоток", а во вторую "гвоздь". И позднее можно вложить в них другие модели, например, "топор" и "полено" (или заменить "молоток" и "гвоздь" высокодетализированными версиями), без пересоздания анимационного трека заново.

Что касается префабов - стоит продумать, какой именно узел будет "лицом" префаба, то есть корнем его иерархии. Лучше всего в корень помещать пустышку, но бывают исключения. Например, у 3д объектов с коллайдерами может иметь смысл вынести в корень саму Area с коллизией, чтобы вешать основной скрипт префаба сразу на неё. В противном случае получается, что если с коллайдером внутри префаба что-то столкнулось и вызывает в нём метод, то на коллайдере должен висеть собственный скрипт, чтобы это обработать. По этой причине у собираемых предметов в Spheramyd в корне префаба сразу находится Area, а вот у врагов там пустышка, но на внутренней Area свой посреднический скрипт, в котором проброшена связь с этой родительской пустышкой префаба. При инициализации родителя этот скрипт получает на него ссылку, а реагируя на столкновения перенаправляет их в вызов родительского метода.

<i>Получив событие ext_AfterCollision скрипт Area переадресует его корневому скрипту в виде вызова в нём метода ext_doAction</i>
Получив событие ext_AfterCollision скрипт Area переадресует его корневому скрипту в виде вызова в нём метода ext_doAction

Анимация-таймер

Можно использовать пустые анимации в качестве более наглядной замены таймеров. Например, в игре при некоторых действиях персонажа в его внутреннем аниматоре, внутри префаба модельки, проигрывается нужный трек. Чтобы не заводить скрипты внутри префаба, создавать визуальный таймер или лишние внутренние счётчики - создаётся специальный аниматор с пустыми треками, который запускается параллельно с анимациями персонажа. После проигрывания трека этот аниматор отправляет сигнал в главный скрипт. В пустую анимацию можно со временем добавить и какие-то дополнительные эффекты, не трогая префаб и анимации самого героя.

Колесо псевдослучайности

Техника, давно использующаяся, например, в консольных играх. Упрощённая замена стандартным генераторам случайных чисел, особо ничего и не вычисляющая.

Суть - имеем заранее созданную последовательность хаотически разбросанных чисел. Например, такой вот массив: [1,0,6,2,8,3,0,2,5,9,7,1,3,9,4,7,8,5,4,6]. К этому массиву прилагается указатель, направленный на конкретную ячейку массива. Каждый раз, когда нам требуется новое случайное "значение" - обращаемся к этому массиву, совершая сдвиг указателя на 1 или другое фиксированное число позиций, в итоге получая значение в той позиции, на которой теперь остановился указатель. Если указатель выходит за пределы массива, то перемещается в его начало.

<i>Указатель остановился на позиции 0</i>
Указатель остановился на позиции 0

Естественно, такой генератор имеет некоторые границы применимости, а также его начальное положение неслучайное. Тем не менее, в определённых играх можно обойтись только им одним. Кроме того, для расширения функционала и удобства, к одной последовательности может быть привязано несколько альтернативных, из которых мы будем брать прочие специфические случайные значения. То есть, крутится одно колесо, но вместе с ним как бы крутятся ещё несколько. Получив позицию на первом - можно взять такую же позицию с любых прочих колёс, где могут быть углы поворота, проценты, названия и прочие вещи.

<i>Указатель остановился на позиции 2, что также означает 30 градусов, 50 процентов и красный цвет в прочих связанных последовательностях</i>
Указатель остановился на позиции 2, что также означает 30 градусов, 50 процентов и красный цвет в прочих связанных последовательностях

В данном диаблоиде подобный генератор используется, например, для поворота осколков, остающихся от кувшина, стартового разворота слизней и молний.

<i>При создании цепной молнии она поворачивается на "случайный" угол, вызывая глобальный метод с параметрами 2,2 для определения этого угла.</i>
При создании цепной молнии она поворачивается на "случайный" угол, вызывая глобальный метод с параметрами 2,2 для определения этого угла.
<i>Посмотрим на сам вызываемый метод. Первый параметр (prirost) показывает на сколько сместить указатель, второй (what_ret) - из какого именно колеса забрать значение. Колесо rnd_numbers_arr содержит числа, rnd_angles - углы, третье колесо - скорость.</i>
Посмотрим на сам вызываемый метод. Первый параметр (prirost) показывает на сколько сместить указатель, второй (what_ret) - из какого именно колеса забрать значение. Колесо rnd_numbers_arr содержит числа, rnd_angles - углы, третье колесо - скорость.
<i>Если подняться выше по глобальному скрипту до объявления этих массивов, то можно увидеть что содержится в каждом колесе.</i>
Если подняться выше по глобальному скрипту до объявления этих массивов, то можно увидеть что содержится в каждом колесе.

Логика заклинаний

Из 4-х доступных заклинаний у двух есть занятные эффекты. Бумеранг персонаж может поймать и перезапустить снова. Каким образом это реализовано - когда половина времени жизни бумеранга истекла, он немного разворачивается и начинает реагировать на коллайдер персонажа. Если столкновение с персонажем состоялось, то время жизни бумеранга обновляется, она начинает лететь в противоположную сторону и опять некоторое время не реагирует на столкновения с героем.

Цепь молний постепенно распадается на части. Каждая отдельная молния это один и тот же объект, но вот создаются они с разным количеством "заряда". В зависимости от "заряда" молнии ведут себя по разному. При максимальном заряде молния понимает, что создана самим персонажем, поэтому немного смещается в сторону от точки создания. При истечении времени жизни каждая молния создаёт копию себя с уменьшенным "зарядом", а также понижает свой "заряд" и восстанавливает своё время жизни. Если время жизни истекло и "заряд" понижать дальше некуда, то молния уничтожается.

Защита от множественного срабатывания

Иногда требуется по условию сделать с объектом что-то важное, необратимое, и только один раз. Например, удалить врага, у которого кончилось здоровье от полученного выстрела. Для гарантии того, что операция не произойдёт несколько раз (в том числе пытаясь удалить то, чего уже нет) желательно использовать в объекте какой-то регулирующий это флаг. Например, у монстра может быть переменная is_exist, изначально истинная. Когда монстр должен умереть, то это происходит только при успешной проверке is_exist на истинность. Тогда сначала is_exist устанавливается в ложное положение и далее происходит операция удаления. Теперь из тысячи одновременно прилетевших во врага снарядов, уничтожит его только один из них.

Игромеханики для ARPG, или сферический диаблоид в вакууме

Создание и импорт моделей

Для моделирования используется Blender. Собирается моделька с модификатором Mirror, разворачивается, красится. Затем сохраняется в отдельный файл, где отзеркаливание применяется и модель крепится к скелету. После этого создаются отдельные файлы с разными анимациями.

Игромеханики для ARPG, или сферический диаблоид в вакууме

Простые модели, без анимаций, я перекидываю в Godot в формате .obj, которые затем следует открывать внутри движка, устанавливая как форму MeshInstance. Нужно убедиться, что у экспортируемой модельки порядок с нормалями, применены модификаторы, есть развёртка (если надо) и экспортируется только она, ничего лишнего и скрытого. Также стоит удалить с модели материал, чтобы собрать его заново уже в Godot. При экспорте также может появиться файл .mtl, он не нужен (тем не менее, более старые версии Godot могут писать об ошибке с .mtl при закидывании .obj файла, но на это можно не обращать внимания).

Анимировать кости в Блендере нужно переключившись в режим Pose Mode, большинству костей хватает анимирования одного только ключа rotate, без scale и location. В процессе анимирования можно подправить веса костей, если возникают заметные искажения на модели. Если анимация должна быть зацикленная, то в конце анимирования, выделив скелет, стоит переключиться в окно Блендера Dope Sheet и там на панели выбрать Key - Interpolation mode - Linear, чтобы анимация шла равномерно, а не замедлялась в начале и конце. Естественно, для зацикленной анимации нужно, чтобы положение костей в начале было таким же, как и в конце (можно даже вынести конечные ключи за пределы итогового трека, на 1 фрейм, чтобы стыковка происходила ещё плавнее). Если не угадали со скоростью анимации в Блендере, то в Godot тоже можно будет управлять скоростью воспроизведения анимации у модельки, через параметр playback_speed.

Импорт анимированного персонажа

Объекты с костной анимацией я экспортирую из Blender в Godot в формате collada (.dae). Это не то, чтобы рекомендуемый вариант, но он работает. Материал с модели также удаляется, а вот модификатор арматуры применять не нужно.

После того как файл перенесён в Godot, нужно щёлкнуть по нему, открыть вкладку Import (слева вверху), в разделе Animation - Storage выбрать пункт "Files (.anim)" и нажать внизу кнопку reimport.

Игромеханики для ARPG, или сферический диаблоид в вакууме

Далее модель добавляется на сцену и нужно открыть её для редактирования (выбрав пункт new inherited). Там, в аниматоре (AnimationPlayer) меняем имя дефолтной анимации на желаемое название анимации и сохраняем эту анимацию как файл .anim. Также сохраняем саму открытую сцену персонажа как файл .tscn - это и будет префаб с моделькой.

Игромеханики для ARPG, или сферический диаблоид в вакууме
Игромеханики для ARPG, или сферический диаблоид в вакууме
Игромеханики для ARPG, или сферический диаблоид в вакууме

Теперь убираем со сцены модельку в формате .dae, и добавляем/используем тот полученный .tscn префаб. Сам файл .dae продолжаем хранить в ресурсах.

Добавление новых анимаций персонажу

Имеется в виду случай, когда у нас есть файл с тем же самым скелетом (возможно даже без самой модели), но с другой анимацией, которую хочется добавить в список анимаций ранее добавленного героя.

Порядок действий аналогичен импорту анимированного персонажа, с тем отличием, что после сохранения дефолтной анимации как файла .anim, закрываем открытую сцену персонажа без сохранения. Далее открываем префаб с персонажем и в аниматоре выбираем пункт Load, чтобы загрузить только что созданный .anim к списку анимаций персонажа.

Удаляем со сцены добавленную .dae, из которой взяли новую анимацию. Сохраняем сцену, после чего удаляем эту .dae и из ресурсов - после сохранения её анимации она больше не понадобится.

<i>в итоге в аниматор персонажа оказалось загружено много разных анимаций</i>
в итоге в аниматор персонажа оказалось загружено много разных анимаций

Фишки среды разработки

В Godot можно нажать Ctrl и кликнуть по строке кода с вызовом функции, чтобы переместиться в то место, где находится эта функция. Таким же образом, кликая по переменным можно перемещаться в место, где эта переменная объявляется. Выделенный код можно двигать вверх-вниз стрелками клавиатуры при зажатом Alt. Чтобы закомментировать или раскомментировать несколько выделенных строк сразу нужно нажать сочетание Ctrl + K. Кнопки Home и End перемещают курсор ввода на начало или конец строки кода. Если нажать Ctrl + C, не выделяя ничего, то в буфер обмена копируется вся строка на которой сейчас находится курсор ввода. В то время как кнопка Tab добавляет символ табуляции, сочетание Shift + Tab удаляет символ табуляции. Tab и Shift + Tab также работают при выделении нескольких строк сразу - добавляя всем шаг табуляции или удаляя его.

Если щелкнуть мышкой левее строчек кода, у самой границы окна, то появится красная точка - breakpoint. Также можно делать это кнопкой F9. Выше окна кода есть пункт "Go To", где во вкладках Bookmarks и Breakpoints находятся инструменты для работы с подобными отметками - поставить, снять, перейти к следующей.

Эффект просвечивания

Для того, чтобы силуэт героя подсвечивался, когда его перекрывает препятствие, нужно сделать вот что: у базового материала героя приоритет рендера сменить с 0 на 1, а в next pass добавляется второй материал с нужным цветом и флагами unshaded и no depth test.

Игромеханики для ARPG, или сферический диаблоид в вакууме
Игромеханики для ARPG, или сферический диаблоид в вакууме

Ещё немного вариантов игромеханик

В Spheramyd они тоже частично реализованы, либо предполагаются:

Ресурсы вместо золота

Имеется в виду разбиение единого универсального ресурса/валюты на отдельные, примерно равноправные виды (когда из этого планируется получить что-то новое для геймплея). То есть это может быть и вариант, с, допустим, делением единого золота на чёрное золото и белое золото. Или отсутствие золота как такового, но наличие каких-нибудь выполняющих его функцию цветных кристаллов - жёлтый, синий, зелёный и так далее.

Отсюда естественным образом вытекает, например, возможность крафта предметов: "для создания предмета N нужны ресурсы A,B и E". Если бы ресурс был единственный, золото, тогда подобный крафт смотрелся бы как покупка предмета у торговца.

Когда есть несколько ресурсов, то они вносят долю разнообразия в падающий с монстров лут. К тому же их можно закрепить за разными монстрами и прочими источниками сокровищ, чтобы добавить глубины игровому процессу - когда игроку нужен тот или иной вид ресурса, то он фокусируется на тех монстрах и источниках, которые его дают.

Опыт не для персонажа

Ввести в игру уровни персонажа - это универсальный хак, который всегда работает, легчайшим образом приносит чувство прогрессии и поддерживает интерес пользователя. Проблемы начинаются когда получение уровней затягивается, перестаёт на что-либо влиять или вовсе остановилось. Опять же в комплекте с подобной уровневостью практически всегда идёт линейность прохождения контента (а вот для того же жанра метроидваний, с нелинейным исследованием уровня и возвращениями в уже посещённые локации, уровневость персонажа как раз не обязательна, часто замещена какими-то находимыми усилениями, увеличением здоровья и собственно, новыми способностями), когда возврат в низкоуровневые места не имеет смысла.

Возможно, стоит вовсе убрать из игры уровни персонажа, сделать их незначительно влияющими на его параметры, сократить количество уровней или сделать систему "перезагрузки", которая так или иначе будет сбрасывать прогресс героя.

Также можно оставить некий получаемый опыт, но направить его не в получение уровней персонажа, а во что-то другое. Например раскачивать не героя, а его предметы и оружие, допустим, улучшая их в определённую сторону, затачивая в определённую стихию, увеличивая шансы попадания или просто приближая момент полного "опознания" предмета (когда выяснение свойств предмета происходит не от применения свитка, а именно раскрывается в процессе ношения предмета).

Предметами и оружием проще манипулировать, чем зашитыми в персонажа уровнями, параметрами и способностями. Они могут ломаться со временем, требовать подзарядки, не работать в определённым местах и ситуациях, против конкретных врагов. Одним словом - эти вещи относительно легко могут быть персонажем потеряны либо заменены, в то время как сбросить возможности зашитые в самого героя - более проблематично.

Получаемый опыт может быть направлен в ещё что-либо специфическое. Например, в получение неких ключей при каждом заполнении полоски, которыми в свою очередь можно открывать какие-то специальные сундуки, разбросанные по локациям. Одним словом, если нужны какие-то вещи, для которых запланирован постепенный гарантированный приход к игроку (в отличие от рандомного дропа с противников, который может долго не приносить нужных вещей или внезапно давать их слишком много), то можно привязать их получение к моментам накопления полоски опыта.

В роли специальных сундуков могут выступать и уже какие-то вскрытые игроком объекты, которые при первом осмотре дают какой-то дроп, раскрываясь не полностью, а по настоящему откроются уже при наличии у персонажа неизрасходованного ключа.

Переселение душ

Развитие предыдущего пункта, один из вариантов "перезагрузки" прогресса персонажа. Что если в качестве основного героя у нас будет некая душа, проживающая скоротечные жизни разных героев, теряющая при их смерти почти всё, но что-то и сохраняющая (хотя бы прогресс в исследовании локаций).

Персонаж, кстати, может тут вполне иметь стандартную уровневую прокачку, в то время как у самой души не будет уровней. Когда текущий персонаж умирает, то можно забыть про его уровни и про его лут - придётся искать другого. Возможно, сразу переселиться в какого-то заранее заготовленного или стандартного низкоуровневого. Возможно, побродить по окрестностям и вселиться в какое-то доступное тело поблизости.

При этом могут возникать дополнительные вопросы - насколько сильно стоит развивать эту сторону игры. Может быть дать душе выходить из тела в любой момент, подыскивая новые тела, и строить геймплей на этом. В таком случае, вероятно, посреди исследуемой местности могут быть некоторые преграды, через которые может пройти только душа, чтобы продвинуться дальше. То есть задача подконтрольного героя может заключаться в том, чтобы донести душу до очередной такой преграды, которую она сможет пересечь и найти себе нового носителя.

Chaosborn

Также у меня есть более ранний вариант заготовки диаблоида, собранный в фреймворке PlayCanvas. В нём герой просыпается/воскресает в некоем подземелье, выбираясь из саркофага и начинает свой путь по подземелью, постепенно попадая на этажи повыше. В Сферамиде в общем предполагается похожая концепция.

Попробовать в браузере:

видео демоверсии Chaosborn

Герой не привязан к некоей базе, типичной для диаблоидов - некуда вернуться и продать/купить предметы. Тем не менее, он может разбирать находимые им вещи на фрагменты. Чтобы позднее из фрагментов собирать какие-то полезности, например, те же зелья маны/жизни. Для сборки нужны свитки с рецептами. Вернее я сделал таким образом, что все рецепты доступны сразу (хотя в демо их доступно мало), в отдельном меню, но поначалу закблокированы, а открывать их можно за такую валюту как универсальные свитки (редкий дроп с монстров и прочих источников лута).

Вышеупомянутая концепция с душами здесь выглядит следующим образом: путешествуя по подземелью герой иногда находит прочие саркофаги, в которых кто-то лежит (помимо тех, из которых просто выпадает лут). Если герой умирает, то игрок переносится в одного из ранее найденных героев, которые воскресают в своих саркофагах. У них свой пустой инвентарь, но накопленные фрагменты, свитки и разблокированные рецепты переносятся, плюс начисляется некоторое количество бонусных фрагментов.

Используются зелья маны и здоровья, по одному виду бутылок для каждого ресурса. Применяются нажатием на символизирующие их кнопки - расходуется первая попавшаяся бутылка такого типа в инвентаре.

Предметы экипировки сделаны простыми, без дополнительных свойств, с фиксированными бонусами. Это несколько разновидностей предметов для слота маски, для слота артефакта и в слот кольца.

<i>Открытый инвентарь и слоты экипировки.</i>
Открытый инвентарь и слоты экипировки.

Что касается оружия - с ним всё сложнее. Герой у нас в целом чисто маг (да, вот так вот ультимативно - ну нравится мне играть в диаблоиды исключительно магами и прочими подходящими на их роль) и может стрелять простеньким слабым заклинанием без всяких проблем и без траты маны, правда оно очень медленно и недалеко летит. Если экипировать специальное хаотическое оружие, то выстрелы становятся более мощными, но расходуют ману. При этом выстрелы каждого конкретного хаотического оружия видоизменяются, в зависимости от скрытых внутри него комбинаций - наносят урон разными "стихиями", ускоряются, замедляются, становятся двойными, расходуют больше/меньше маны, замедляют или поджигают врагов.

При уничтожении врагов из них вылетают недолговечные искры, если поймать такую искру, то она втянется текущим экипированным хаотическим оружием, заняв одну из 9 его ячеек. Каждая вертикальная колонка из ячеек отвечает за какое-то свойство (модификатор) выстрела. Всего у выстрела 3 модификатора, они поочерёдно открываются и меняются по мере накопления оружием искр. Таким образом, побегав с оружием какое-то время, лишаешь его скрытых тайн и хаотичности, фиксируя эффекты. Но это ещё не всё, после накопления искр через некоторое количество выстрелов хаотическое оружие ломается, оставляя кристаллы-заряды определённой стихии.

<i>Справа внизу видно, что в текущем оружии собрано 4 искры.</i>
Справа внизу видно, что в текущем оружии собрано 4 искры.

Что же это за заряды такие? Дело в том, что имеется ещё такой вид оружия как жезлы. Если экипировать жезл, то он постреливает встроенной слабой магией, которая не тратит маны. Но в жезл также встроены пара заклинаний (огненный шар и молнии), которые расходуют не ману, а те самые кристаллы-заряды. Таким образом предполагается, что игрок периодически экипирует герою хаотическое оружие, ловит в него искры, а когда то оружие разрушается, то может иногда постреливать из жезла накопившимися зарядами.

По сути в хаотическом оружии тоже действует система умножения идентификаторов, но в несколько упрощённом виде. Также это похоже на концепт Куба с дарами из первой давней статьи с интересностями для ARPG, однако тот Куб планировался как вариант полной замены инвентаря вобще, в то время как в этом проекте мне захотелось иметь всё-таки более развёрнутого (классического) вида инвентарь и концепцию Куба пришлось несколько переделать.

На этом у меня всё.

3838
41 комментарий

Комментарий недоступен

4
Ответить

потрясающие идеи. Мне нравится когда люди делают не то что все. Я честно устал от этих зазывающих статей: поиграй в мою игру! она лучше всех!.Я прям в захлёб читал, как поток мыслей. Очень уважаю людей которые стремятся посмотреть на "игру" с разных сторон. Как вот художник ищет для себя мотивацию или там попытки разобраться с тем как рисовать корни. Класс.
Понимаю почему ресурс модит и почему предъявляет какие то требования к статье, но эта статья на вес золота, ей вообще цены нет.
Если есть админы, лучше помогите оформить.

3
Ответить

Спасибо. На Хабре тоже выкладывал в виде пары отдельных последовательных материалов - в одном сначала описание самих концепций, в другом уже пример реализации. Здесь уже более плотная статья.

1
Ответить

На Хабре больше в итоге не будешь публиковать ничего?

Ответить

А какой смысл, если пишешь статью, а потом тебе модераторы её внезапно прикрывают, мотивируя тем, что "тебе же лучше". Это уже полный произвол.
Вероятно, просто кто-то из пользователей пишет администрации в личку, диктуя администрации какие материалы он хочет видеть. Я бы не сказал, что на dtf с этим лучше, потому как тут организованная толпа пользователей чисто механизмами площадки может сливать кого угодно, но хотя бы администрация пока не шла у них на поводу и не сходила с ума.

3
Ответить

на хабре разве много кому про игры интересно?

Ответить

1. Механика концентрации для зелий, - достаточно годная тема. То что описано имхо можно ещё додумать, но в целом мне нравится.

2. Смешиваемые анимации способности чтобы искусственным образом поднять количество всяких способностей, из-за их комбинаций, - имхо подход ну очень на любителя. Это более-менее неплохо смотрелось в игре Magicka, но за рамки серии Magicka такое распространить не хотелось бы. Тем более что лучше чем в Magicka всё равно не получится (но даже там это на любителя). Так что подход сомнительный, в целом.

3. Якобы органический инвентарь, - тут я соглашусь, что как минимум проблема присутствует, и надо её как-то решать. Но предложенное решение мне не нравится от слова совсем. Куча каких-то пустых залоченных слотов, которые только занимают пространство, и нифига не интуитивно работают. Кривая механика.

4. Ресурсы вместо золота? Нууу, это вообще большая отдельная тема насчёт золота. Имхо я считаю что надо не избавляться от золота, а надо делать полноценную симуляцию с экономикой, чтобы золото не искусственно в мобах генерировалось и поглощалось в торговцев как в чёрную дыру, а чтобы золото было физическим обьектом мира, не только занимало инвентарь как в Stoneshard, но ещё и циркулировало по миру из рук в руки между NPC, а единственный способ где золото как бы «генерируется», - это чеканные фабрики (даже пусть очень простые средневековые). Но тут у меня конечно, такое, очень непопулярное мнение, потому что такие симуляции делать невероятно сложно.

5. Опыт не для персонажа, а для оружия и его предметов? Ну тут вроде идея достаточно годная с одной стороны, а с другой стороны если это система как в Dark Souls например, то как-то очень легко будет запороть прохождение, потому что ты все свои ресурсы потратил на оружие которому позже находишь более крутую замену, - но ресурсы уже истрачены. Так что либо приходится проходить сильно далеко вперёд, пока не будет избыток ресурсов чтобы прокачать второе оружие (а это может быть очень непросто), либо стартовать игру заново, заранее планируя расход ресурсов так, чтобы не вбрасывать все ресурсы в отстойное оружие, дотянуть до момента пока не найдёшь прикольное, и тогда уже тратить. Решится на такой рестарт игры довольно непросто, но и продолжать ходить с говном которое прокачал тоже не хочется. В итоге приходится находиться в этом диссонансе, либо дропать игру. Но точно такая же проблема может быть и с кривой прокачкой персонажа, - так что в этом смысле особо без разницы какой подход использовать. Имхо тут на самом деле вовсе проблема в другом. Просто слишком многие разработчики абузят игрока на прокачке. Как игрок, когда я понимаю что меня искусственно пытаются завлечь просто прокачкой, - сразу дропаю игру. Либо делайте это вторичным-третичным элементом игры, либо не добавляйте в игру прокачку вообще. Те кто ставит это во главе игры (wow, borderlands), - те сразу идут нахер. Diablo 1-2 в данном случае ходит по острому лезвию, но тот же POE пошёл дальше, он уже явно абузит игрока на прокачке (вы видели ветку способностей? это пиздец, ну его нахер).

6. Переселение душ, - офигенная идея. Хотя тоже не универсальная, как и комбинация способностей, поэтому такую идею лучше не пихать во всё что попало, во все игры. Нет. Она не подойдёт везде. Но какую-то одну хорошую игру построить на вот такой идее, - было бы прикольно. В отличие от комбинированных способностей Magicka, я тут конкурентов не вижу, так что идея будет ощущаться как свежий глоток, если сделано качественно.

1
Ответить