My:Abs — различия между версиями
WikiSysop (обсуждение | вклад) (→Введение) |
WikiSysop (обсуждение | вклад) |
||
Строка 12: | Строка 12: | ||
Внутри каждого состояния (далее тег '''st''') могут находиться 4 вида команд - тегов: | Внутри каждого состояния (далее тег '''st''') могут находиться 4 вида команд - тегов: | ||
− | * ''' | + | * '''Инициализации''': |
− | ** '''init''' - инициализация состояния | + | ** '''init''' - инициализация состояния (координаты и т.п.) |
+ | ** '''draw''' - установка графического ресурса | ||
** '''set''' - установка состояния другого объекта | ** '''set''' - установка состояния другого объекта | ||
− | * ''' | + | * '''Процессов''': |
** '''time''' - задержка по времени | ** '''time''' - задержка по времени | ||
** '''move''' - движение | ** '''move''' - движение | ||
Строка 26: | Строка 27: | ||
* '''Воздействия''': | * '''Воздействия''': | ||
− | ** ''' | + | ** '''click''' - что делать при клике на объект |
* '''Проверки''': | * '''Проверки''': | ||
** '''if''' - логическое условие перехода | ** '''if''' - логическое условие перехода | ||
− | Рассмотрим пример. Пусть объект находится в состоянии | + | Рассмотрим пример простой машины состояний. Пусть объект находится в состоянии st="stop". |
При клике на него мышкой, он переходит в состояние st="run" | При клике на него мышкой, он переходит в состояние st="run" | ||
в котором находится 3000 ms, через которые возвращается в состояние "stop". | в котором находится 3000 ms, через которые возвращается в состояние "stop". | ||
+ | В каждом состоянии рисуется своя картинка. | ||
В xml-файле сцены он описывается следующим образом: | В xml-файле сцены он описывается следующим образом: | ||
<pre class="brush:xml; gutter: false;"> | <pre class="brush:xml; gutter: false;"> | ||
− | <obj id="1" kn="sts" w="64" h="64" x="734" y="534" state="stop"> | + | <obj id="1" kn="sts" nm="мигалка" w="64" h="64" x="734" y="534" state="stop"> |
<st id="stop"> | <st id="stop"> | ||
Строка 45: | Строка 47: | ||
<st id="run"> | <st id="run"> | ||
<init res="102" /> <!-- рисовать граф.ресурс номер 102 --> | <init res="102" /> <!-- рисовать граф.ресурс номер 102 --> | ||
− | <time tm="3000" go="stop" /> <!-- через время tm | + | <time tm="3000" go="stop" /> <!-- через время tm возвращаемся в stop --> |
</st> | </st> | ||
Строка 51: | Строка 53: | ||
</pre> | </pre> | ||
Поле state="stop" при описании свойств объекта (тег obj) задает начальное состояние объекта. | Поле state="stop" при описании свойств объекта (тег obj) задает начальное состояние объекта. | ||
+ | Если его нет - берем первое состояние как начальное. | ||
Наличие поля go="состояние" в любой команде инициирует остановку всех процессов | Наличие поля go="состояние" в любой команде инициирует остановку всех процессов | ||
− | и изменяет состояние. | + | и изменяет состояние на указанное. |
− | == | + | == Воздействия и условия == |
− | Если в команде click указано значение go, то происходит переход в новое состояние. Так, безусловный переход выглядит следующим образом: | + | Если в команде click указано значение go, то происходит переход в новое состояние. |
+ | Так, безусловный переход в состояние "run" выглядит следующим образом: | ||
<pre class="brush:xml; gutter: false;"> | <pre class="brush:xml; gutter: false;"> | ||
<click go="run" /> | <click go="run" /> | ||
</pre> | </pre> | ||
− | Поля go может и не быть. Тогда все процессы внутри состояния (например, перемещения) останавливаются до следующего клика, которым они запускаются снова: | + | Поля go может и не быть. Тогда, при клике на объект, все процессы внутри состояния (например, перемещения) останавливаются до следующего клика, которым они запускаются снова: |
<pre class="brush:xml; gutter: false;"> | <pre class="brush:xml; gutter: false;"> | ||
<click/> | <click/> | ||
Строка 71: | Строка 75: | ||
<if obj="2" st="show"/> | <if obj="2" st="show"/> | ||
</pre> | </pre> | ||
− | Условие может содержать логические теги: | + | Условие может содержать логические теги. Например, если 2-й объект в show '''и''' (3-й в run '''или''' 4-й в slip): |
<pre class="brush:xml; gutter: false;"> | <pre class="brush:xml; gutter: false;"> | ||
<if> | <if> | ||
Строка 81: | Строка 85: | ||
</if> | </if> | ||
</pre> | </pre> | ||
− | |||
В условиях может проводится и проверка значений параметров данного объекта. Например: | В условиях может проводится и проверка значений параметров данного объекта. Например: | ||
<pre class="brush:xml; gutter: false;"> | <pre class="brush:xml; gutter: false;"> | ||
− | <if al="1.0" /> | + | <if al="1.0" /> <!-- значение прозрачности --> |
− | <if move="5" /> | + | <if move="5" /> <!-- выполнилась 5-я команда move --> |
+ | <if x=">50" / > <!-- координата > 50 (первый символ строки знак меньше) --> | ||
</pre> | </pre> | ||
В дальнейшем предполагается вставка простых скриптов, типа: | В дальнейшем предполагается вставка простых скриптов, типа: | ||
Строка 95: | Строка 99: | ||
== Повтор команд == | == Повтор команд == | ||
− | Все | + | Все команды могут встречаться при описании данного состояния по несколько раз. |
Интерпретация таких повторов зависит от типа команды: | Интерпретация таких повторов зависит от типа команды: | ||
− | * В последовательности одинаковых команд инициализации (init,init,...) выполняется только первая. Выполнение следующих команд инициируется другими командами. | + | * В последовательности одинаковых команд инициализации (init,init,...) выполняется только первая. Выполнение следующих команд инициируется другими командами (обычно командами процессов). |
− | * Команды процессов выполняются параллельно и независимо. Например, последовательность move, move, move, alpha, alpha выглядит в виде параллельных цепочек, выполняющихся одновременно сверху вниз. | + | * Команды процессов выполняются параллельно и независимо. Например, последовательность move, move, move, rot, alpha, alpha выглядит в виде параллельных цепочек, выполняющихся одновременно сверху вниз. В начале функционирования состояния запускается первый move, rot и первая alpha. Когда одна из них выполнится происходит переход на следующую команду '''с таким же''' именем: |
− | + | move rot alpha | |
− | + | move alpha | |
− | + | move | |
− | * Условная проверка ( | + | * Условная проверка (if) производится на выходе из состояния. Выход происходит, если условие выполнилось. |
− | * Каждая команда может управлять номером выполнения команды из списка команд с данным именем | + | * Каждая команда может управлять номером выполнения команды из списка команд с данным именем. Для этого в ней указывается имя и номер команды, выполнение которой надо запустить. |
Рассмотрим перемещение персонажа по экрану. | Рассмотрим перемещение персонажа по экрану. | ||
Пусть различные этапы движения персонажа собраны в графическом ресурсе в прямоугольную | Пусть различные этапы движения персонажа собраны в графическом ресурсе в прямоугольную | ||
− | таблицу (по строкам | + | таблицу (по строкам расположены кадры движения вперед, поворот и т.п.). |
Тогда можно указать начальный '''с1''', конечный '''c2''' фреймы в строке таблицы '''r''' (по умолчанию r=0). Движение вправо и вверх можно реализовать двумя способами. | Тогда можно указать начальный '''с1''', конечный '''c2''' фреймы в строке таблицы '''r''' (по умолчанию r=0). Движение вправо и вверх можно реализовать двумя способами. | ||
Первый - при помощи последовательности двух состояний (left и up): | Первый - при помощи последовательности двух состояний (left и up): | ||
Строка 113: | Строка 117: | ||
<st id="left"> | <st id="left"> | ||
<init res="101" c1="0" c2="15" r="0"/> <!-- 16 кадров ходьбы --> | <init res="101" c1="0" c2="15" r="0"/> <!-- 16 кадров ходьбы --> | ||
− | <move dx="100" tm="500" go="up"/> <!-- движемся на dx за dt и | + | <move dx="100" tm="500" go="up"/> <!-- движемся на dx за dt и переход в go --> |
</st> | </st> | ||
<st id="up"> | <st id="up"> | ||
− | <init res="101" c1="0" c2=" | + | <init res="101" c1="0" c2="7" r="1"/> <!-- 8 кадров подъема вверх --> |
<move dy="100" tm="500"/> <!-- движемся на dy за dt и стоп --> | <move dy="100" tm="500"/> <!-- движемся на dy за dt и стоп --> | ||
</st> | </st> | ||
Строка 125: | Строка 129: | ||
<init res="101" c1="0" c2="15" r="0" /> | <init res="101" c1="0" c2="15" r="0" /> | ||
<move dx="100" tm="500" init="1"/> <!-- сдвигаемся и вызываем 2-й init --> | <move dx="100" tm="500" init="1"/> <!-- сдвигаемся и вызываем 2-й init --> | ||
− | <init res="101" c1="0" c2=" | + | <init res="101" c1="0" c2="7" r="1" /> |
<move dy="100" tm="500"/> <!-- сдвигаемся и останавливаемся --> | <move dy="100" tm="500"/> <!-- сдвигаемся и останавливаемся --> | ||
</st> | </st> | ||
</pre> | </pre> | ||
− | При начале функционирования этого состояния выполняется инициализация с заданием графики. | + | При начале функционирования этого состояния выполняется инициализация с заданием графики (первый init). |
Затем происходит горизонтальное перемещение. Когда оно заканчивается выполняется следующая команда | Затем происходит горизонтальное перемещение. Когда оно заканчивается выполняется следующая команда | ||
− | init | + | init, см. свойство init="1" в первом move (все команды нумеруются с 0). После этого сразу запускается следующий move. |
== Инициализация == | == Инициализация == | ||
Строка 140: | Строка 144: | ||
<init x="100" y="0" al="0" /> | <init x="100" y="0" al="0" /> | ||
</pre> | </pre> | ||
− | Кроме этого, машина состояний может изменять состояния других объектов (не только машин) | + | Кроме этого, машина состояний может изменять состояния других объектов (не только машин), которые имеют состояния: |
<pre class="brush:xml; gutter: false;"> | <pre class="brush:xml; gutter: false;"> | ||
<set obj="2" st="show" /> <!-- перевод объекта 2 в состояние show --> | <set obj="2" st="show" /> <!-- перевод объекта 2 в состояние show --> | ||
</pre> | </pre> | ||
− | Команды init и set могут повторяться, но при первом попадании выполняется всегда только первый init и первый set. Если необходимо выполнить несколько set-ов, делается это так: | + | Команды init и set могут повторяться, но при первом попадании выполняется всегда только первый init и первый set (точнее нулевые, так как нумерация команд идет с 0). |
+ | Если необходимо выполнить сразу несколько set-ов, делается это так: | ||
<pre class="brush:xml; gutter: false;"> | <pre class="brush:xml; gutter: false;"> | ||
<set obj="2" st="show" set="1"/> <!-- второй объект в состояние show и сразу--> | <set obj="2" st="show" set="1"/> <!-- второй объект в состояние show и сразу--> | ||
Строка 173: | Строка 178: | ||
<time go="st2"/> <!-- иначе перейдем в st2 --> | <time go="st2"/> <!-- иначе перейдем в st2 --> | ||
</pre> | </pre> | ||
+ | И так, параметры таймера могут быть следующими: | ||
+ | * '''tm''' - длительность выполнения команды в ms | ||
+ | * '''dt''' - интервал tm+/- dt внутри которого, команда может быть случайно прервана (при t>tm+dt прерывается по любому) | ||
+ | * '''p''' - вероятность срабатывания перехода в состояние, указываемое параметром go. | ||
+ | * '''go''' - состояние в которое нужно перейти поле окончания времени и срабатывания условия if (если оно не сработала команда ждет его срабатывания, даже после истечения времени). | ||
+ | * '''if''' - номер команды if. | ||
== Движение объекта == | == Движение объекта == |
Версия 11:56, 14 октября 2011
Машина состояний (state machine) является универсальным объектом для программирования в редакторе сцен сложного поведения объектов. Для машин с типовым набором состояний и поведений, в дальнейшем будут вводиться отдельные объекты.
Логика поведения машины состояний разбивается на отдельные узлы (состояния). Объект всегда находится строго в одном состоянии. Переход из одного состояния в другое происходит либо в результате внешнего воздействия на объект, либо в результате окончания некоторых процессов протекающих внутри объекта.
Параметры состояний задаются в редакторе сцены. Ниже, вместо скриншотов панели свойств, приводятся примеры на языке xml (так, как это выглядит во внутреннем файле сцены).
Содержание
Введение
Внутри каждого состояния (далее тег st) могут находиться 4 вида команд - тегов:
- Инициализации:
- init - инициализация состояния (координаты и т.п.)
- draw - установка графического ресурса
- set - установка состояния другого объекта
- Процессов:
- time - задержка по времени
- move - движение
- alpha - изменение прозрачности
- scale - изменение размера
- rot - вращение
- spin - движение по окружности
- phys - движение в силовом поле.
- Воздействия:
- click - что делать при клике на объект
- Проверки:
- if - логическое условие перехода
Рассмотрим пример простой машины состояний. Пусть объект находится в состоянии st="stop". При клике на него мышкой, он переходит в состояние st="run" в котором находится 3000 ms, через которые возвращается в состояние "stop". В каждом состоянии рисуется своя картинка. В xml-файле сцены он описывается следующим образом:
<obj id="1" kn="sts" nm="мигалка" w="64" h="64" x="734" y="534" state="stop"> <st id="stop"> <init res="101" /> <!-- рисовать граф.ресурс 101 --> <click go="run" /> <!-- при клике - идем в состояние run--> </st> <st id="run"> <init res="102" /> <!-- рисовать граф.ресурс номер 102 --> <time tm="3000" go="stop" /> <!-- через время tm возвращаемся в stop --> </st> </obj>
Поле state="stop" при описании свойств объекта (тег obj) задает начальное состояние объекта. Если его нет - берем первое состояние как начальное. Наличие поля go="состояние" в любой команде инициирует остановку всех процессов и изменяет состояние на указанное.
Воздействия и условия
Если в команде click указано значение go, то происходит переход в новое состояние. Так, безусловный переход в состояние "run" выглядит следующим образом:
<click go="run" />
Поля go может и не быть. Тогда, при клике на объект, все процессы внутри состояния (например, перемещения) останавливаются до следующего клика, которым они запускаются снова:
<click/>
Выход из состояния по go в любой команде (не только в click) может делаться по условию. Различные условия описываются в секциях if. Их может быть несколько для различных переходов. Например, пусть смена состояния происходит, только, если объект номер 2 находится в состоянии "show":
<click go="run" if="0"/> <!-- проверяем первое (нулевое) условие --> <if obj="2" st="show"/>
Условие может содержать логические теги. Например, если 2-й объект в show и (3-й в run или 4-й в slip):
<if> <and obj="2" st="show"> <and> <or obj="3" st="run"> <or obj="4" st="slip"> </and> </if>
В условиях может проводится и проверка значений параметров данного объекта. Например:
<if al="1.0" /> <!-- значение прозрачности --> <if move="5" /> <!-- выполнилась 5-я команда move --> <if x=">50" / > <!-- координата > 50 (первый символ строки знак меньше) -->
В дальнейшем предполагается вставка простых скриптов, типа:
<click go="run" if="x > 10 and y < 100"/>
Повтор команд
Все команды могут встречаться при описании данного состояния по несколько раз. Интерпретация таких повторов зависит от типа команды:
- В последовательности одинаковых команд инициализации (init,init,...) выполняется только первая. Выполнение следующих команд инициируется другими командами (обычно командами процессов).
- Команды процессов выполняются параллельно и независимо. Например, последовательность move, move, move, rot, alpha, alpha выглядит в виде параллельных цепочек, выполняющихся одновременно сверху вниз. В начале функционирования состояния запускается первый move, rot и первая alpha. Когда одна из них выполнится происходит переход на следующую команду с таким же именем:
move rot alpha move alpha move
- Условная проверка (if) производится на выходе из состояния. Выход происходит, если условие выполнилось.
- Каждая команда может управлять номером выполнения команды из списка команд с данным именем. Для этого в ней указывается имя и номер команды, выполнение которой надо запустить.
Рассмотрим перемещение персонажа по экрану. Пусть различные этапы движения персонажа собраны в графическом ресурсе в прямоугольную таблицу (по строкам расположены кадры движения вперед, поворот и т.п.). Тогда можно указать начальный с1, конечный c2 фреймы в строке таблицы r (по умолчанию r=0). Движение вправо и вверх можно реализовать двумя способами. Первый - при помощи последовательности двух состояний (left и up):
<st id="left"> <init res="101" c1="0" c2="15" r="0"/> <!-- 16 кадров ходьбы --> <move dx="100" tm="500" go="up"/> <!-- движемся на dx за dt и переход в go --> </st> <st id="up"> <init res="101" c1="0" c2="7" r="1"/> <!-- 8 кадров подъема вверх --> <move dy="100" tm="500"/> <!-- движемся на dy за dt и стоп --> </st>
Этот же процесс движения можно реализовать как одно состояние:
<st id="move"> <init res="101" c1="0" c2="15" r="0" /> <move dx="100" tm="500" init="1"/> <!-- сдвигаемся и вызываем 2-й init --> <init res="101" c1="0" c2="7" r="1" /> <move dy="100" tm="500"/> <!-- сдвигаемся и останавливаемся --> </st>
При начале функционирования этого состояния выполняется инициализация с заданием графики (первый init). Затем происходит горизонтальное перемещение. Когда оно заканчивается выполняется следующая команда init, см. свойство init="1" в первом move (все команды нумеруются с 0). После этого сразу запускается следующий move.
Инициализация
При первом попадании в состояние можно установить те или иные параметры объекта. Например, его начальное положение в этом состоянии и alpha-прозрачность (если они отличны от общих параметров в разделе obj):
<init x="100" y="0" al="0" />
Кроме этого, машина состояний может изменять состояния других объектов (не только машин), которые имеют состояния:
<set obj="2" st="show" /> <!-- перевод объекта 2 в состояние show -->
Команды init и set могут повторяться, но при первом попадании выполняется всегда только первый init и первый set (точнее нулевые, так как нумерация команд идет с 0). Если необходимо выполнить сразу несколько set-ов, делается это так:
<set obj="2" st="show" set="1"/> <!-- второй объект в состояние show и сразу--> <set obj="3" st="play" set="2"/> <!-- третий объект в состояние play и сразу --> <set obj="4" st="run"/> <!-- четвертый объект в run -->
Например, пусть необходимо при заходе в состояние установить 2-й объект в состояние show, затем переместиться на dx и после этого перемещения задать состояние 3-го объекта в play. Делаем это так:
<set obj="2" st="show"/> <!-- второй объект в состояние show, затем --> <move dx="100 set="1"/> <!-- перемещаемся и затем следующий set --> <set obj="3" st="play"/>
Если в начале не нужна установка состояния, вставляем пустую команду set:
<set/> <move dx="100 set="1"/> <!-- перемещаемся и затем выполняем set --> <set obj="3" st="play"/>
Таймер
Время пребывания в данном состоянии задается командой time с параметром tm в ms:
<time tm="1000 go="next"/> <!-- ждем 1000ms и покидаем состояние -->
Для программирования объектов со случайным поведением можно использовать последовательность команд time с параметром вероятности перехода p:
<time go="st1" tm="100" p="0.5"/> <!-- через tm с вероятностью 1/2 go to st1 --> <time go="st2"/> <!-- иначе перейдем в st2 -->
И так, параметры таймера могут быть следующими:
- tm - длительность выполнения команды в ms
- dt - интервал tm+/- dt внутри которого, команда может быть случайно прервана (при t>tm+dt прерывается по любому)
- p - вероятность срабатывания перехода в состояние, указываемое параметром go.
- go - состояние в которое нужно перейти поле окончания времени и срабатывания условия if (если оно не сработала команда ждет его срабатывания, даже после истечения времени).
- if - номер команды if.
Движение объекта
Возможно управление alpha-каналом, перемещением по сцене, вращением и т.п. Всё это команды процессов, начинающихся выполняться сразу и параллельно. Например, пусть объект должен переместиться в целевую точку с координатами tx,ty со скоростью 0.1 пиксель в ms, увеличивая свою непрозрачность с начальной (из раздела init) до ta=1 со скоростью da в ms. При этом он вращается и уменьшается:
<move tx="100" ty="200" v="0.1" /> <alpha ta="1.0" da="0.001" /> <rot ta="0.1" da="0.001" /> <scale ts="0.1" ds="0.001" />
Любых команд движения может быть несколько. Например, пусть в данном состоянии объект должен двигаться по квадрату, пока мы на него не кликнем:
<init x="0" y="0" /> <!-- левый верхний угол --> <move tx="10" ty="0" v="0.1" /> <!-- вправо --> <move tx="10" ty="10" v="0.1" /> <!-- вниз --> <move tx="0" ty="10" v="0.1" /> <!-- влево --> <move tx="0" ty="0" v="0.1" move="0"/> <!-- вверх и повтор --> <click go="next"/>
Флаг move="0" в последней команде move означает, повторный запуск проигрывания команд move.
Рассмотрим подробнее команды процессов движения.
move
Перемещение по сцене:
- tx, ty - целевые координаты в px к которым должен переместиться объект
- v - скороcть перемещения в px/ms. Для фреймовой анимации, например, ходьбы скорость рассчитывается исходя из длительности одного кадра и числа кадров на фазу движения. Поэтому задается не время движения, а именно скорость.
rot
Вращение
spin
Движение по окружности
alpha
Изменение прозрачности
scale
Изменение размеров объекта
Симулирование физики
Смоделируем падение объекта до линии ty="0", с последующим его подпрыгиванием три раза с затухающей амплитудой (координата y направлена вниз):
<init x="0" y="0" /> <phys vx="0" vy="0" ay="9.8" ty="100"/> <!-- падаем --> <phys vx="0.5" vy="-0.5" ay="9.8" ty="100"/> <!-- первый отскок --> <phys vx="0.3" vy="-0.3" ay="9.8" ty="100"/> <!-- второй отскок --> <phys vx="0.1" vy="-0.1" ay="9.8" ty="100"/> <!-- третий отскок -->
Рассмотрим теперь подпрыгивающий мячик, который при касании с землей деформируется (вертикально сплюскивается). Это можно сделать в 3-е состояния (падаем, касаемся и взлетаем)
<st id="down"> <!-- падаем --> <phys vx="0" vy="0" ay="9.8" ty="100" go="touch"/> </st> <st id="touch"> <scale ty="0.5" tm="100"/> <!-- сжимаемся --> <scale ty="1.0" tm="100"/> <!-- разжимаемся --> </st> <st id="up"> <!-- взлетаем --> <phys vx="0.1" ay="9.8" ty="0" go="down"/> </st>
Естественно, необходимо рассчитать начальную скорость взлета, равную v=sqrt(2*ay*h), где h-высота падения.
Примеры
Часы с маятником
Есть 2 машины: стрелка и маятник в виде шарика, качающегося под стрелкой. При клике на шарик, часы останавливаются. При еще одном клике - опять начинают идти. Просто тикающую стрелку можно сделать из одного состояния "run":
<obj id="1" nm="стрелка" res="1" x="0" y="0" w="10" h="100" px="5" py="100" state="run"> <st id="run"> <rot da="30" tm="100"/> <!-- поворот на 30 град. за 100 ms --> <rot tm="900" rot="0"/> <!-- задержка 900 ms и зацикливание rot-эйтов--> </st> </obj>
Однако, мы хотим останавливать часы только в состоянии окончания движения стрелки (после поворота от цифры к цифре). Кроме этого, нужна синхронность стрелки и маятника (т.е. стрелка поворачивается, когда маятник достигает максимального отклонения вправо). Поэтому:
<obj id="1" nm="стрелка" res="1" x="0" y="0" w="10" h="100" px="5" py="100" > <st id="stop" > <!-- просто рисуем res c текущими параметрами --> </st> <st id="run"> <!-- поворачиваем на 30 град. вокруг (px,py) --> <rot da="30" tm="100" go="stop"/> </st> </obj>
Картинку шарика маятника будем не вращать относительно вынесенной за него точки пивота, а двигать по окружности, т.к. он должен принять на себя клик в своем текущем положении. Простая реализация может выглядеть так:
<obj id="2" nm="шарик" res="1" x="" y="" cx="5" cy="100" state="run"> <st id="run"> <set obj="1" st="run"/> <!-- запускание поворота стрелки --> <spin ta="60" va="0.24"/> <!-- вправо до 60 град. со скоростью va --> <spin ta="-60" va="-0.24"/><!-- отклоняемся влево --> <spin ta="60" va="0.24"/> <!-- снова вправо --> <spin set="0" spin="0"/> <!-- поворот стрелки, зацикливаем качание --> <click/> <!-- при клике - пауза до следующего клика --> </st> </obj>
Скорость поворота по окружности (град/ms) вычисляется исходя из амплитуды в 60 град. Четыре таких амплитуд (качание влево-вправо) должны произойти за 1000 ms. Поэтому va=60*4/1000 = 0.24.
Добавим маятнику физичности. Мы ходим, чтобы после остановки и последующего клика он падал "вниз", а не начинал всё время поворачиваться вправо.
<obj id="2" nm="шарик" res="1" x="" y="" cx="5" cy="100" state="right"> <st id="right"> <!-- движемся cправа --> <set /> <!-- первый раз стрелку не трогаем --> <set obj="1" st="run"/> <!-- переключаем из spin --> <spin ta="60" va="0.24" set="1"/> <!-- отклонившись, включаем стрелку--> <spin ta="0" va="-0.24" go="left" /> <click spin="1"/> </st> <st id="left"> <!-- движемся влево --> <spin ta="-60" va="-0.24" /> <!-- до 60 град. со скоростью va --> <spin ta="0" va="0.24" go="right" /> <click spin="1"/> </st> </obj>