Движение машин: жучки, ключи и многое другое

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

Цель: изучить процессы движения и способы взаимодействия машин.

Задачи:

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

Процессы и движение

До сих пор мы ограничивались только одной командой процессов - wait (ожидание). В этом уроке мы рассмотрим другие процессы. Существуют следующие команды движения (изменения объекта):

  • move - движение по прямой
  • rot - вращение
  • scale - изменение размера
  • alpha - изменение прозрачности
  • phys - движение в силовом поле.

В каждой команде достаточно много параметров. Например, можно сдвинуться (move) по горизонтали на dx пикселей за время t. Вместо времени можно указать скорость v в пикселях в секунду. Можно двигаться в определенную целевую точку с координатами (tx, ty) и т.д.

Ход конем

Lady bug 64.png

Рассмотрим простой пример в котором объект движется сначала вверх на 100 пикселей, а затем вправо на 200. В качестве такого объекта будем использовать ladybug (божья коровка или для краткости жучок), картинку которой можно найти в редакторе ресурсов в папке этого урока. Как обычно, перетаскиваем её на сцену, переименовываем и превращаем в машину состояний. Организуем сначала движние нашего жучка при помощи трёх состояний:

Lesson 5 bug1.png

Первое, стартовое состояние является запускающим. Если кликнуть на жучка, то он начнет своё движение. Когда перемещение по вертикали заканчивается (состояние up), жучок переходит в третье состояние (right), в котором движется вправо. Закончив движение, он возвращается в стартовое состояние и ждет очередного клика.

Координатная ось Y (вертикальная) на сцене направлена вниз, а ось X (горизонтальная) -- вправо. Поэтому, чтобы жучок начал двигаться вверх, его смещение должно быть отрицательным. В нашем случае это dy=-100. Эти 100 пикселей объект должен пройти за одну секунду (t=1000). В конце выполнения команды move происходит переход в следующее состояние, имя которого задается в поле "go".

При движении вправо, происходит смещение на 200 пикселей. Чтобы скорость движения вверх и вправо была постоянной, необходимо соответственно увеличить время движения (t=2000).

Точно такое же движение можно организовать в рамках одного, а не двух состояний. Поместите на сцену еще одного жучка, дайте ему другое имя и задайте следующую машину состояний:

Lesson 5 bug2.png

Команды move в состоянии way выполняются последовательно. Сначала отрабатывает нулевой move, а затем первый.

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

Последовательно и параллельно

И так, в рамках данного состояния все команды процессов с одинаковым названием выполняются последовательно. При этом команды с разными именами выполняются параллельно.

Def procesess1.png

Пусть в состянии есть несколько команд move и несколько команд rot (вращения):

  • move0 move1 rot0 move2 rot1 rot2

Независимо от их чередования, они выстраиваются в параллельные списки процессов:

  • move0 rot0
  • move1 rot1
  • move2 rot2

Эти списки начинают одновременно выполняться сверху вниз: отрабатывают команды move и одновременно с ними комананды rot.

Заставим нашего жучка при совершении хода конем поворачиваться на 360 градусов. Для этого поместим третью машину со следующими состояниями:

Lesson 5 bug3.png

Запустите проект во вьювере. Одновременно с выполнением двух команд move будет выполняться одна команда rot. Время выполнения rot равно суммарному времени двух команд move (t=3000=1000+2000). В результате, к концу движения, жучок поворачивается на da=360 градусов (da - изменение угла).

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

Циклическое движение

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

Lesson 5 bug4.png

Четыре команды move выполняются одна за одной. Затем вьювер натыкается на параметр loop=1 и еще раз выполняет это движение с самого начала. Последняя команда move служит для перехода в стартовое состояние ожидания клика.

Заметим, что нельзя было для перехода использовать wait. Она, как и остальные команды процессов, запустилась бы параллельно с командами и сработала бы сразу. В результате ни какого движения не получилось бы!

Если параметр loop=-1, то движение будет бесконечным. Этого же можно было добиться, поставив вместо loop=-1 параметр go с переходом в это же состояние (перезапустить состояние).

Управление командами

Кроме команды зацикливания данного процесса, можно из одних команд запускать другие (начинать процессы другого типа). Например, пусть жучок движется вправо в течении 1 секунды и вращается 2 секунды. Команда move закончится раньше команды rot. Пусть затем, закончив полный оборот (на уже остановившемся объекте), он начинает снова двигаться вправо. Реализуется это так:


Lesson 5 move rot.png

Команда rot зациклена (loop=-1). Кроме этого, она имеет параметр move=0 в котором задается номер команды move которую надо запустить на выполнение после окончания вращения.

Вызов одних команд процессов из других очень мощный, но одновременно и опасный инструмент. Множественные вызовы различных команд превращают состояние в спагетти переходов, логику которого часто сложно понять. Более того, иногда можно получить зависание вьювера при неудачной перекрестной ссылке двух команд друг на друга. По этой причине запрещено вызывать из данной команды другие команды этого же типа (из rot вызывать rot и т.д.). Однако это, к сожалению, не снимает полностью проблему зависания. Так, что готовьтесь...

Вращение вокруг пивота

При вращении (команда rot) машина вращается вокруг, так называемой, точки пивота (pivot - штырь, болт; опорная точка). В примерах выше эта точка находилась в центре картинки. Изменим теперь её положение и посмотрим к какому эффекту это приведет.

Откроем редактор ресурсов и в папке этого урока найдем солнышко. Сделаем пять действий:

  1. Кликнем на имя солнышка (sun_1) в дереве на левой панели
  2. Под этой панелью поставим галочку в чекбоксе Edit pivot.
  3. Схватимся мышкой за красный крестик в центре солнышка на правой панели и перетащим этот крестик влево.
  4. Уберем галочку в чекбоксе Edit pivot:
    Lesson 5 pivot.png
  5. Закрываем редактор ресурсов и обязательно обновляем ресурсную базу - нажатием на кнопку или на кнопку Apply в редакторе ресурсов:

Wiki resourses reload.png

Теперь перетащим солнышко на сцену этого урока (лучше для него создать отдельный экран). Уменьшим его размеры модификаторами scale x и scale y, поставив там значение 0.5. Превратим солнышко в такую машину:


Lesson 5 pivot2.png


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

В качестве упражнения попробуйте организовать вращение, подобное вращению Земли и Луны (Земля вращается по окружности, а Луна вращается по окружности меньшего радиуса вокруг Земли). Для этого Вам потребуется "Луну" сделать подобъектом "Земли" и правильно выставить положение "Луны" относительно "Земли".

Ok.png

Внимание! Если объект помещен в группу, то координаты его пивота изменяются относительно точки пивота того объекта, чьим подобъектом он является.

Поэкспериментируйте. Можно использовать следующие картинки:

Eath.png Moon.png

Как ездят кареты

Подготовка

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

Lesson 5 kareta1.png


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

Kareta.png Koleso.png

Немного математики

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

Посмотрите на формулы возле рисунка кареты. В первой из них приводится связь длины окружности L колеса и его радиуса r. Ниже - коэффициент 57.3 перевода числа пи из углов в радианы (пи - это 3.1415.. в радианах или 180 градусов). Во второй колонке даны определения поступательной и угловой скорости (изменение расстояния или угла за некоторое время t). Наконец, в третей колонке дана ключевая для нашей задачи формула, связывающая радиус r, линейную v и угловую скорости w. Попробуйте её самостоятельно вывести, из предположения, что за одно и тоже время длина смещения кареты должна совпадать с длиной окружности обода колеса. При этом поступательная скорость v измеряется в пикселях за секунду, а угловая скорость w в градусах за секунду.

Машины

Запрограммируем теперь карету и колеса так, что бы после клика начинала двигаться карета и вращаться колеса. Когда карета останавливается, то и колеса должны переставать крутится. Колеса сделаем одинаковыми простыми машинами с двумя состояниями, которые назовем off (неподвижны) и on (крутятся):


Lesson 5 kareta2.png


Заметим, что в состояниях колес нет команды draw, так как колесо всегда рисуется при помощи одной картинки, она берется по умолчанию, из той, которая была перетащена на сцену (свойство res). Угловая скорость v (в формулах она была обозначена как w) в команде rot измеряется в градусах в секунду. Поэтому v=180 означает, что колесо будет делать полный оборот (360 градусов) за 2 секунды.

Обратим внимание, что раньше вращение мы задавали двумя параметрами: изменением угла da за время t. Так как сейчас время вращения колеса не фиксируется (этим занимается карета), то указан только один параметр: угловая скорость v.

Так как колеса одинаковые, то имеет смысл создать сначала одно колесо, запрограммировать его, а затем скопировать, получив готовое второе колесо. Его надо будет только затем мышкой перетащить на нужное место. Не забываем также переименовать машины (в нашем случае это koleso1 и koleso2).

Карета должна при клике запускать на вращение колеса (переводя в состояние on), а при остановке их останавливать:


Lesson 5 kareta3.png


Проверьте что параметр v=66 в команде move состояния run кареты, угловая скорость колес v=180 в команде rot состояния on согласуются с радиусом колеса r=21. Радиус колеса равен половине ширины его картинки (свойство "размер"). Если Ваше колесо имеет другой размер, пересчитайте поступательную скорость по приведенной выше формуле.

Как открыть замок ключом

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

Постановка задачи

Рассмотрим простой пример подобного взаимодействия объектов. Создайте новый экран и сцену на нём, так как предыдущий экран должен быть уже забит различными насекомыми. Перетащите на сцену из редактора ресурсов очередного жука, саквояж и ключ. Лучше их перетаскивать именно в таком порядке. Если порядок получился другой, мышкой в дереве проекта упорядочите эти объекты, так чтобы последовательность рисования была: жук, затем саквояж, затем ключ. Расположите эти объекты примерно в центре сцены и сделайте их машинами состояний. Поменяйте их имена на ladybug (божья коровка), bag (саквояж) и key (ключ).


Lesson 5 key1.png


Наша задача состоит в организации взаимодействия ключа, саквояжа и жука при котором, работают такие правила:

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

Ключ

Запрограммируем сначала машину для ключа:

Lesson 5 key3.png


Три состояния соответствуют трём режимам функционирования ключа:

  • drag - в этом, стартовом состоянии мы таскаем ключ по экрану
  • open - открываем саквояж и уменьшаемся до 0
  • back - восстанавливаем размер и возвращаемся в исходное положение на экране

Второе состояние состояние open не должно вызвать особых затруднений. В команде set меняется состояние объекта bag (саквояж) на open. В этот момент саквояж должен изобразить своё открытие. В команде процессов scale происходит за время t=500 миллисекунд уменьшение масштаба рисования ключа (его размера) до целевых значений tx, ty которые выбраны близкими к нулю. После этого ключ переходит в третье состояние back.

В состоянии back он снова меняет свой масштаб, но уже к исходному значению 1 по ширине и высоте. Кроме этого появилась новая команда init. Это команда инициализации или переустановки свойств данной машины. В нашем случае, мы меняем её координаты на значения x=0 и y=250 пикселей. Как и команда set, команда init выполняется сразу при входе в состояние, поэтому ключ начнет увеличиваться уже с исходной позиции. Желательно, на сцене ему сразу задать в свойствах положение x=0, y=250, чтобы туда в дальнейшем он и возвращался.

Разберемся теперь с системой координат на сцене.

Координаты на сцене

Если сцена создавалась по умолчанию, то она имеет ширину 960 пикселей, а высоту - 640. Начало отсчёта координат на сцене расположено в её центре. Ось x, по которой откладывается горизонтальная координата объекта направлена вправо, а ось Y для вертикальной координаты - вниз:

Lesson 5 key2.png

Стоит раскрыть координаты любого объекта (свойство положение) на панели Properties. Затем потаскать объект по сцене, наблюдая как эти координаты меняются. Если x стало отрицательным, значит объект находится левее центра сцены. Если же отрицательный y, то объект расположен выше центра.

Заметим, что координата объекта определяется его точкой пивота. Для трех объектов, находящихся сейчас на сцене точка пивота должна совпадать с центрами картинок.

Таким образом, когда мы задали в команде init положение для ключа x=0, y=250, мы поместили его по середине, в нижней части сцены.

Драг энд дроп

Lesson 5 key3 drag.png

Осталось разобраться с ещё двумя новыми командами, которые появились в состоянии drag. Первая команда drag (таскать) означает, что данную машину в этом состоянии разрешено мышкой таскать по экрану. При этом могут быть заданы координаты области за которую объект вытащить нельзя. Эта область определяется двумя точками - верхним левым углом с координатами (x1,y1) и правым нижним углом с координатами (x2,y2).

В нашем случае эти точки почти совпадают с углами сцены. На самом деле область разрешенного таскания чуть меньше размера сцены. Сделано это для того, чтобы ключ нельзя было даже краешком вытащить за сцену. Правый нижний угол сцены имеет координаты (x2, y2)=(480, 320)=(960/2, 640/2). Ширина ключа 128 пикселей (см. свойство размер). Поэтому, чтобы правая сторона ключа не выехала за сцену, для координаты y1 мы берем значение 416 = 480-128/2 (сужаем границу таскания на половину ширины ключа). Аналогично считаются остальные координаты области разрешенного таскания.

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

Саквояж и жук

Создадим теперь машину для саквояжа. Она будет иметь два состояния open и close:


Lesson 5 key4.png


В состоянии close в команде init саквояж переустанавливает свою прозрачность в единичное значение (непрозрачен) al=1.00. Это необходимо, что бы появиться на экране после исчезновения. Сам процесс исчезновения задается командой процесса alpha в состоянии open. Эта команда меняет текущую прозрачность объекта до целевого значения ta=0 (полностью прозрачен) за время t=500 миллисекунд (пол секунды). Кроме этого в этом состоянии меняется состояние жука (команда set), который должен из под саквояжа начать бежать.

Осталось создать два состояния для жука, который будет выбегать из под саквояжа. Ничего сложного в его поведении нет:


Lesson 5 key5.png

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

Что дальше?

Мы разобрали много команд и их параметров. Однако это далеко не все возможности машин состояний. Стоит прочитать документ Machine в котором описан весь язык машин. Чем больше Вы создадите машин с различным поведением, тем увереннее будете с ними работать.

В заключение приведем несколько общих советов:

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

Упражнения

  • создайте движение Земли по окружности, а Луны также по окружности, но вокруг Земли;
  • сделайте движение кареты бесконечным так, что после того как она уезжает за правый край экрана, она появлялась из-за левого края;
  • заставьте жука начинать бежать, только когда саквояж полностью уменьшится (set на выходе из состояния).
  • организуйте так, чтобы при перетаскивании ключа в область саквояжа, жук развернулся на 180 градусов и двигался вниз, обратно под саквояж.

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