Godot
May 21, 2023

Архитектурный косяк или как я забыл, что один скилл может иметь несколько зон поражения

Столкнулся с одной проблемой и удивился, как до сих пор на это не напоролся

Если вкратце, то у скиллов/перков есть сущность TargetStore

  • В него добавляются юниты, которые попадают в зону поражения (ну или по любому другому условию)
  • В этом же сторе прописывается условие, каких юнитов фильтровать (например, только врагов, или юнитов с меткой)
  • После чего TargetStore триггерит события о добавлении/удалении юнитов, и на них уже навешиваются всякие эффекты (например, нанести демедж, повесить статус итд)

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

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

Но вот незадача - TargetStore один на скилл. И когда зона деспавнится, вычищаются в том числе и юниты, стоящие в другой зоне))

В итоге перепиливаю этот кусок так, чтобы TargetStore принадлежал зонам, а не скиллам/перкам, но пока что выглядит оно костыльно


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

Вот, например, скилл-милишная тычка:

У этого скилла есть:

  • Скорость атаки (с отдельным таймером на длительность, на промежуток между атаками и даже на подготовительную АоЕшку, если нужно). Полностью кастомизируется по необходимости
  • Зона поражения, которая трекает только юнитов, враждебных к конкретному
  • Отдельная сущность для нанесения урона, с возможностью декларативно модифицировать урон и подписываться на события нанесения/убийства/итд

Каждый пресет под капотом создаёт нужные подписки на события и дёргает методы

Например, вот пресет для соединения скорости атаки с активатором скилла:

Как видите, из 5 пресетов в этом скилле 3 относятся к TargetStore

С его переносом из скилла в зону будет менее изящно. Как бы вся эта декларативная красота не рухнула от необходимости где-то в рантайме подписку создать


UPD: Actually, обошлось малой кровью. И получилось даже меньше кода, чем на первом скрине. Ибо в скиллах удалился HitAreaPreset.StoresTargets и TargetPreset.CleanupOnFinish

Теперь, раз TargetStore лежит в зоне, она же и будет автоматически заполнять/очищать стор

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

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

Например, в самом первом прототипе у роги должен был быть перк, с которым тычка критует в спину

Но, поскольку ассеты в игре чисто с видом сбоку, а юниты могут смотреть на 360, вопрос "а что есть спина" был неочевидным

По итогу "криты в спину" заменились на "дэш через моба навешивает оглушение" + "тычка критует по оглушению". С точки зрения действий игрока - результат по сути тот же. С точки зрения реализации - совсем другое


Есть один неприятный момент. При клонировании ноды с зоной, к сожалению, создаётся чистый TargetStore, а не клон того, который в ней был до этого

Это плохо, поскольку я хотел статично навешивать на "родительский" стор все нужные пресеты, и надеялся, что при клонировании оно всё продублируется

Нет, не продублируется :(

К счастью,DSubscribe умеет принимать не только DEvent напрямую, но и любой DTrigger, который при вызове вернёт DEvent. Как-то это в ФП по-умному называется, но вертел я, честно говоря, эти термины

Посему я просто сделал пресет, который подписывается на спавн зоны, вытаскивает из ноды тамошний таргет стор и создаёт подписки на все интересующие ивенты:

Декларативно-императивная эквилибристика

Этот пресет сохранит декларативность в скиллах, когда мы создаём простую подписку

Но, к сожалению, с пресатами такое не прокатит

С пресетами придётся подписываться на спавн уже в самом скилле и докидывать интересующие пресеты внутри функции:

См. remote_area.spawned

Но, в целом, не так уж и страшно

Вообще, можно было бы параметр TargetStore сделать триггером, и тогда была бы возможна вот такая запись:

Но тогда придётся переписать все пресеты, которые относятся к TargetStore

Так что пока придётся смириться с коллбеком в декларации

Работает - и уже хорошо!


Спасибо, что дочитал до конца! Лови пару скриншотов: