

= jMonkeyEngine 3 Урок (7) - Знакомство с Анимацией

Предыдущий: <<документация/jme3_ru/начальная/знакомство_с_материалом#,Знакомство с Материалом>>,
Следующий: <<документация/jme3_ru/начальная/знакомство_с_взаимодействиями#,Знакомство с Взаимодействиями>>,
Английская версия: <<jme3/beginner/hello_animation#,Hello Animation>>


Этот урок показывает, как добавить контроллер и каналы анимации, и как реагировать на действия пользователя, вызывая анимацию у загруженной модели.



image::jme3/beginner/beginner-animation.png[beginner-animation.png,with="",height="",align="center"]



““““



== Пример кода

[source,java]

----

package jme3test.helloworld;

import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.AnimEventListener;
import com.jme3.animation.LoopMode;
import com.jme3.app.SimpleApplication;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;

/** Пример 7 - как загрузить модель OgreXML и анимацию,
 * используя каналы, контроллер и AnimEventListener. */
public class HelloAnimation extends SimpleApplication
  implements AnimEventListener {
  private AnimChannel channel;
  private AnimControl control;
  Node player;
  public static void main(String[] args) {
    HelloAnimation app = new HelloAnimation();
    app.start();
  }
  
  @Override
  public void simpleInitApp() {
    viewPort.setBackgroundColor(ColorRGBA.LightGray);
    initKeys();
    DirectionalLight dl = new DirectionalLight();
    dl.setDirection(new Vector3f(-0.1f, -1f, -1).normalizeLocal());
    rootNode.addLight(dl);
    player = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
    player.setLocalScale(0.5f);
    rootNode.attachChild(player);
    control = player.getControl(AnimControl.class);
    control.addListener(this);
    channel = control.createChannel();
    channel.setAnim("stand");
  }
  
  public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
    if (animName.equals("Walk")) {
      channel.setAnim("stand", 0.50f);
      channel.setLoopMode(LoopMode.DontLoop);
      channel.setSpeed(1f);
    }
  }
  
  public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
    // не используем
  }
  
  /** Настроим сочетания клавиш: Привязку Ходьба(Walk) сопоставим с клавишей Пробел. */
  private void initKeys() {
    inputManager.addMapping("Walk", new KeyTrigger(KeyInput.KEY_SPACE));
    inputManager.addListener(actionListener, "Walk");
  }
  private ActionListener actionListener = new ActionListener() {
    public void onAction(String name, boolean keyPressed, float tpf) {
      if (name.equals("Walk") && !keyPressed) {
        if (!channel.getAnimationName().equals("Walk")) {
          channel.setAnim("Walk", 0.50f);
          channel.setLoopMode(LoopMode.Loop);
        }
      }
    }
  };
}

----


== Создание и Загрузка Анимированных Моделей

Вы можете создавать анимированные модели с помощью такого инструмента, как Blender. Не пожалейте времени и узнайте, как создать собственные модели здесь link:http://www.blender.org/education-help/tutorials/animation/[Blender Animation Tutorials]. В настоящее время, можете загружать и использовать бесплатные модели, как те что используется здесь в качестве примеров (link:http://jmonkeyengine.googlecode.com/svn/trunk/engine/test-data/Models/Oto/[Oto Golem], и link:http://jmonkeyengine.googlecode.com/svn/trunk/engine/test-data/Models/Ninja/[Ninja]).


Загружать анимированные модели довольно просто как вы уже узнали в предыдущих главах. Модели Анимированные Ogre представляют из себя набор файлов: Модель находится в  `Oto.mesh.xml`, детали анимации в `Oto.skeleton.xml`, плюс обычный файлов для материалов и текстур. Убедитесь что все файлы модели находятся вместе в одном и том же подкаталоге `Model`.


[source,java]

----

  public void simpleInitApp() {
    /* Отображение модели требует источника света */
    DirectionalLight dl = new DirectionalLight();
    dl.setDirection(new Vector3f(-0.1f, -1f, -1).normalizeLocal());
    rootNode.addLight(dl);
    /* Загрузить и прикрепить модель, как обычно */
    player = assetManager.loadModel("Models/Oto/Oto.mesh.xml");
    player.setLocalScale(0.5f); // изменим размер
    rootNode.attachChild(player);
    ...
    }

----

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



== Анимация Контроллер и Канал

После того, как вы загрузите анимированные модели, зарегистрируйте их в Animation Controller.


*  Объект контроллер дает вам доступ к имеющейся последовательности анимации.
*  Контроллер может иметь несколько каналов, каждый канал может работать в одной последовательности анимации за один раз.
*  Чтобы запустить несколько последовательностей, вы создаете несколько каналов, и устанавливаете каждый из них на отдельную анимации.

[source,java]

----

  private AnimChannel channel;
  private AnimControl control;

  public void simpleInitApp() {
    ...
    /* Загрузить элементы управления анимацией, ждать события анимации  ,,
     * создать канал анимации, и привести модель в позиции по умолчанию.  */
    control = player.getControl(AnimControl.class); 
    control.addListener(this);
    channel = control.createChannel();
    channel.setAnim("stand");
    ...

----

*Nehon на форум jME link:http://jmonkeyengine.org/groups/general-2/forum/topic/helloanimation-animations-seem-to-be-clashing/#post-180994[написал],*


“















[source]

----

control = player.getControl(AnimControl.class);

----

“


[source]

----

player.getChild("Subnode").getControl(AnimControl.class);

----





== Реагирование на События Анимации

Добавить `implements AnimEventListener` в объявление главного класса. Этот интерфейс предоставляет доступ к событиям, которые уведомляют вас, когда последовательность будет сделана, или при переходе от одной последовательности к другой, так что вы сможете сделать реакцию на это. В этом примере вы сбросите персонаж в положение стоя после цикла `Ходьбы(Walk)`.


[source,java]

----

public class HelloAnimation extends SimpleApplication
                         implements AnimEventListener {
  ...
  
  public void onAnimCycleDone(AnimControl control, 
                              AnimChannel channel, String animName) {
    if (animName.equals("Walk")) {
      channel.setAnim("stand", 0.50f);
      channel.setLoopMode(LoopMode.DontLoop);
      channel.setSpeed(1f);
    }
  }
  public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
    // не используем
  }

----


== Триггер Анимации После пользовательского Ввода

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


.  Инициализируем новый контроллер ввода (в `simpleInitApp()`).
**  Напишем метод `initKey()` и вызовем его из `simpleInitApp()`.

.  Добавим клавише привязку к именем действия, которое вы хотите вызвать.
**  Вот к примеру, ваше действие `Walk` привяжем к клавише Пробел.

.  Добавим ввод listener для действия `Walk`.

[source,java]

----

  private void initKeys() {
    inputManager.addMapping("Walk", new KeyTrigger(KeyInput.KEY_SPACE));
    inputManager.addListener(actionListener, "Walk");
  }

----

Для использования контроллера ввода, вы должны реализовать actionListener путем тестирования для каждого действия по имени, а затем для запуска анимации, установите канал с соответствующей анимацией.


*  Второй параметр setAnim() это blendTime (как долго текущая анимация должна совпадать с последней).
*  LoopMode может быть Loop (повтор), Cycle (вперед, затем назад), и DontLoop (только один раз).
*  При необходимости используйте channel.setSpeed() чтобы задать скорость анимации.
*  При необходимости используйте channel.setTime() для перемотки вперед или назад в какой-то момент во время этой анимации.

[source,java]

----

  private ActionListener actionListener = new ActionListener() {
    public void onAction(String name, boolean keyPressed, float tpf) {
        if (name.equals("Walk") && !keyPressed) {
            if (!channel.getAnimationName().equals("Walk")){
                channel.setAnim("Walk", 0.50f);
                channel.setLoopMode(LoopMode.Cycle);
            }
        }
    }
  };

----


== Упражнения


==== Упражнение 1: Две Анимации

Сделайте щелчок мыши вызовом другой последовательность анимации!


.  Создайте второй канал в контроллере
..  Создать новую клавишу вызова и привяжите её к действию (см.: <<документация/jme3_ru/начальная/знакомство_с_системой_ввода#,Знакомство с вводом>>)
..  Полезный совет: Если вы хотите узнать, какие последовательности анимации доступны в модели используйте: 
[source,java]

----
for (String anim : control.getAnimationNames()) { System.out.println(anim); }
----



==== Упражнение 2: Показать скелет (1)

Откройте файл `skeleton.xml` в текстовом редакторе по вашему выбору. Вам не нужно уметь читать или писать эти xml-файлы (Blender  сделает это за вас) - но это нужно, чтобы знать, как скелет работает. “Там нет никакой магии к нему!


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


==== Упражнение 3: Показать скелет (2)

Добавьте следующие операторы import для SkeletonDebugger и класс Material:


[source,java]

----

     import com.jme3.scene.debug.SkeletonDebugger;
     import com.jme3.material.Material;

----

Добавьте следующий фрагмент кода в `simpleInitApp()` чтобы сделать кости (о которых вы только что прочли) видными!


[source,java]

----

     SkeletonDebugger skeletonDebug = 
         new SkeletonDebugger("skeleton", control.getSkeleton());
     Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
     mat.setColor("Color", ColorRGBA.Green);
     mat.getAdditionalRenderState().setDepthTest(false);
     skeletonDebug.setMaterial(mat);
     player.attachChild(skeletonDebug);

----

Можете ли вы идентифицировать отдельные кости скелета?



== Вывод

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


Теперь, когда ваш персонаж может ходить, разве не было бы здорово, если бы он также мог собирать предметы, или направляйте оружие на объекты, или открыть дверь? Время раскрыть секреты <<документация/jme3_ru/начальная/знакомство_с_взаимодействиями#,Взаимодействий>>!

'''

Смотрите также: link:https://docs.google.com/leaf?id=0B9hhZie2D-fEYmRkMTYwN2YtMzQ0My00NTM4LThhOTYtZTk1MTRlYTNjYTc3&hl=en[Создание анимированной модели OgreXML в Blender]

<tags><tag target="beginner" /><tag target="intro" /><tag target="animation" /><tag target="documentation" /><tag target="keyinput" /><tag target="input" /><tag target="node" /><tag target="model" /></tags>
