Tesserfact, обновление 01_02

О процессе улучшения прототипа походовой 3d тактики на Godot, до новой версии.

Tesserfact, обновление 01_02

Кратко о том, что изменилось в этой версии: добавлены модельки двух новых героев, всплывающие цифры урона, простой ИИ (который можно включить или выключить и играть в режиме hotseat), хоткеи для опций (кнопка Esc) и конца хода (Tab), включатель vsync (теперь по умолчанию отключено), переключатель перевода на русский язык (частично), кнопка сброса битвы, эффект обводки персонажей, и прочее

версия 01_02, процесс

Страница проекта (браузерная версия, билды для windows/linux):

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

Игровые модели

<i>Первый новый персонаж, Миранда, после всех предварительных перерисовок и правок</i><br />
Первый новый персонаж, Миранда, после всех предварительных перерисовок и правок

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

Tesserfact, обновление 01_02

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

Tesserfact, обновление 01_02

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

Далее, делаем копию персонажа и применяем модификатор зеркала, чтобы модель стала сплошной. Лучше убедится, что все вертексы в точках соприкосновения слились и спаять их вместе или воспользоваться функцией удаления дублей. На этом этапе можно добавить модели асимметрии и специфических элементов - браслет на одной руке, прядь волос с левой стороны, сумка на правом боку и так далее. Асимметричные элементы надо развернуть отдельно и расположить на той же текстуре.

Теперь создаём скелет (можно клонировать заранее заготовленный базовый) и привязываем его к модели. Для этого нужно сначала выделить модель (лучше перед этим сохранить её копию), лишь затем уже кости с зажатым Ctrl, затем зайти в меню Object и найти опцию Parent, где выбрать Bone, а затем with automatic weights.

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

<i>Тёмно-синий цвет - области на которые выделенная кость 011 не влияет. Красный, жёлтый, зелёный, светло-синий - места на которые она влияет с разной силой.</i><br />
Тёмно-синий цвет - области на которые выделенная кость 011 не влияет. Красный, жёлтый, зелёный, светло-синий - места на которые она влияет с разной силой.

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

<i>Переключаем тип интерполяции в линейный, чтобы не было затуханий по краям (дефолтный вариант).</i><br />
Переключаем тип интерполяции в линейный, чтобы не было затуханий по краям (дефолтный вариант).

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

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

<i>В о вкладке Import, в пункте Storage для файла .dae по умолчанию стоит Built-in. Нужно менять это на Files (.anim) и жать кнопку Reimport. Кстати, там есть ещё один пункт с названием Storage, где находятся другие опции - не стоит путать.</i><br />
В о вкладке Import, в пункте Storage для файла .dae по умолчанию стоит Built-in. Нужно менять это на Files (.anim) и жать кнопку Reimport. Кстати, там есть ещё один пункт с названием Storage, где находятся другие опции - не стоит путать.

Затем я беру файл с анимацией походки персонажа, как его главный файл. Сохраняю отдельной сценой (.tscn), и сохраняю её default анимацию тоже отдельным .anim файлом.

Если потом нужно добавить новые анимации этому персонажу, то закидываем файл с новой анимацией (для того же скелета) в движок, делаем реимпорт с включением .anim, открываем и сохраняем её default анимацию с новым именем, после чего закрываем открытый .dae без сохранения (теперь его можно удалить, в отличие от того, основного, .dae, от которого сохранена .tscn). Затем открываем главную сцену персонажа, и в её анимациях через пункт load загружаем тот новый сохранённый .anim. Теперь через код можно будет менять - какую анимацию персонаж должен проигрывать.

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

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

<i>С шейдингом</i><br />
С шейдингом
<i>Уже без шейдинга, как останется дальше</i><br />
Уже без шейдинга, как останется дальше

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

Ромео, следующий персонаж. 3D-адаптация героя из более раннего flash прототипа.<br />
Ромео, следующий персонаж. 3D-адаптация героя из более раннего flash прототипа.
Вот как он выглядел в 2д.<br />
Вот как он выглядел в 2д.
<i>Собственно, это был полноценный герой "Монстробоя" из мира Мглы. На фото часть его открытки с параметрами.</i><br />
Собственно, это был полноценный герой "Монстробоя" из мира Мглы. На фото часть его открытки с параметрами.

AI

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

Далее была спланирована простая схема базовая действий для AI, некий набор его шагов и приоритетов: старается продвинуться к уязвимому персонажу - если такой далеко, то старается ударить кого-то поблизости - затем передаёт ход. Уязвимая цель со временем может меняться, а если такой не обнаружено, то враг вместо этого недалеко смещается и пробует ударить вблизи.

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

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

<i>Сверху слева виден лист, фиксирующий попавшие в пересечение объекты. Сверху на некоторых клетках чёрные, смотрящие вниз призмы - так отмечались клетки, попавшие в область.</i><br />
Сверху слева виден лист, фиксирующий попавшие в пересечение объекты. Сверху на некоторых клетках чёрные, смотрящие вниз призмы - так отмечались клетки, попавшие в область.

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

Цифры урона

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

<i>самодельный атлас с цифрами</i><br />
самодельный атлас с цифрами

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

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

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

Прочие особенности

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

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

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

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

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

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

Таким образом непотраченные ОД сохраняются после атак до конца хода, просто далее их уже нельзя тратить конкретно на обычное движение.

Куда-то в эту сторону и хочется двигаться.

9
10 комментариев