Tesserfact, обновление 01_02
О процессе улучшения прототипа походовой 3d тактики на Godot, до новой версии.
Кратко о том, что изменилось в этой версии: добавлены модельки двух новых героев, всплывающие цифры урона, простой ИИ (который можно включить или выключить и играть в режиме hotseat), хоткеи для опций (кнопка Esc) и конца хода (Tab), включатель vsync (теперь по умолчанию отключено), переключатель перевода на русский язык (частично), кнопка сброса битвы, эффект обводки персонажей, и прочее
Страница проекта (браузерная версия, билды для windows/linux):
Для начала я решил сделать пару новых текстурированных моделей персонажей, в таких же миниатюрных пропорциях и посмотреть, как их лучше стилизовать.
Игровые модели
Модельки проходят примерно такой цикл разработки - сначала в Blender создаётся половинка персонажа, с зеркальным модификатором. Когда базовая форма более менее закончена - проводим по модели швы, отмечая грани, где будет разрез в текстуре. Выделяем все полигоны модели и жмём unwrap, чтобы получить развёртки. Далее размещаем развёртки оптимальным образом, увеличив какие-то важных части (лицо) и уменьшив малозначительные (внутренняя сторона рук, стопа). Не помешает сразу же проверить нормали.
Теперь ставим силу света сцены на 1, размещаем свет, вешаем на персонажа diffuse материал (какого-нибудь цвета) и создаём в нём новую текстуру, не прикрепляя её к узлам шейдера. Отключаем из рендера все объекты кроме ламп и персонажа, нажимаем опцию запекания (bake), включив там просто diffuse или добавив что-то ещё, вроде AO (но под это и шейдер стоит усложнить). Получается первичная текстура, которую нужно сохранить и затем подключить её к diffuse, отрубив лампы. Сцену можно осветлить (поставив, например, 2) чтобы в режиме рендера получившаяся моделька выглядела поярче.
Уже можно красить по модели в самом Блендере, добавляя деталей - как по сетке, так и по текстуре. Главное прокрашивать отдельно на текстуре области, немного за пределами островков развёртки - если красить только по модели, то цвет обрезается по границе и уже в игре или на рендере цвет участка текстуры будет смешиваться с непрокрашенной областью за пределами этого участка и порождать нежелательные линии на текстуре.
Далее, делаем копию персонажа и применяем модификатор зеркала, чтобы модель стала сплошной. Лучше убедится, что все вертексы в точках соприкосновения слились и спаять их вместе или воспользоваться функцией удаления дублей. На этом этапе можно добавить модели асимметрии и специфических элементов - браслет на одной руке, прядь волос с левой стороны, сумка на правом боку и так далее. Асимметричные элементы надо развернуть отдельно и расположить на той же текстуре.
Теперь создаём скелет (можно клонировать заранее заготовленный базовый) и привязываем его к модели. Для этого нужно сначала выделить модель (лучше перед этим сохранить её копию), лишь затем уже кости с зажатым Ctrl, затем зайти в меню Object и найти опцию Parent, где выбрать Bone, а затем with automatic weights.
Если подвигать различные кости в режиме pose, то становится видно, насколько хорошо кости привязались к модели. Если совсем плохо, то лучше расположить их по иному или изменить модель более правильным образом. Если есть лишь отдельные искажения, то правим их переключившись на модель и выбрав режим рисования весов - кликая по списку костей соотнесённых с моделью, видим область их влияния и добавляем или снижаем его.
Когда скелет нормально привязан стоит сохранить сцену в отдельный проект, оставив в ней только модель и кости. Теперь можно делать анимации. Конкретно для этих персонажей я задавал по 60 кадров на одну анимацию и сохранял разные анимации разными файлами. Количество кадров выбрано произвольное и просто как некий единый стандарт, для удобства анимирования. В любом случае в игровом движке можно будет ускорять/замедлять полученную анимацию отдельно. Ещё один момент - для получившейся зацикленной анимации скорее всего стоит выделить её всю и перевести в линейный режим, чтобы анимационная петля лучше стыковалась (но тут зависит от вида зацикленного действия, для той же ходьбы это не так принципиально, для равномерного вращения вокруг оси - важно).
Для переноса в Godot подойдёт формат collada (.dae). Для того, чтобы сделать это, нужно убедиться, что в сцене присутствуют только модель и кости (без прочего мусора), а также удалить материал с модели (так как в движке будем накладывать его отдельно). И затем экспортировать в виде .dae файла.
В движке этому файлу нужно дополнительно сделать реимпорт, выбрав, что анимации будут сохраняется как файлы .anim. Чтобы в них не путаться, стоит каждому персонажу выделить свою папку, где будет хранится модель и отдельные анимации из файлов, которые будут закинуты позже.
Затем я беру файл с анимацией походки персонажа, как его главный файл. Сохраняю отдельной сценой (.tscn), и сохраняю её default анимацию тоже отдельным .anim файлом.
Если потом нужно добавить новые анимации этому персонажу, то закидываем файл с новой анимацией (для того же скелета) в движок, делаем реимпорт с включением .anim, открываем и сохраняем её default анимацию с новым именем, после чего закрываем открытый .dae без сохранения (теперь его можно удалить, в отличие от того, основного, .dae, от которого сохранена .tscn). Затем открываем главную сцену персонажа, и в её анимациях через пункт load загружаем тот новый сохранённый .anim. Теперь через код можно будет менять - какую анимацию персонаж должен проигрывать.
В итоге, со стандартным материалом, модельки этого размера в игре смотрелись размытыми и пересвеченными, поэтому я отключил в их материале шейдинг, что убирает самозатенение и влияние света на текстуру. И модели стали чётче и больше похожи на что-то рисованное.
Кроме того в движке можно сделать некий простой аналог эффекта cellsading (обводка) для ещё более "нарисованного" вида, добавив основному материалу второй проход, где тёмный материал применяется на чуть более увеличенную сетку объекта и отрисовывается позади текстурированной модели. Так вобщем и оставил.
AI
Для начала я завёл слайдер, включающий или отключающий будущий компьютерный интеллект, управляющий чёрными на доске. В дальнейшем он, вероятно, будет не нужен, но пока так.
Далее была спланирована простая схема базовая действий для AI, некий набор его шагов и приоритетов: старается продвинуться к уязвимому персонажу - если такой далеко, то старается ударить кого-то поблизости - затем передаёт ход. Уязвимая цель со временем может меняться, а если такой не обнаружено, то враг вместо этого недалеко смещается и пробует ударить вблизи.
Закодировано всё таким образом, что враг просто бросает область проверки на точку уязвимого персонажа и смещается ближе к ней, если область пересекается с клетками в его зоне движения. Если же враг атакует, то бросает область проверки уже на себя и если та задевает клетку в радиусе атаки, на которой есть живой противник, то враг совершает удар в ту сторону.
Для удобства я завёл лист с обнаруженными целями, который формирует область проверки при каждом запуске и специальные маркеры, появляющиеся на всех клетках попавших в пересечение, но в итоговом билде это всё скрыто.
Когда ходят чёрные и включен AI, то они каждый раз забирают управление себе. В их ход они крутят специальный таймер, переводящий действия с одного шага на другой. Для аккуратности предпоследний шаг откручивается назад, пока не сработали все важные пост-таймеры, и только когда они прошли - передаёт ход дальше. Если ход передан белым, то те перехватывают автоматическое управление, возвращая средства контроля игроку.
Цифры урона
Для отображения текста в 3д-пространстве в Godot есть Label3D, но я решил сделать другим методом, по старинке, составляя числа из отдельных нарисованных цифр. Для этого мне понадобилось нарисовать небольшой атлас с цифрами и накладывать его на 3д-спрайты.
Правда, так как билбордить отдельные спрайты с цифрой урона не имеет смысла (они не будут нормально складываться в число, из за того что поворачиваются на камеру независимо), приходится поворачивать к камере сам узел с этими спрайтами в коде (Label3D в этом плане куда удобнее, но его могло и не быть, а этот способ много где применим).
Был ещё вариант выводить цифры в 2д, цепляя к позиции мыши, или показывать их на квад через вьюпорт, но это всё немного более сложные и шаткие схемы (пробовал их уже в иных проектах).
В итоге, теперь урон показан всплывающими цифрами и принцип нанесения урона немного изменился. Теперь здесь действуют принципы системы "Монстробой" - атака оружием имеет 4 позиции с разным количеством урона, и во время атаки бросается кубик, выбирая одну их них. То есть белые сейчас атакуют словно одним, чуть более слабым оружием, а чёрные оружием с уроном посильнее.
Прочие особенности
В настольно-ролевой системе "Монстробой" у меня действовало такое правило, что после атаки (магии или иного атакущего действия) персонаж терял возможность передвигаться. В принципе, я хочу сохранить это и здесь, но на данный момент после атак двигаться можно.
Зачем это было в настолке? Во-первых, для ускорения сражений (на что у меня там был направлен целый комплекс решений), меньшего их затягивания и большей плотности происходящего. В компьютерных играх это тоже актуально - часто бывает, что тот же противник двигается всегда при любом удобном моменте, и до и после действий, даже когда особо нет нужды и постоянно разбегается в стороны. Что утомляет, заставляет бегать за огрызающимися недобитками через пол-карты и так далее.
Во-вторых, психология. За столом очень легко забыть, кто ходил, кто не ходил, поэтому окончание движения после проведения приёма даёт некоторую пользу и играющие меньше путаются.
В-третьих, там была ещё и механика Очков Действия. Которые расходуются как на движение, так и на действия не совсем боевого характера - вроде использования предметов (хотя, наносящие урон предметы-гранаты метать тоже можно, пока они есть). Плюс неизрасходованные ОД можно запасать (если владеешь такой способностью) и тратить позднее, уже на отдельные боевые приёмы.
Так вот, Очки Действия позволяли часто совершить ещё как минимум одно, третье действие в ход, пожертвовав дальностью передвижения, но как до, так и после атак. Например, герой, имеющий 3 ОД на ход, мог сходить не на все 3, а на 1 клетку (потратив первый ОД), ударить противника и выпить лечебное зелье, потратив 2 оставшихся ОД.
Опять же, Очки Действия очень хороши конкретно в настольной-ролёвке, так как у отдельных персонажей могут быть свои уникальные боевые опции за сколько-то ОД, а также любую произвольную заявку игрока на околобоевое действие персонажа мастер может на ходу гибко оцифровать, назначив ей какую-то цену в ОД.
Таким образом непотраченные ОД сохраняются после атак до конца хода, просто далее их уже нельзя тратить конкретно на обычное движение.
Куда-то в эту сторону и хочется двигаться.