Вступление
Сегодня я, наконец, расскажу, почему игровой ИИ, основанный на машине состояний (State Machine или просто SM), это ленивое подобие ИИ и не должно видеть свет в любом не пет-проекте.
Меня все также зовут Миша, я программирую больше 7 лет, последний год плотно занимаюсь игровым искусственным интеллектом, реализовывал его во всех возможных вариациях. А также успел увидеть немало говнокода со стейт машинами, где его довольно просто можно было бы избежать. Также, конечно же, веду телеграм канал, где рассказываю еще больше о разработке, карьере в геймдеве и прогреваю на менторство.
Почему нельзя?
Конечно, всегда можно сказать “да вот у меня элементарнейшее поведение, там нечего выделываться с ИИшными фреймворками” или “у меня жанр Idle, там мобы просто выполняют последовательность действий”. Но давайте вспомним цикл жизни практически любой игры. Да, мы делаем сначала core-фичи, только основной набор действий для мобов. Но как долго это длится? Как долго наши мобы из айдлеров не начинают менять поведение на ходу. Например, когда не могут что-то выполнить или когда просто хотят порадоваться просмотренной рекламке?
В такие моменты мы и понимаем, насколько были ленивы, выбрав машину состояний. Ведь у нее квадратичная сложность увеличения количества поведений. И еще все вместе вспоминем, что граф для состояний в наших стейт машинах ориентированный и умножаем количество на 2. То есть, 2 состояния - 2 стрелочки между ними, 5 состояний - 20 стрелочек, 12 → 132 и так далее.
Остаемся в зоне комфорта
Конечно, все мы следуем великой мантре “работает - не трожь!”. Поэтому как бы сильно ни стреляла нам по ногам эта стейт машина, мы остаемся с ней до победного (иногда) конца.
Второй стадией обычно идет “принятие” или как мы это называем “написание костылей”. То есть, чтобы бороться с квадратичной сложностью добавления нового поведения, программисты используют один из трех костылей или все вместе.
Рассмотрим их на примере NPC или условной RPG игры. Наш моб умеет некоторые действия типа: общаться с главным героем, ходить на работу, есть, спать, сражаться.
Any State. Этот синий квадратик в аниматоре знаком любому юнитисту. Проводя стрелку от него, мы показываем, что в это состояние можно перейти из любого другого. Чтобы не рисовать 20 стрелочек к 10-му состоянию, мы нарисуем 10, а одиннадцатую проведем от Any state. И все хорошо, пока мы не решим, что из какого-то состояния мы все таки не хотим переходить в это новое…
Говоря о нашем NPC. Хотим мы ему добавить для смерти отдельное состояние (может наконец-то раскошелились на анимацию не из mixamo и хотим ею похвастаться). Мы решаем “ну умереть он может когда угодно, значит можно сделать переход из any state. Все классно, только вот NPC давал герою квест во время диалога и теперь во время болтовни он может умереть и закончить сюжетную арку на 10 часов раньше. Поэтому придется все таки провести еще 10 стрелочек.Hierarchical state machine (HSM). Кому-то тоже знаком из аниматора как “вложенная стейт машина”. Если часть состояний одинаково связана с остальной частью стейт машины, то вполне резонно можно выделить ее в свою внутреннюю SM. Таким образом из 90 стрелок мы можем убрать условно половину. Но все снова идет по одному месту, когда часть таких состояний все таки должна иначе работать с внешней стейт машиной…
Вернемся к NPC. Мы решаем, что все его мирные действия (поесть, поспать, поболтать) достойны того чтобы лежать отдельно от его боевых навыков. Убираем их отдельно и смотрим на похорошевший граф состояний… Ах да, он все еще сюжетный квест дает, но вместо диалога убежал сражаться… Тогда распаковываем обратно и рисуем обратно 40 стрелочек…Слои.
- Понимаешь, стейт машины, они как лук
- Воняют? Доводят до слез?
- Нет, слои. У лука есть слои и у стейт машины есть слои.
Ставьте лайки, если узнали отсылку))
Если какое-то действие может выполняться независимо от части остальных, то его можно вынести в отдельный слой. По своей сути, это такая же стейт машина, работающая параллельна, но в этом же внешнем контексте. Она смотрит на те же данные и меняет состояния по тем же самым триггерам, что и основная SM. Но точно также все ничего до тех пор, пока ее параллельность абсолютна. Что как правило не так…
Геймдизайнер поиграл в скайрим и решил что наш NPC тоже может разговаривать во время некоторых действий. Например, он может делать это сидя за столом. Мы создаем отдельный слой для стейт машины, в котором есть только состояния общения. Но ведь персонаж не может уйти из-за стола, пока разговаривает. Тем более когда дает сюжетный квест. Да и разговаривать он может далеко не всегда. Что нам тогда приходится делать? Все равно опосредованно соединять наши стейт машины. Состояние общения должно точно знать что происходит в остальной логике персонажа. И та в свою очередь должна ждать, пока голова договорит. В итоге это даже хуже остальных костылей. Мы не только зазря пытаемся оптимизировать поведение, а еще и прячем зависимости состояний внутрь самого контекста игрока.
P.S. изначально примеры могли быть разнообразнее, но мне понравилось как один жалкий сюжетный квест портит абсолютно всю машину состояний. Очень разрушительный сюжет получается)
Боимся и поджимаем хвост
На самом деле, большей частью эта проблема берется из-за необразованности или лени. Кто-то либо боится либо ленится посмотреть на окружающий мир и на альтернативы, которые он предлагает, либо боится проиграть, реализуя более серьезные подходы.
И не надо далеко ходить, чтобы понять главную альтернативу нашей стейт машине - Behavior Tree или Дерево Поведений. То, что по умолчанию есть в Unreal и то, что и было использовано в альтернативу сжигающей себя стейт машине. То, что возвело противников из серии Halo в пример остальным игроделам.
Посмотрим на “фичи”, которая она предлагает из под коробки. Заметьте, ИЗ ПОД КОРОБКИ, для этого не надо наворачивать сову на глобус как мы делали с NPC:
Вложенность (не просто так оно деревом называется)
Глобальные переходы между состояниями, как следствие из этого
Параллельное выполнение состояний
Конечно, нельзя не забывать и про Utility AI. Он больше подойдет в случае когда состояний много и нам не хочется выстраивать связи между ними (вспомните Sims). Хотя выбор между подходами для написания ИИ посвящена целая отдельная статья.
Итог
Не бойтесь выходить из зоны комфорта и искать новые решения для стандартных проблем! И НИКОГДА НЕ ИСПОЛЬЗУЙТЕ STATE MACHINE ДЛЯ ИГРОВОГО ИИ!!! И конечно же подписывайтесь на мой канал и обращайтесь за менторством, если до сих пор не нашли работу в геймдеве) Спасибо!
Комментарии (13)
NickDoom
15.04.2024 09:11Стейтмашина — это моб с одним свойством (то самое состояние).
А вот если мы используем несколько свойств (BOOL ЗанятВДиалоге и так далее), то переходами из состояния в состояние (такое свойство мы тоже сохранили!) мы управляем гибко и адекватно, не раздувая количество состояний и переходов.
GospodinKolhoznik
15.04.2024 09:11+5Это статья-ирония? И её надо понимать с точностью до наоборот? Простоя ирония получилась настолько тонкая, что вообще неразличима.
Fitbie
15.04.2024 09:11+5Я прямо сейчас пишу большое дерево поведения для проекта и BT ни разу не панацея. Да, ряд плюсов имеется, но Боже упаси реализовывать такую дуру для каких нибудь аркад да roguelike. Отсутствие Transition замещается NodeState, с которым конечно проще работать, но потребуется написать и распихать не один декоратор. С разделяемыми ресурсами проблемы остаются, вообще главное отличие дерева - его высокий уровень абстракции и масштабируемости, и из-за этого отличия местами приходится накостыливать по полной, либо 2 вариант - дерево имеет тонну зависимостей, и при каждой новой фиче разработчик умывается своей кровью.
sedyh
15.04.2024 09:11Так обычно деревья решений руками никто не делает, их тренируют на датасетах как и любые другие ml-модели. Только зачастую не одно дерево, а совокупность, через bagging или boosting.
Nognomar
15.04.2024 09:11Простите, а вы сейчас точно про геймдев говорите? Тут речь о игровом ии, а не о ml-моделях.
sedyh
15.04.2024 09:11Думаю, меня смутила терминология. Речь сверху идет про desicion tree, а не behavior tree. В decision tree состояний, как таковых нет. Если нам нужна альтернатива fsm, то это будут именно behavior tree из ml.
not-allowed-here
15.04.2024 09:11далеко не спец в программировании поведения.... но помне так-то что выбирать, для описания состояний и поведения NPC, начинается еще на моменте первичной отработки сценария - когда для моба определяются степени свободы поведения, его адаптивность к условиям, влияние внешних факторов на это, ну и в целом вне сюжетные и сюжетные - факторы... В общем-то уже тогда становится понятна достаточность того или иного подхода для простой двери не будешь делать сложный AI, а вот для "сюжетных врат" которые требуют отработки реакции на разные моменты игры или "карму" героя придется...
SadOcean
15.04.2024 09:11+1Заголовок провокационный, но не соответствует содержанию.
Рекомендация то не про FSM в целом, а про использование их для ИИ.
bayan79
15.04.2024 09:11+2Так и не понял, почему State Machine не может быть вложенной ( и как следствие иметь глобальные переходы между состояниями) и почему невозможно параллельное выполнение состояний (даже не совсем понял, что это такое. Если к игровому объекту привязано 2 разных State Machine , можно ли считать это параллельным?) ?
Видимо, статья посвящена Unity, хотя не нашел в статье примеров использования ни того, ни другого, поэтому не могу точно сказать. Возможно, по списку хабов мне надо офильтровывать статьи, но хотелось бы в названии видеть, с чем имеем дело.С абстрактной точки зрения, что SM, что дерево - оба графы, с разными свойствами. Не понимаю, почему один из них по древним заветам ситхов низведен до абсолюта НИКОГДА-не-использования.
danil_gazizov
15.04.2024 09:11StateMachine NPC это не сугубо AnimatorController в юнити. От этого, видимо, и пошла казуистика данной темы. И BehaviourTree никак не заменит StateMachine. Суть StateMachine - как раз, наделить и ограничить NPC теми действиями, которые подобают контексту, равно как и заглушить те, что не подобают. И задать ограниченные правила перехода состояний. В правильно составленной StateMachine как раз, персонаж квеста не убежит нечаянно воевать, он будет в состоянии: "я зачитываю квест". И не перейдет в смерть, потому этого перехода не предусмотрено из состояния.
rbetik
Такая долгая подводка к фразе: «Давайте юзать behavioural tree» и на самом интересном месте оборвать статью. Вы бы хоть в кратце рассказали как с этими деревьями в Юнити работать