Рассмотрим ситуация моделирования поведения игрового объекта при возникновении определенных событий. Имеется класс игрового объекта с интерфейсом вида.
public interface IGameObject
{
void playAnimation(String name);
}
Функция playAnimation запускает анимацию определенного вида по имени. Текущий набор возможных анимаций
Passive - статическая поведения
Attack - анимация атаки
Damage_Passive - статическое поведения после нанесения повреждения
Damage_Attack - анимация атаки после нанесения повреждения
Death - смерть
Дерево переходов графа, описывающее конечный автомат с необходимым поведением, выглядит следующим образом.
Для реализации его на программном уровне, введем понятия состояний и событий.
События
Damage - объект получил повреждения
Attack - объект начинает атаку
Death - объект умер
При этом переход в конкретное состояние сопровождается запуском соответствующей анимации, которая зависит от предыдущего состояния.
Данное моделирование можно реализовать несколькими способами, например с помощью условий if else, но тогда будет сложно понять логику реализации из-за множества ветвлений. А если будут добавлять дополнительные состояния - это еще более усложнить саму программу. Проще всего данный алгоритм реализовать с помощью конечных автоматов. Конечные автоматы это конечное множество состояний моделируемого объекта плюс условия перехода из одного состояния в другое. Такая схема позволяет легко добавлять новые состояния и новые условия перехода, при этом не нарушая основную логику и оставляя читабельным программный код.
Продемонстрируем это на выше описанном примере. Небольшие конечные автоматы проще всего реализовывать с использованием конструкции switch case.
Ниже приведен участок кода для реализации выше приведенной логики
public classGameObjec implement IGameObject
{
private String _State = "Passive";
public void SetState(String state)
{
switch (state)
{
case "Death":
playAnimation("Death");
break;
case "Attack":
if (_State == "Damage")
playAnimation("Damage_Attack");
else
playAnimation("Attack");
break;
case "Passive":
playAnimation("Passive");
break;
case "Damage":
if (_State ==" Passive")
playAnimation("Damage_Passive");
else
playAnimation("Damage_Attack");
break;
}
_State = state;
}
}
В данном случае, для запуска той или иной анимации производится перевод объекта в необходимое состояние, и в зависимости от данного состояния и предыдущего, в котором находился объект, производится запуск необходимой анимации.
Как видно сама конструкция достаточно простая и легко читается. Также при усложнении логики (добавлении новых состояний и условий), достаточно добавить новую конструкцию case и описать новое условие, при этом прежние останутся неизменными.
Более сложные конечные автоматы реализуются с помощью матриц состояний и таблиц условий перехода, в результате чего в основной код остается неизменным, изменяются только данные, которыми он оперирует (состояния и условия). Такое описание позволяет использовать уже готовые реализации конечных автоматов.