Статьи

Библиотека JavaFX для обратной кинематики 2.0

Это первое руководство, объясняющее основы javafx-ik , библиотеки для обратной кинематики с JavaFX. Исходники библиотеки можно скачать с GitHub .

Что такое объект Bone ?

Одиночная кость

Рисунок 1: Одиночная кость

Кость является фундаментальным строительным блоком для обратной кинематики с библиотекой javafx-ik . Кость имеет длину и сустав, вокруг которого она может вращаться. Конец с суставом называется головкой кости, другой конец называется хвостом. На рисунке 1 показана одна кость, ее голова, хвост и длина.

В библиотеке javafx-ik кость напоминает класс Bone . Чтобы инициализировать это, длина должна быть передана как минимум, но обычно также угол устанавливается во время построения. Пример кода выше устанавливает кость длиной 50 и вращением 0, что означает, что она будет указывать горизонтально вправо.

1
2
// Adding bone b2 to the children of bone b1
b1.getChildren().add(b2);

Посмотреть код на Gist .

Сборка костей

Сборка двух костей

Рисунок 2: Сборка двух костей

Кости могут быть собраны, чтобы определить каркас анимированного объекта. Две кости связаны путем прикрепления хвоста одной кости b1 к головке другой кости b2 . Кость b1 называется родителем кости b2 , кость b2 называется потомком кости b1 . На рисунке 2 показаны две соединенные кости b1 и b2 .

Класс Bone предлагает два свойства для структуры. Свойство только для чтения parent хранит родительский элемент кости, а свойство children представляет собой ObservableList всех дочерних костей. Следующий фрагмент показывает, как можно соединить две кости b1 и b2 :

1
2
// Adding bone b2 to the children of bone b1
b1.getChildren().add(b2);

Посмотреть код на Gist .

Структура манекена

Рисунок 3: Структура манекена

Угол свойства «только для чтения» определяет поворот между костью и расширением ее родительской кости. Значение 0 приводит к прямой линии, значение 180 приводит к тому, что дочерняя кость перекрывает своего родителя, но указывает в противоположном направлении. Хотя наличие родителей и детей накладывает порядок на кости в скелете, обычно не имеет значения, какая кость является родителем, а какая — ребенком. Путем многократного связывания голов и хвостов объектов Bone можно создать цепочку. Эта простая структура используется в образце гусеницы. Сложные объекты определяются с помощью дерева костей. Точное положение кости зависит от положения родителя и поворота. Кость в верхней части дерева, у которой нет родителя, называется корневой костью. Корневая кость немного отличается во время инициализации. Угол свойства корневой кости определяет ее вращение в общей сцене. Вы можете думать об этом как о наличии родительской кости, которая направлена ​​горизонтально вправо. Теперь мы можем определить структуру нашего манекена, как показано на рисунке 3. Код для генерации каркаса манекена можно увидеть в примере кода ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Definition of head
final Bone head = new Bone(30, 90);
  
// Definition of torso
final Bone torso = new Bone(80, 0);
head.getChildren().add(torso);
  
final Bone[] upperArm = new Bone[2];
final Bone[] upperLeg = new Bone[2];
  
for (int i=0; i<2; i++) {
  
    // Definition upper arms
    upperArm[i] = new Bone(60, 60 - 90 * i);
  
    // Definition of lower arms
    final Bone lowerArm = new Bone(60, -90);
    upperArm[i].getChildren().add(lowerArm);
  
    // Definition of upper legs
    upperLeg[i] = new Bone(60, 30 - 90*i);
  
    // Definition of lower legs
    final Bone lowerLeg = new Bone(75, 90);
    upperLeg[i].getChildren().add(lowerLeg);
}
  
// Connect arms and legs to head and torso
head.getChildren().addAll(upperArm);
torso.getChildren().addAll(upperLeg);

Посмотреть код на Gist .

Присоединение визуальных компонентов

Сами кости не видны. Они только определяют структуру. Содержимое свойства, которое представляет собой ObservableList объектов Node , может использоваться для присоединения видимых элементов к кости.

1
2
3
4
5
6
7
// Attaching visual elements to a bone
final Bone bone = new Bone(50, 30);
bone.getContent().addAll(
new Circle(20),
new Ellipse(45, 0, 25, 15),
new Circle(80, 0, 10)
);

Посмотреть код на Gist .

Добавление визуальных элементов

Рисунок 4: Добавление визуальных элементов

Положение и вращение прикрепленных объектов Node определяется базовой костью. Источником локальной системы координат для всех прикрепленных узлов является положение головы кости. Если положение головы перемещается, то же самое происходит и с его узлами. Вращение кости также передается узлам. Если кость вообще не вращается и угол имеет значение 0, она направлена ​​горизонтально вправо. В приведенном выше примере кода кость определяется двумя кружками и эллипсом. Эта кость показана на рисунке 4. В приведенном ниже примере кода показаны необходимые изменения, которые определяют внешний вид нашего манекена путем добавления кругов и эллипсов. Получившуюся манекен можно увидеть на рисунке 5. На рисунке я добавил символы для костей, чтобы сделать их видимыми. Обратите внимание, что ни один из визуальных компонентов не вращается самостоятельно, и все положения являются локальными для кости. Окончательные положения и повороты в сцене рассчитываются только из положений и поворотов костей.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// Definition of head
final Bone head = new Bone(30, 90);
head.getContent().add(new Ellipse(20, 15));
 
// Definition of torso
final Bone torso = new Bone(80, 0);
torso.getContent().add(new Ellipse(40, 0, 50, 20));
head.getChildren().add(torso);
 
final Bone[] upperArm = new Bone[2];
final Bone[] upperLeg = new Bone[2];
 
for (int i=0; i<2; i++) {
 
    // Definition upper arms
    upperArm[i] = new Bone(60, 60 - 90 * i);
    upperArm[i].getContent().add(new Ellipse(22.5, 0, 30, 12.5));
 
    // Definition of lower arms
    final Bone lowerArm = new Bone(60, -90);
    lowerArm.getContent().addAll(
            new Circle(12.5),
            new Ellipse(30, 0, 20, 12.5),
            new Circle(60, 0, 12.5)
    );
    upperArm[i].getChildren().add(lowerArm);
 
    // Definition of upper legs
    upperLeg[i] = new Bone(60, 30 - 90*i);
    upperLeg[i].getContent().add(new Ellipse(20, 0, 30, 15));
 
    // Definition of lower legs
    final Bone lowerLeg = new Bone(75, 90);
    lowerLeg.getContent().addAll(
            new Circle(15),
            new Ellipse(40, 0, 30, 15),
            new Ellipse(75, -10, 10, 22.5)
    );
    upperLeg[i].getChildren().add(lowerLeg);
}
 
// Connect arms and legs to head and torso
head.getChildren().addAll(upperArm);
torso.getChildren().addAll(upperLeg);

Посмотреть код на Gist .

Пустышка со всеми визуальными элементами

Рисунок 5: Пустышка со всеми прикрепленными визуальными элементами

Добавление всего на сценограф

Теперь отсутствует только одна последняя часть, чтобы можно было отобразить манекен на экране: класс Skeleton . Класс Skeleton — это мост между Scenegraph и костями с прикрепленными объектами Node . Он расширяет Parent и поэтому может быть добавлен в любое место в Scenegraph. Это может быть переведено, повернуто, масштабировано и преобразовано как любой другой узел в Scenegraph.

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

Чтобы увидеть результат, загрузите полный файл Dummy.java . Чтобы скомпилировать скрипт, вам нужно добавить библиотеку javafx-ik в ваш путь к классам. Вы можете скачать исходники с GitHub .

Что дальше?

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

Ссылка: библиотека JavaFX для обратной кинематики 2.0 от нашего партнера по JCG Майкла Хайнрихса в блоге Майка .