Как прошла моя попытка научиться использовать конечные автоматы.

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

Один из этих методов известен как «шаблон состояния», и именно его я пробовал.

В чем проблема?

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

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

Один из способов, которым мы можем реализовать такую ​​систему, заключается в следующем:

У нас есть четыре состояния из переменной enum, которая оценивается в серии операторов if в цикле обновления. Код может показаться немного неприятным, но для того, что мы намеревались сделать, он отлично работает.

Но тогда возникает проблема, когда вы хотите расширить систему, добавив больше состояний или добавив сложные функции для каждого состояния. Если мы добавим еще одно состояние, мы должны написать функциональность в том же файле, а также еще один оператор if, чтобы проверить, можем ли мы перейти в это состояние. Это кошмар, когда дело доходит до идеи «единой ответственности» для этого класса, поскольку он отвечает не только за каждое состояние и их функции, но и за их переходы.

Как мы можем решить эту проблему?

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

Схема может помочь

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

У нас есть конечный автомат, в данном случае называемый Context, который получает данные в форме State и функционирует на основе этого состояния. Само состояние действует как родитель для "Конкретных состояний", которые сами являются фактическими состояниями. Родительское состояние имеет функциональные возможности, которые постоянны для всех его дочерних элементов, в данном случае это "handle()".

Моя попытка реализации

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

В нашем случае у нас есть класс «PlayerMovement», который действует как «Контекст», затем у нас есть абстрактный класс под названием «Состояние», и, наконец, у нас есть сами состояния движения в форме «WalkingState», «IdlingState», «RunningState» и «JumpingState»; все они наследуются от State.

Давайте посмотрим на это в действии.

Вот класс PlayerMovement.

Затем класс «Состояние».

А вот и класс IdlingState, структура которого идентична остальным состояниям.

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

Этот метод разделения состояний на их собственные классы позволяет нам создавать пользовательские функции связным и чистым образом.

Можем ли мы улучшить его?

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

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

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

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

У меня мало опыта работы с иерархическими конечными автоматами, поэтому я не могу показать, как они реализованы, но я хотел упомянуть об этом.

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

Любые другие варианты использования?

Государственный паттерн очень универсален. Один из наиболее распространенных вариантов использования — игровой искусственный интеллект.

У вас может быть состояние перемещения врага, от «Атака» к «Патрулированию», «Преследование» к «Расследованию» и т. д. Вы также можете иметь состояния здоровья персонажа, такие как «хромает», «здоров» или «критический». ». Варианты использования почти бесконечны, поэтому понимание конечных автоматов определенно может дать вам преимущество в вашей карьере программиста.

Спасибо, что прочитали мою статью, надеюсь, вам понравилось читать ее так же, как я ее писал. Я надеюсь, что вы следите за моими следующими несколькими сообщениями.

До встречи!