Архитектура конечных автоматов в Unity для организации поведений юнитов
Первым этапом разработки моей игры стала разработка RTS-движка. Я планирую написать серию постов о возникших задачах и их решениях в этом блоге. В этом посте я расскажу как организовал поведения юнитов.
Размышляя с чего начать вообще этот RTS движок, я пришел к выводу, что стоит начать с конкретики и от нее перейти к абстракции. Первой прикладной задачей, пришедшей на ум, был сбор ресурсов, а точнее, добыча дерева.
Обычно этот процесс в большинстве стратегий заключается в том, что работник при получении указа рубить дерево отправляется к дереву, некоторое время машет возле него киркой топором, затем отправляется к складу и начинает заново.
То есть процесс выглядит примерно так:
Чтоб этот рисунок по настоящему мог претендовать на имя автомата, ему не хватает условий переходов между состояниями и начального и конечного состояния.
Тут все просто: автомат инициализируется с состоянием «Иду рубить», а конец работы происходит во время сдачи. Переходы между состояниями мы можем выразить с помощью следующих условий: «дошел до дерева», «нарубил полную охапку дров», «дошел до склада», «сдал ресурсы». При положительном ответе автомат переходит в следующее состояние, при отрицательном — остается в текущем состоянии.
В каждом из состояний автомата вызывается соответствующее действие при итерации. Так же набор действий может производиться и при переходе между состояниями.
Например, при итерации в состоянии «Сдаю», ресурсы из рюкзака юнита переместятся в склад ресурсов игрока, а при переходе из состояния «Иду рубить» в состояние «Рублю», запустится соответствующая анимация.
Еще отмечу, что сама ходьба не является «атомарной» операцией, встречается во многих поведениях и сама является поведением и по этому на самом деле поведение добычи дерева использует внутри себя поведение ходьбы. То есть новый автомат может быть получен с помощью композиции нескольких других конечных автоматов.
Написав поведения подобным образом, мы получаем архитектурную границу между деталями реализации поведений и высокоуровневой политикой управления этими поведениями. Реализации поведений становятся по сути плагинами ко всей остальной игре, то есть изменения в них не повлияют на корректность работы высокоуровневой логики.
Работают эти поведения посредствам вызова метода iteration из события Update объектов типа Unit (это событие срабатывает каждый кадр). Для связи с остальным миром поведения вызывают методы IStateMachineListener«a.
Это пример работы конечного автомата строительства в моей игре. Юнит при получении команды строительства отправляется к укзанной точке, а затем переходит в состояние непосредственного конструирования, передавая единицы постройки зданию. Когда здание накопило достаточно единиц постройки, автомат строительства оканчивается и юнит получает новое поведение, поведение по умолчанию.
На этом всё. Если вам нравится или не нравится такой формат, то пишите об этом в комментариях!