Шаблон Состояние

Tulenber 20 March, 2020 ⸱ Beginner ⸱ 4 min ⸱

Казалось бы, при чём тут Ведьмак?

Сегодняшний пост продолжит тему, поднятую в статье под названием "Unity и Singleton" и расскажет о паттерне, который можно встретить в любой игре, в том числе и настольной - Состояние. Этот шаблон занимает в игровой индустрии одну из самых заметных ролей, он очень популярен применительно к таким аспектам игры как искусственный интеллект - состояние определяет выполняемое действие, анимация - отображаем анимацию прыжка или бега, интерфейсы - переходы между окнами, или даже системы диалогов(привет картинка с Ведьмаком!). Если присмотреться, то игры практически полностью представляют из себя контролируемые последовательности, а следовательно, могут быть описаны с точки зрения этого паттерна.

Теория и практика

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

С практической точки зрения, в отличие от синглетона, который имеет довольно типичную реализацию почти во всех языках, состояния могут иметь довольно разнообразный вид. В зависимости от цели он может быть очень большим, для описания состояния в рамках какого-нибудь эвента, а может иметь вид простого switch, для добавления поля с именем кота в UI. Поэтому приводить реализацию, которую можно было бы скопировать, не имеет смысла. За обычными примерами можно обратиться к приведённым выше статьям с теорией или поискать в интернете.

Ну раз с теорией и практикой покончено, перейдём к развлекательной части.

Unity

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

Для примера сделаем простую сцену, которая по клику мышки будет переключаться между состояниями "Active" и "Paused".

  1. Создайте сцену и разместить на ней UI элемент с текстом

  2. Создайте ассет AnimatorController - это объект шаблон для нашей машины состояний
    Open Test Runner

  3. На вкладке "Animator"(можно открыть по двойному клику на нашем новом ассете или через Window > Animation > Animator) создайте два состояния "Active" и "Paused"
    Open Test Runner

  4. Кликнув по стейту, можно добавить переход. Соедините стейты в обоих направлениях
    Open Test Runner

  5. Переключитесь на панель "Parameters" и создайте триггер "Switch State"
    Open Test Runner

  6. Выделите переход между "Active" и "Paused", в инспекторе выключите параметр "Has Exit Time", поставьте значение "Transition Duration" в ноль, добавьте "Conditions” - “Switch State". То же самое проделайте с переходом из "Paused" в "Active"
    Open Test Runner

  7. Создайте скрипт "Game"

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
    public class Game : MonoBehaviour
    {
        [SerializeField] private Text text = null;
    
        private Animator _animator = null;
    
        // Start is called before the first frame update
        void Start()
        {
            _animator = GetComponent<Animator>();
        }
    
        // Update is called once per frame
        void Update()
        {
            if (_animator.GetCurrentAnimatorStateInfo(0).IsName("Active"))
            {
                text.text = "Active";
            }
    
            if (Input.GetMouseButtonDown(0))
            {
                _animator.SetTrigger("Switch State");
            }
        }
    
        public void SwitchToPause()
        {
            text.text = "Paused";
        }
    }
    

  8. Создайте скрипт "PauseState"

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    public class PauseState : StateMachineBehaviour
    {
        public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
        {
            base.OnStateEnter(animator, stateInfo, layerIndex);
            
            animator.gameObject.GetComponent<Game>().SwitchToPause();
        }
    }
    

  9. Создайте на сцене пустой объект и добавьте в него скрипт "Game", выставите текст, который будет отображать текущее состояние, а также добавьте компонент "Animator" и выставьте в поле "Controller" созданный на шаге 1 AnimatorController
    Open Test Runner

  10. Откройте закладку "Animator", выберите состояние "Paused" и добавьте в него скрипт "PauseState"
    Open Test Runner

В результате всех этих действий мы получим сцену, в которой состояние переключается по левому клику мышки, "Active" проверяется из кода объекта напрямую, а состояние "Paused" выполняет код непосредственно из машины состояний.

Заключение

Данный метод похож на предложение почесать правое ухо левой ногой, но, с другой стороны, это реальный и проверенный механизм для создания машин состояний, хоть и предназначенный изначально для работы с анимациями. Не очень понятно почему разработчики не отделят функционал машины состояний от анимаций, превратив его в универсальный инструмент. Одно можно сказать точно - это один из основных инструментов при разработке игры на Unity, так что мы убили двух зайцев сразу и потренировались с Mecanim и вспомнили что такое паттерн Состояние. Хотя, вылавливая очередной баг в собственной реализации, этот способ может показаться не такой уж плохой альтернативой. Пока! =)



Privacy policyCookie policyTerms of service
Tulenber 2020