My:Abs — различия между версиями

Материал из synset
Перейти к: навигация, поиск
(Введение)
Строка 12: Строка 12:
  
 
Внутри каждого состояния (далее тег '''st''') могут находиться 4 вида команд - тегов:
 
Внутри каждого состояния (далее тег '''st''') могут находиться 4 вида команд - тегов:
* '''Инициализация''':  
+
* '''Инициализации''':  
** '''init''' - инициализация состояния
+
** '''init''' - инициализация состояния (координаты и т.п.)
 +
** '''draw''' - установка графического ресурса
 
** '''set''' - установка состояния другого объекта
 
** '''set''' - установка состояния другого объекта
  
* '''Процессы''':
+
* '''Процессов''':
 
** '''time''' - задержка по времени
 
** '''time''' - задержка по времени
 
** '''move''' - движение
 
** '''move''' - движение
Строка 26: Строка 27:
  
 
* '''Воздействия''':
 
* '''Воздействия''':
** '''clik''' - что делать при клике на объект
+
** '''click''' - что делать при клике на объект
  
 
* '''Проверки''':
 
* '''Проверки''':
 
** '''if''' - логическое условие перехода  
 
** '''if''' - логическое условие перехода  
  
Рассмотрим пример. Пусть объект находится в состоянии state="stop".
+
Рассмотрим пример простой машины состояний. Пусть объект находится в состоянии 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 переходим в stop -->
+
       <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>
Т.е.: 2-й в show '''и''' (3-й в run '''или''' 4-й в slip).
 
  
 
В условиях может проводится и проверка значений параметров данного объекта. Например:
 
В условиях может проводится и проверка значений параметров данного объекта. Например:
 
<pre class="brush:xml; gutter: false;">
 
<pre class="brush:xml; gutter: false;">
<if al="1.0" />   <!-- значение прозрачности -->
+
<if al="1.0" /> <!-- значение прозрачности -->
<if move="5" />   <!-- выполнилась 5-я команда move -->
+
<if move="5" /> <!-- выполнилась 5-я команда move -->
 +
<if x=">50" / >  <!-- координата > 50 (первый символ строки знак меньше) -->
 
</pre>
 
</pre>
 
В дальнейшем предполагается вставка простых скриптов, типа:
 
В дальнейшем предполагается вставка простых скриптов, типа:
Строка 95: Строка 99:
 
== Повтор команд  ==
 
== Повтор команд  ==
  
Все теги могут встречаться при описании данного состояния по несколько раз.
+
Все команды могут встречаться при описании данного состояния по несколько раз.
 
Интерпретация таких повторов зависит от типа команды:
 
Интерпретация таких повторов зависит от типа команды:
* В последовательности одинаковых команд инициализации (init,init,...) выполняется только первая. Выполнение следующих команд инициируется другими командами.
+
* В последовательности одинаковых команд инициализации (init,init,...) выполняется только первая. Выполнение следующих команд инициируется другими командами (обычно командами процессов).
* Команды процессов выполняются параллельно и независимо. Например, последовательность move, move, move, alpha, alpha выглядит в виде параллельных цепочек, выполняющихся одновременно сверху вниз. При начале функционирования состояния запускается первый move и первая alpha. Когда одна из них выполнится происходит переход на следующую команду '''с таким же''' именем:
+
* Команды процессов выполняются параллельно и независимо. Например, последовательность move, move, move, rot, alpha, alpha выглядит в виде параллельных цепочек, выполняющихся одновременно сверху вниз. В начале функционирования состояния запускается первый move, rot и первая alpha. Когда одна из них выполнится происходит переход на следующую команду '''с таким же''' именем:
    move   alpha
+
          move rot    alpha
    move   alpha
+
          move         alpha
    move
+
          move
* Условная проверка (wait) производится на выходе из состояния. Выход происходит, если условие выполнилось.
+
* Условная проверка (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 и идем в go -->  
+
   <move dx="100" tm="500" go="up"/>      <!-- движемся на dx за dt и переход в go -->  
 
</st>
 
</st>
 
<st id="up">
 
<st id="up">
   <init res="101" c1="0" c2="15" r="1"/> <!-- 16 кадров подъема вверх -->
+
   <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="15" r="0" />   
+
   <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="1" в первом move; помним, что все команды нумеруются с 0). После этого сразу запускается следующий move.
+
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>