Checkbox как машина состояний

Материал из wiki.appsalutecreator.com
Перейти к: навигация, поиск
Урок 2 << Оглавление >> Урок 4

Цель: изучить понятие машины состояний на примере объекта Checkbox

Задачи:

  • Cоздать новый экран и сцену для этого урока.
  • При помощи объекта Checkbox заставит слонёнка поворачивать голову.
  • Воспроизвести этого же слонёнка при помощи машины состояний.
  • Добавить разнообразия в поведении слонёнка.
  • Создать эмоциональную рожицу.


Checkbox

Подготовка

Начнём урок, как обычно, с открытия проекта lessons. В нём добавим новый экран lesson_3 со сценой stg_main, так как это было описано в предыдущем уроке. Поместим из редактора ресурсов на сцену любой фон из папки этого или предыдущего урока. Для примера далее используется "лунный пейзаж" (картинка bg).

Lesson 3 chk1.png

Поверх фона перетащите из редактора ресурсов тело слонёнка (ресурс body) и затем его голову head_1. Расположите их так, чтобы получился примерно целый слонёнок. Переименуйте эти 2 объекта в elephant_body и elephant_head_chk (помним о необходимости осмысленности в именах объектов?). Чтобы голова и тело стали "целым" существом, стоит в дереве проекта (Layout) перетащить голову на тело, сделав её подобъектом. Теперь, если на сцене начать таскать тело слоненка, то голова будет послушно следовать за ним. Хотя, если схватить за голову, то её легко можно оторвать. Так, что будьте со слоненком поласковее.

Создание чекбокса

Lesson 3 chk.png

Зайдем в свойство головы elephant_head_chk и изменим её тип на checkbox. Раскроем треугольнички возле свойств "не отмечена (внешний вид)" и "отмечена (внешний вид)". Поля не отмечена (внешний вид) и отмечена (внешний вид) содержат по три ресурса (up, down и over).

Не отмечена (внешний вид): up - так выглядит чекбокс, пока мы на него не нажали; down - так выглядит чекбокс, если мы на него нажмём, но не откликнем в не нажатом состоянии; over – так выглядит чекбокс, если мы на него наведём мышкой, в не нажатом состоянии.

Отмечена (внешний вид): up - так выглядит чекбокс, когда мы на него нажали; down - так выглядит чекбокс, если мы на него нажмём, но не откликнем в нажатом состоянии; over – так выглядит чекбокс, если мы на него наведём мышкой, в нажатом состоянии. Разместим поверх окна редактор ресурсов и перетащим в поле up первой группы (не отмечена) картинку head_1, а в такое же поле второй группы (отмечена) картинку head_2. Делается это также, как и формирование внешнего вида кнопки из предыдущего урока.

Сохраняем проект и запускаем вьювер. При клике на голову слонёнка она должна туда-сюда поворачиваться. Вы узнали в этом поведении чекбокс? Обычно чекбоксы (сheckbox) это такие квадратики в которых ставят или убирают галочку. Конечно и в игровом приложении может понадобиться подобный чекбокс. В этом случае необходимо будет подготовить соответствующие картинки. Простые игровые объекты (типа подобной головы) часто могут находиться в двух состояниях (выбран, не выбран). Они не похожи на чекбокс внешним видом, но являются им в силу правил своего поведения.

Теперь наша задача состоит в воспроизведении поведения чекбокса при помощи объекта другого типа - машины состояний. Мы не только повторим вращающего головой слоненка, но и придадим его поведению новые функции.

Машины состояний

Немного теории

Машина состояний (state machine) является универсальным инструментом для программирования в редакторе сцен сложного поведения объектов. Логика поведения машины состояний разбивается на отдельные узлы (состояния). Объект всегда находится строго в одном состоянии. Переход из одного состояния в другое происходит либо в результате внешнего воздействия на объект, либо в результате окончания некоторых процессов протекающих внутри объекта.

Любая машина состояний может быть изображена в виде графа с кружочками и стрелками. Настоятельно рекомендуется рисовать такие графы в процессе проектирования машины, а лишь затем переходить к их описанию в окне редактора состояний. У машины всегда активно строго одно состояние (ниже это изображено на левом рисунке темным кружком):

Def states.png

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

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

Проектирование машины

Перейдем теперь от нудной теории к веселой практике. Посмотрим внимательно на поведение головы слоненка, которая является чекбоксом. На самом деле это типичная машина состояний с двумя состояниями. Назовём их left и right по направлению ориентации головы слонёнка. Сначала слоненок находится в состоянии left (смотрит влево). Когда мы кликаем в голову, машина переходит в новое состояние right (голова поворачивается вправо). Это поведение жестко запрограммированно внутри объекта checkbox. Попробуем добиться того же эффекта, используя универсальный игровой объект "машина состояний".

Прежде всего не поленимся нарисовать весёлый граф состояний и переходов между ними:

Lesson 3 states.png

Теперь, когда на бумаге всё стало ясно :), необходимо эту машину создать в редакторе.

Создание машины

Чтобы не тратить время на редактор ресурсов, скопируем уже существующего слонёнка. Для этого станем в дереве проекта на его тело (elephant_body) и правой кнопкой мыши вызовем меню. В нём выбираем пункт Copy. Затем повторяем операцию, но выбираем уже пункт вставить ("Paste after curent object"). В дереве проекта появятся два слоненка. Причем оба будут иерархичной веткой, состоящей из "тела" (elephant_body) и подобъекта "голова" (elephant_head). А вот на сцене редактора видимых изменений не произойдет. Мы помним, что при копировании объектов копируются также все их свойства. Поэтому на сцене, на самом деле, два слоненка, но они имеют одинаковые координаты. Поэтому хватаемся за тело и тащим его правее на свободное место:

Lesson 3 2elephants.png


Левого слона оставим чекбоксом, а голову правого превратим в машину состояний. Для этого кликаем в голову и меняем её тип с checkbox на machine (поле тип в панели Properties). Поменяем также имя головы, укоротив его до "elephant_head".

Можно попробовать посмотреть, что у нас получилось. Сохраняем проект и запускаем вьювер. Упс... Голова исчезла, а вверху экрана появилось малопонятное ругательство, из которого можно заключить, что у объекта elephant_head состояний то и нет.

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

Программирование состояний

Сделаем активной голову правого слонёнка, которая стала машиной состояний. В панели её свойств кликаем на поле states, а затем на появившиеся в нём три точки (помним первый урок?). В результате этих действий, выскочит окошко в котором мы будем создавать состояния и их программировать. Два раза нажмём кнопку Добавить состояние в левом нижнем углу окна:


Lesson 3 states1.png


В результате появилось две области, каждая из которых будущее состояние головы. Зададим имена состояний, введя названия left и right в полях имя каждой области. Затем станем мышкой на поле имя левого состояния и нажмём правую кнопку мышки. Появится большое меню, в котором кликнем на первый пункт "draw - внешний вид":


Lesson 3 states2.png


В левом состояни под его именем добавится команда draw. Эта команда управляет способом отрисовки машины в данном состоянии. Нам потребуется ещё одна команда click, которую добавим аналогичным образом (правая кнопка мыши на команде draw).

В команды необходимо добавить параметры, определяющие их свойства. Для этого кликнем справа от draw и затем ещё раз на появившиеся три точки. В выскочившем диалоговом окне поставим галочку возле пункта "графический ресурс" и нажмем кнопку Ok:


Lesson 3 states dlg.png


Аналогично кликаем на команду click и добавляем ей параметр "go - новое состояние". Те же действие проделываем с правым состоянием. В результате должно получиться:


Lesson 3 states3.png


Осталось заполнить поля команд. В поле go команды click левого состояния пишем right, а в тоже поле правого состояния пишем left. Это означает, что при клике машина должна перейти в то состояние, которое указано в поле go. Имена состояний вводятся с клавиатуры руками, поэтому необходимо следить за правильностью их написания.

Теперь откроем окно редактора ресурсов. Сделав активным значение поля res команды draw левого состояния, перетащим в него из ресурсов картинку head_1. В аналогичное поле правого состояния перетащим картинку head_2. Делается это точно так, как мы задавали поля внешнего вида кнопки или чекбокса в панели Properties. Финальный результат наших действий приводит к следующей машине (мы не будем больше приводить всё окно редактирования состояний):


Lesson 3 states4.png


Насладимся результатами нашей работы. Закроем окно с состояниями (крестик в правом верхнем углу). Сохраним проект и запустим вьювер (для ускорения можно не закрывать окно состояний, а кликнуть на иконки сохранения и запуска вьювера внизу этого окна). В результате, во вьювере оба слоненка должны вести себя абсолютно одинаково.

Ускорение работы и отладка

При программировании состояний машины можно использовать некоторые приёмы ускоряющие процесс. Так, в примере с головой, команды обоих состояний выглядят одинаково. Поэтому имеет смысл сначала создать одно состояние (left), наполнить его командами и параметрами. Затем нажать кнопку "Скопировать состояние". Состояние продублируется и останется только подправить его параметры.

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

Если машина создается из картинки, изменением типа, то не стоит глазами искать слово machine в списке типов. Необходимо на клавиатуре нажать клавишу M и затем Enter. Вообще, активнее пользуйтесь горячими клавишами. Например, если в дереве проекта (Layout) активна машина, то нажатие Ctrl+M приведет к открытию окна редактирования состояний.

При запуске проекта во вьювере можно следить за отладочной информацией о состояниях объектов. Это помогает понять почему что-то работает не так как надо.

Важно !!! Чтобы отладочная информация отображалась нужно для каждого наблюдаемого объекта (машины), поставить в поле отладка значение вкл.

Запустим вьювер и нажмём кнопку "F1". Появится небольшое меню с командами, которое гасится повторным нажатием F1. Кнопка "S" (на клавиатуре) выводит список всех активных объектов на сцене. Если текст информации совпадает с текстом фона, то эта информация будет плохо видна. В этом случае необходимо несколько раз нажать клавишу "C", меняя по кругу цвет текста.

Первые две колонки отладочной информации, это имя сцены и объекта. В колонке state видно текущее состояние. Понажимайте на голову слоненка, наблюдая как его состояние меняется с left на right и наоборот.

Запустите теперь во вьювере экран первого урока и нажмите "S". Покликайте на слоненка, его голову и солнышко, наблюдая как изменяется информация об этих машинах. Так как внутри состояний могут происходить некоторые процессы, их можно затормозить, нажав пробел (в этот момент солнышко остановится). Повторное нажатие пробела, снова запустит все процессы.

Немного нравоучений

Освоив машины состояний, дизайнеры обычно применяют их там где надо и там где не надо. Например, в этом уроке быстрее оформить голову слонёнка как чекбокс, а не как машину состояний. Поэтому прежде чем хвататься за шестеренку, стоит сначала подумать о том, каким игровым объектом можно реализовать данное поведение.

При работе над проектами, некоторые машины иногда повторяются. Их конечно можно копировать, подправляя соответствующие поля команд. Однако, на определенном этапе, возможно, стоит начать дергать руководство и С++ программистов, чтобы они оформили эту "типовую" машину в виде отдельного игрового объекта. Как это, например, сделано с чекбоксом или кнопкой.

Если Вы пытаетесь сделать что-то при помощи машины состояний, но получается очень громоздкий объект (куча состояний, переходов, команд), значит Вы делаете что-то не так. Либо машина плохо спроектирована, либо вообще эту задачу не стоит решать при помощи машин. Возможно это уже миниигра и необходимо подключать C++ программистов для её реализации.

Стоит также помнить, что работа над проектом это коллективный процесс. Спустя некоторое время Вашу работу может подправлять другой человек. Да и Вы к этому моменту станете другим человеком и все забудете. Поэтому относитесь максимально ответственно к процессу документирования. Это означает выбор осмысленных имен объектов и заполнение поля "описание", находящееся у большинства сложных объектов в Properties. Для машин это поле находится в окне редактирования состояний, сразу под состояниями. Например, в нашем случае текст документации может выглядеть примерно так: "При клике на голову она поворачивается влево или в право".

Правильно работающий незадокументированный проект - это плохой проект.

Усложняем машину

Непослушный слоненок

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

Для реализации этого поведения добавим в состояние right команду wait c параметром t, равным 1000 миллисекундам (что равно одной секунде) и параметром go=left В результате должна получиться такая машина:

Lesson 3 states5.png


До сих пор, переход из одного состояния в другое происходил в результате клика на машину. Это было внешнее воздействие. Однако, переход между состояниями может происходить в результате некоторых процессов внутри состояния. В нашем случае, при заходе в правое состояние начинает работать команда wait. Она ждет в течении времени, определяемом полем t. Затем, если задано поле go, машина сама инициирует переход в соответствующее состояние.

Сохраните и запустите во вьювере этот проект. Кликая на головы обоих слонят, сравните их поведение.

Сумашедший слоненок

В заключение, поиздеваемся немного над слоненком, сделав его голове такую машину состояний:

Lesson 3 states6.png

В результате слонёнок начнет нерегулярно крутит головой то влево, то вправо. В каждом состоянии он будет находиться случайное время от 1-й до 3-х секунд. Это определяется параметрами t и dt, которые задают интервал времени от t-dt до t+dt.

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

Машина эмоций

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

Smiles.png


Разберемся, что нарисовано на картинке. Каждая рожица - это отдельное состояние, отражающее настроение нашего существа. Стрелки - переходы между состояниями по командам click и wait.

Начинает работать существо из состояния S1.png. Как и любые существа, Оно любит внимание. Поэтому, при клике переходит в более жизнерадостное состояние S2.png. Если больше на него не кликать, Оно опять вернется в начальное состояние, а спустя еще какое то время начнет плакать S5.png, из-за полного отсутствия внимания. Впрочем, небольшой клик - и Оно снова довольно жизнью.

Обратим внимание на последовательность состояний S3.png, переходящих в недовольную гримасу S4.png. Излишнее внимание также вредно, как и его отсутствие.

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

В заключение, несколько общих замечаний:

  • Если машина имеет более двух состояний, её необходимо сначала проектировать на бумаге. Именно на диаграмме состояний отчетливо проявляется логика поведения машины.
  • Обычно не должно быть тупиковых состояний, из которых ни куда попасть нельзя. В эмоциональной машине с рожицами таких состояний нет.
  • Всегда необходимо выделять начальное состояние в котором машина начинает своё существование.
  • Памятью машины состояний не обладают, однако имитация памяти может быть реализована при помощи цепочки состояний, как было сделано выше с состояниями счастливой рожицы.

Упражнения

  • Уменьшите в 2 раза размер слонят (параметр scale в модификаторах свойств). Для этого достаточно поменять масштаб у тела, т.е. его scale распространится и на подобъект.
  • Копированием создайте третьего слонёнка.
  • Запрограммируйте его голову как машину, состоящую из 3-х состояний. В качестве картинки третьего состояния возьмите солнышко из первого урока. Пусть состояния при клике прокручиваются по кругу (left, right, sun,left,...)
  • Добавьте четвертого слонёнка с тремя состояниями и сделайте с ним что хотите.
  • Создайте эмоциональную рожицу
  • Посмотрите снова на описание машин состояний из первого урока. Стало ли понятнее их поведение?

Урок 2 << Оглавление >> Урок 4